如何有效地读取字节的位?

我正在开发一个包含WebSockets的项目,服务器(Node.js)和客户端(Chrome)之间的数据使用自定义(非常简单)的格式发送,用于我设置的数据交换。

我正在发送3位数据,因为我发送了所有有8种可能性的项目。 数据格式如下所示:

0 1 bit index 01234567 8901... item aaabbbcc cddd... 

目前,我正在parsing像这样的字节的项目:

 var itemA = bytes[0] >> 5; var itemB = (bytes[0] >> 2) & 7; var itemC = (bytes[0] & 3) << 1 | bytes[1] >> 7; var itemD = (bytes[1] >> 4) & 7; 

就个人而言,这感觉太复杂了。 问题是,它只是复杂的,因为我得到的字节数是8的倍数。要parsing出3位的项目,我必须进行位移,执行AND操作,因为8不能被3整除有时甚至必须像itemC那样结合两个字节的itemC

以3位组而不是8位组读取这些数据会更加有效。

我想到的是使用.toString(2)将所有字节转换为一个string,然后使用.substring得到一个长度为3的子string,并转换回一个数字parseInt(bitString, 2) ,但我想这不是做这件事的方式,因为string操作很慢,我实际上没有做任何string相关的。

是否有可能读取例如3组中的位而不是从字节parsing它们? 还是有更高效的方法来读取字节的位?

二进制AND和位移操作是最快的方法。 它们很好地转换成机器码指令。 进一步加速的唯一方法是通过牺牲带宽来提高速度,例如,每字节不要超过3比特,但从你的问题来看,你可能已经考虑过了,并且拒绝了这种折衷。

 function byte2bits(a) { var tmp = ""; for(var i = 128; i >= 1; i /= 2) tmp += a&i?'1':'0'; return tmp; } function split2Bits(a, n) { var buff = ""; var b = []; for(var i = 0; i < a.length; i++) { buff += byte2bits(a[i]); while(buff.length >= n) { b.push(buff.substr(0, n)); buff = buff.substr(n); } } return [b, buff]; } var a, b, r; a = [227, 142]; [b, r] = split2Bits(a, 3); //b = ["111", "000", "111", "000", "111"]; //r = '0'; //rest of bits 

如果小心谨慎,可以将它作为int或long int数组来访问。 还有另一个可能不使用位3和位7

我们可以通过获得适当的16位整数来获得我们需要的值,然后进行位移。

很明显,为了得到第i-th值,我们应该得到16位整数,其偏移量以字节为单位(bits * (i + 1) - 16)/8 <= offset <= (bits * i)/8

让我们取M=bits*i/8 ,所以我们有M + bits/8 - 2<= offset <= M 然后我们得到最小的偏移量作为ceil(M + bits/8 - 2)并用偏移量计算出16位整数中的第i个值的位置。 我刚才写了下面的函数

 function getDataFromStream(buffer, bitsPerValue, endianness) { var valuesCount = Math.floor(buffer.length * 8 / bitsPerValue); var ret = new Buffer(valuesCount); if (valuesCount > 0) { for (var i = 0; i < valuesCount; i++) { var offsetMin = Math.ceil(bitsPerValue * i / 8. + bitsPerValue / 8. - 2); if (offsetMin < 0) { offsetMin = 0; } if(endianness == 'BE') var wordWithValue = buffer.readUInt16BE(offsetMin, true); else var wordWithValue = buffer.readUInt16LE(offsetMin, true); var offsetInWord = bitsPerValue * i - offsetMin * 8; var leftInWord = 16 - bitsPerValue - offsetInWord; // then get value in the word by shifting and then remove other bits by "%" ret[i] = (wordWithValue >> (endianness == 'BE' ? leftInWord : offsetInWord )) % Math.pow(2, bitsPerValue); } } return ret; } 

下面的例子读取5个字节长度的缓冲区中的8个5位值。

 // buffer with 5 bytes var xx = new Buffer(5); xx[0] = 255; xx[1] = 255; xx[2] = 255; xx[3] = 255; xx[4] = 250; // get data, 5bits per value. var yy = getDataFromStream(xx, 5, 'BE'); console.log('got buffer with length='+ yy.length); for(i = 0; i < yy.length; i++){ console.log(i+'-'+yy[i]); } 

当我启动节点test.js我得到了

 got buffer with length=8 0-31 1-31 2-31 3-31 4-31 5-31 6-31 7-26