/** *@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的場景下。
類型肯定,能夠避免數字轉二進制字符串帶來的性能開銷。