如何在JavaScript中以最佳方式从位数组读取位子集

如何在JavaScript中以最佳方式从位数组读取位子集,javascript,integer,bit-manipulation,Javascript,Integer,Bit Manipulation,假设我有一个这样的位数组 101110101010101010101111111011001100100100001011001111101000101001 我想做的两个操作是: 将连续的比特子集读入单个比特数组(对于JavaScript,则读入整数) 在JavaScript中将非连续的比特子集读入单个整数 对于(1),假设我想要这个: 1011[101010101010101011111]11011001100100100001011001111101000101001 == 101010

假设我有一个这样的位数组

101110101010101010101111111011001100100100001011001111101000101001
我想做的两个操作是:

  • 将连续的比特子集读入单个比特数组(对于JavaScript,则读入整数)
  • 在JavaScript中将非连续的比特子集读入单个整数
  • 对于(1),假设我想要这个:

    1011[101010101010101011111]11011001100100100001011001111101000101001
    == 101010101010101011111
    = 1398111 in decimal
    
    第4-25位左右

    对于(2),我希望以最佳方式选择一个不连续的比特子集,并将它们以最佳方式组合成最终值

    1011[101]0101010101010[11]1111[1]011001100100100001011001111101000101001
    == 101 ++ 11 ++ 1
    = 101111
    = 47 in decimal
    
    位4-6、21-22和27左右


    这样做的正确/最佳方式是什么?

    格式仍然有点模糊,但这里有一种方法可以做到这一点。我正在做一些使问题更容易解决的假设,即:

    • 一次最多提取32位(因此它适合一个
      数字
      ,没有奇怪的破解)
    • 位位于
      uint32阵列中(或兼容存储器,只要每个元素有32位)
    • 数组第0项的最低有效位为数字0
    • 以这种方式表示的位字符串是
      …+tobits(数组[1])+tobits(数组[0])
      例如
      [0256]
      表示
      00000000000000000000000000000000000000\u00000000000000000000000000000000000000000000
      (下划线表示片段之间的边界)。也许这是错误的方法,它可以改变,但这种方法很简单
    第i位位于该字内偏移量
    i&31
    (aka
    i%32
    )处的
    i>-5
    -th(aka
    i/32
    )字中。这就是为什么这个订单很容易处理的原因

    根据第一个假设,数组中最多有2个条目/字由范围跨越,因此只有两种情况:

    • 范围的底部在一个单词中,顶部在下一个单词中

    • 这个范围完全包含在一个单词中。应避免触摸第二个单词,因为它可能超出数组的末尾。此外,即使第二个字可以被触摸,处理多余的位也不会那么容易,因为移位计数采用模32,因此
      high是一个简化版本,在最后一步只使用生成器,从而避免将整个输入加载到内存中

      // for single number
      function* gUintToBits(input, dim) {
        let i = 0;
        while (i < dim) {
          yield (input >> (dim - 1 - i++)) & 1;
          // or like this, if bits should bo from left to right:
          // yield (input >> i++) & 1;
        }
      }
      
      // for array of numbers
      function* gUintArrayToBits(input, dim) {
        for (let item of input) {
          yield* gUintToBits(item, dim);
        }
      }
      
      // apply intervals mask directly to generator
      function* genWithIntervalsApplied(iterOfBits, intervals) {
        // fast, if number of intervals is not so big
        const isInsideIntervals = (n, itrvs) => {
          for (let intv of itrvs) {
            if (n >= intv[0] && n < intv[1]) return true;
          }
          return false;
        };
      
        let index = 0;
        for (let item of iterOfBits) {
          if (isInsideIntervals(index++, intervals)) {
            yield item;
          }
        }
      }
      
      // Finally consume the generator
      function extractIntervalsFromUint8Array(input, intervals) {
        let result = ''
        for (let x of genWithIntervalsApplied(gUintArrayToBits(input, 8), intervals)) {
          result += `${x}`
        }
        return result
      }
      
      const result = extractIntervalsFromUint8Array(
        [1, 3, 9, 127],
        [[8, 16], [24, 32]],
      );
      const dec = parseInt(result, 2);
      
      console.log(result);
      console.log(dec);
      

      请看一下API,它是否适用于位而不是字节?位存储的格式是什么?打包在UINT8阵列中?UINT32阵列?带32位“整数”的普通JS数组?BigInt?问得好。位将存储在UINT8阵列或32阵列中,可能是UINT32阵列。但这只是普通的bitarray/binaryarray之上的一个“视图”(尽管我不知道如何构造它,除了在http请求中返回binaryarray)?例如,哪些位存储在UINT32数组的第0个条目中(第0到第31位?或最高有效位?),哪些“方式”是位(第0个条目的最低有效位也是字符串的第0位)?索引是否应该像您的示例中那样工作(看起来是相反的)?即使间隔相互重叠或以随机顺序排列,此解决方案也应该工作。注意:此处的间隔确实作为整个掩码生效,请检查位的位置是否在任何间隔内。如果这不是期望的行为,则应对生成器函数应用掩码进行相应更改。
      [0xdeadbeef, 0xcafebabe] means that the string is really 0xcafebabedeadbeef (in bits)
      extractRange([0xdeadbeef, 0xcafebabe], 0, 31).toString(16) =
          deadbeef
      extractRange([0xdeadbeef, 0xcafebabe], 4, 35).toString(16) =
          edeadbee
      extractRange([0xdeadbeef, 0xcafebabe], 8, 39).toString(16) =
          bedeadbe
      extractRange([0xdeadbeef, 0xcafebabe], 60, 63).toString(16) =
          c
      extractRange([0xdeadbeef, 0xcafebabe], 30, 33).toString(16) =
          b // ...ed... in binary 11101101, taking the middle 4 bits, 1011 = b
      
      // for single number
      function* gUintToBits(input, dim) {
        let i = 0;
        while (i < dim) {
          yield (input >> (dim - 1 - i++)) & 1;
          // or like this, if bits should bo from left to right:
          // yield (input >> i++) & 1;
        }
      }
      
      // for array of numbers
      function* gUintArrayToBits(input, dim) {
        for (let item of input) {
          yield* gUintToBits(item, dim);
        }
      }
      
      // apply intervals mask directly to generator
      function* genWithIntervalsApplied(iterOfBits, intervals) {
        // fast, if number of intervals is not so big
        const isInsideIntervals = (n, itrvs) => {
          for (let intv of itrvs) {
            if (n >= intv[0] && n < intv[1]) return true;
          }
          return false;
        };
      
        let index = 0;
        for (let item of iterOfBits) {
          if (isInsideIntervals(index++, intervals)) {
            yield item;
          }
        }
      }
      
      // Finally consume the generator
      function extractIntervalsFromUint8Array(input, intervals) {
        let result = ''
        for (let x of genWithIntervalsApplied(gUintArrayToBits(input, 8), intervals)) {
          result += `${x}`
        }
        return result
      }
      
      const result = extractIntervalsFromUint8Array(
        [1, 3, 9, 127],
        [[8, 16], [24, 32]],
      );
      const dec = parseInt(result, 2);
      
      console.log(result);
      console.log(dec);
      
      // 0000001101111111
      // 895