如何有效地从字节中读取位?

时间:2011-10-22 13:19:41

标签: javascript parsing node.js byte bit-manipulation

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

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

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

目前,我正在解析字节中的项目,如下所示:

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的倍数。要解析3位的项目,我必须进行位移,进行AND操作,因为8不能被3整除有时甚至必须组合两个字节的部分,如itemC

将这些数据作为3位组而不是8位组进行读取会更有效。

我想到的是使用.toString(2)将所有字​​节转换为字符串,然后使用.substring获取长度为3的子字符串,然后使用{{转换回数字1}},但我想这不是这样做的方法,因为字符串操作很慢而且我实际上没有做任何与字符串相关的事情。

是否可以读取例如组中的位。 3而不是从字节解析它们?或者是否有更有效的方法从字节中读取位?

4 个答案:

答案 0 :(得分:6)

二进制AND和位移操作是执行此操作的最快方法。它们很好地转换为机器代码指令。进一步提高速度的唯一方法是牺牲速度带宽,例如每个字节不要超过3位,但从您的问题判断,您可能已经考虑过并拒绝了这种权衡。

答案 1 :(得分:4)

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

答案 2 :(得分:0)

如果注意了endian-ness,则可以将其作为int或long int数组访问。 还有一种可能是不使用第3位和第7位

答案 3 :(得分:0)

我们可以通过获得适当的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;
}

以下示例从缓冲区读取8个5位值,长度为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