如何有效地读取字节的位?
我正在开发一个包含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