/** *@global {Uint8Array} arr - 即字节流如[0xff,0xaa,0x10...] *@global {Number} pos - 二进制流索引,好比0的时候为这个buffer的二进制第0位 * *@param {Number} size - 位数,范围可为1~31 *@return {Number} */
function read(size) {
var i, code = 0;
for (i = 0; i < size; i++) {
if (arr[pos >> 3] & 1 << (pos & 7)) {
code |= 1 << i;
}
pos++;
}
return code;
}
复制代码
来源 github.com/intellilab/…javascript
这个函数是用来读取那些二进制流中,读取规则并非8bit 16bit 64bit的数据格式。java
典型场景为读取gif数据流作解码时,gif支持自定义位数编码,好比对于黑白二值化的gif图,能够经过用2个bit编码像素,相比8个bit编码像素能够大大减小文件体积。git
pos>>3
能够先看看pos>>3
的几个结果是什么:github
0>>3 //0
7>>3 //0
8>>3 //1
15>>3 //1
16>>3 //2
255>>3 //31
256>>3 //32
复制代码
分析计算过程,对于任意二进制,能够表示为十进制计算为:函数
当往右移动三位,即丢弃末尾三位,而后总体除以性能
即 编码
又 spa
因此右移以后的结果至关于把一个数除以8以后向下取整,即Math.floor(n/8)
;code
而一个字节恰好是8位二进制。 因此,pos>>3
表明的是,在二进制流中,当前位数对应到字节码里面是第几个字节。索引
pos & 7
一样,先看看pos & 7
的结果:
0 & 7 // 0
1 & 7 // 1
6 & 7 // 6
7 & 7 // 7
8 & 7 // 0
...
63 & 7 //7
64 & 7 //0
65 & 7 //1
复制代码
能够发现,计算的结果至关于对一个数求8的余数,至关于n%8
。
分析计算过程,能够把等式化为二进制:
// 0 & 7 = 0
0b00000000 & 0b00000111 //-> 0b00000000
// 1 & 7 = 1
0b00000001 & 0b00000111 //-> 0b00000001
// 63 & 7 = 7
0b00111111 & 0b00000111 //-> 0b00000111
// 64 & 7 = 0
0b01000000 & 0b00000111 //-> 0b00000000
// 65 & 7 = 1
0b01000001 & 0b00000111 //-> 0b00000001
复制代码
任意数字与7(n & 0b111
),都是提取该数二进制模式的末三位;
所以不管所给的数字多大,末三位必定是按顺序在0b000
到0b111
之间,也就是0到7;
所以结果跟求8的余数结果相同。
因此,pos & 7
表示的是,当前位数对应到一个字节内第几位
arr[pos >> 3] & 1 << (pos & 7)
由上可知:
arr[pos >> 3]
取到的是当前的字节,
pos & 7
则是表明当前是字节内的第几位。
由于1
的二进制值为0b00000001
,
根据位运算"与"的特性,任意数字与1(n & 0b00000001
),能够提取该数的末位的值;
发生左移时,
左移1位1<<1
为0b00000010
, 则能够提取倒数1位的值
左移2位1<<2
为0b00000100
, 则能够提取倒数2位的值
以此类推,
arr[pos >> 3] & 1 << (pos & 7)
则能够得到索引pos
对应的二进制值0或1
code |= 1 << i
当索引pos取到的值为1的时候,把值拼到code里面。
for循环以后,就能够拿到具体值。
由于js位运算中左右移运算最大只支持31位,所以参数size的最大只能为31。并且跨字节读取数据还须要考虑大端序小端序的问题。所以最好是使用在小于8bit的场景下。
类型肯定,能够避免数字转二进制字符串带来的性能开销。