How to efficiently read bits out of bytes?

前端 未结 4 1912
野的像风
野的像风 2021-01-02 02:24

I\'m working on a project that includes WebSockets, and data between the server (Node.js) and the client (Chrome) is sent using a custom (very simple) format for data exchan

相关标签:
4条回答
  • 2021-01-02 03:00

    We can get value we need by getting appropriate 16-bits integer and then bitshift it.

    It is clear, that to get i-th value we should get 16-bits integer with offset in bytes that fits (bits * (i + 1) - 16)/8 <= offset <= (bits * i)/8.

    Lets take M=bits*i/8, so we have M + bits/8 - 2<= offset <= M. Then we get minimal offset as ceil(M + bits/8 - 2) and calculate position of i-th value in the 16-bit integer by offsets. I have just wrote the following function

    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;
    }
    

    And the following example to read 8 5-bit values off the Buffer with 5 bytes length.

    // 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]);
    }
    

    When I launch node test.js I got

    got buffer with length=8
    0-31
    1-31
    2-31
    3-31
    4-31
    5-31
    6-31
    7-26
    
    0 讨论(0)
  • 2021-01-02 03:03

    The binary AND and bit shifting operations are the fastest way of doing this. They translate well into machine code instructions. The only way to further speed this up is by sacrificing bandwidth for speed by for example simply not using more than 3 bits per byte, but judging from your question you've probably already considered and rejected that tradeoff.

    0 讨论(0)
  • 2021-01-02 03:07
    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
    
    0 讨论(0)
  • 2021-01-02 03:08

    if endian-ness is taken care, you can access it as an int or a long int array. There is another possibily of not using bit 3 and bit 7

    0 讨论(0)
提交回复
热议问题