0

I am aware that a similar question has already been asked before (see How to read a single bit buffer in node), but it seems none of the answers are helful. The first comment that has been accepted, stating to use buf.readUIntLE(), is not working since 2 arguments are expected, but 0 was provided.

Thus, I am currently trying to read a single bit (the 17th one) from a Buffer, but I can't find a way to easily read it. I have been using buffer.readUInt16BE(0) to read the 2 first bytes of the Buffer and so far it works fine, but I need to assign the 17th bit to a variable.

Note: My Buffer data is Big-Endian and I am coding in TypeScript.

I thought about doing something like:

const myVar: number = parseInt(buffer.readUIntBE(2, 1).toString().slice(0,1));

in order to read, then stringify the 3th byte, then getting the first caracter and convert it into a number, but I find it very clunky.

Is there a simple way to achieve this ?

UPDATE

I finally decided to create myself a small utility function to read a specific bit in a Buffer/UInt8Array that seems to do the job pretty well. Note: Byte are always read from left to right and bits are read from left to right. That comes handy when using the function.

/**    
 * @name readBit()
 * @brief Allows to read the value of a bit in a buffer by specifying from which byte to read the specified bit. 
 * @param buffer The Buffer/UInt8Array to read the bit from.
 * @param index The zero-based cardinal index of the byte to read the bit from.
 * @param bit The zero-based cardinal index of the bit to read from the byte.
 * @return The bit value.
 */
export function readBit(buffer: Buffer, index: number, bit: number): number {
    return (buffer[index] >> bit) & 1;
}

export default readBit;

If you want the function to be more intuitive, and to specify the zero-based index from left to right of bits, the function must be modified to this instead:

/**    
 * @name readBit()
 * @brief Allows to read the value of a bit in a buffer by specifying from which byte to read the specified bit. 
 * @param buffer The Buffer/UInt8Array to read the bit from.
 * @param byteIndex The zero-based cardinal index of the byte to read the bit from. Bytes reading is from right to left.
 * @param bitIndex The zero-based cardinal index of the bit to read from the byte. Bits reading is from left to right.
 * @return The bit value.
 */
export function readBit(buffer: Buffer, byteIndex: number, bitIndex: number): number {
    return (buffer[byteIndex] >> 7 - bitIndex) & 1;
}

export default readBit;

Thanks a lot to @Bergi for his help with this.

matreurai
  • 147
  • 12
  • Thanks, an honest mistake. I've updated my question. – matreurai Jul 11 '22 at 22:04
  • "*My Buffer data is Big-Endian*" - how exactly are you counting your bits then? Is the 17th bit not just the 1st bit (least significant bit) of the 3rd byte? – Bergi Jul 11 '22 at 22:19
  • Yes exactly ! The 17th bit is the first bit of the 3rd byte. Exemple: 1010 1100 1010 1100 X100 ... where X is the bit i'm trying to read. – matreurai Jul 11 '22 at 22:42
  • 1
    Then my answer will work for you. I was afraid you'd have groups of 2 or 4 bytes to represent an integer (and then accessing 1st-16th or 1st-32nd bit in the respective number), at which point endianness would have mattered. It doesn't matter if you talk about individual bytes. – Bergi Jul 11 '22 at 22:49
  • I'm a bit rusty with the bit shifting thing tho haha, so I'll have to go back reading about it :P But thanks a lot mate ! It's really appreciated. – matreurai Jul 11 '22 at 22:56
  • 1
    Well if you want to work with single bits, you won't be able to avoid that – Bergi Jul 11 '22 at 22:58

1 Answers1

1

A buffer is also a Uint8Array typed array, you can simply access its bytes by index. (If you prefer to do it with a method call, the equivalent would be readUint8). Then simply access the respective bit in that byte:

const position = 17;
const bitOffset = position & 7; // in byte
const byteIndex = position >> 3; // in buffer
const bit = (buffer[byteIndex] >> bitOffset) & 1;

(Regarding the numbers: there are 8 = bits in a byte, and 7 is (1<<3)-1 or 0b111)

If you subscribe to MSB 0 bit numbering, as is used in most network protocols, you will however have to do

const bit = (buffer[byteIndex] >> (7 - bitOffset)) & 1;
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    I've been trying to understand your snippet, but there few things I don't get. First line is a no brainner. On the second line, you assign the value of the Bitwise AND of 17 and 7. Correct me if I'm wrong but I believe 0001 0001 & 0111 = 0000 0001 right ? So you are assigning the value 1 to bitIndex ? Then on the third line you are doing an arithmetic right shift between 17 and 3. That equal 0010, so 2 ? Then finally, on the last line you assign to "bit" the value of the 2nd index arithmetic right shift 1, Bitwise AND 1 ? This part I don't get at all... – matreurai Jul 12 '22 at 17:36
  • 1
    How did you come up with this logic ? I mean, I pretty much understand the syntax, but I don't get the logic out of it I think. Why did you choose the number 7 on the second line ? Would the number 1 have done the same thing such as : position & 1 – matreurai Jul 12 '22 at 17:42
  • Yes, yes, and yes, but in the end I think there's a off-by-one mistake between ordinal numbers in writing and zero-based cardinal indexes. `buffer[2]` gets the third byte, `(byte >> 1) & 1` gets the second bit in that byte. The `position` variable is zero-based, so `17` actually gets us the 18th bit. – Bergi Jul 12 '22 at 17:43
  • The bitmask `7` (`0b111`) used as an AND bitwise operand does the same as `position % 8` or `position % (1 << 3)`, whereas `position >> 3` is the same as `Math.floor(position / 8)`. Basically, we divide the bits into groups of 8, and get the index of the group (byte) and the index within that group. – Bergi Jul 12 '22 at 17:45
  • I understand what position & 7 does, but I'm wondering why are you doing this ? I mean, in the sens that I am trying to find the 17th bit, sure, but does the AND bitwise operand is normally used to find position of a bit in a byte ? How exactly did you come up with this since doing 17 & 1 equal the same as 17 & 7... Why 7 ? – matreurai Jul 12 '22 at 17:55
  • Then does simply doing buffer[2][0] works ? 3th Byte, First bit ? – matreurai Jul 12 '22 at 18:02
  • 1
    Try with 22 or 23 instead of 17 if you think `& 1` would work as well as `& 7`. No, we need to do modulo 8. As for `buffer[2][0]`: you cannot access indices on a number. You need to do `(buffer[2] >> 0) & 1`. – Bergi Jul 12 '22 at 19:34
  • Is there a universal way to access any bit value in a Buffer ? I can't wrap my head around this. I understand some LSB are missing sometimes so I can't directly access them, this makes it very difficult to access bits in some context. I'm trying to write a function that allows me to access any Buffer bit value. Your code proposition seems not to work with any bit position. As an example, my Buffer 3th byte is 0x01 so I should be able to read zeros at position 16,17,18,19,20,21 and 22 if indexes are zero-based and I should be able to read 1 at position 23. Right ? – matreurai Jul 12 '22 at 20:51
  • If your 3rd byte has value `0x01`, then it's the 17th bit in the buffer (position `16`) that is set, and bits `17`-`23` that are clear. Unless you use the rather unusual [MSB 0 bit order](https://en.wikipedia.org/wiki/Bit_numbering#MSB_0_bit_numbering). – Bergi Jul 12 '22 at 21:19
  • I'm trying to parse a DNS packet. The first 12 bytes are the Header section. Within the Header section, the first 2 bytes are a random ID, and the third byte is a mix of different flags. The first bit of this byte is indicating if the packet is a query or a response. As an example, lets say I receive the packet . Then I received 0111 1100 1101 0000 0000 0001 0000 0000 0000 0000... right ? So the 3th byte is : 0x01 So 0000 0001. Its first bit is then 0, indicating the packet is a query. Right ? I want to be able to assign the query/response value to a var – matreurai Jul 12 '22 at 21:30
  • 1
    Yes. And the "first bit" of `0b0000_0001` is conventionally the `1`, the LSB. However, according to the answer to [this question](https://stackoverflow.com/q/39969580/1048572), in RFC packet diagrams, the MSB is leftmost and labelled `0`, as do [RFC 1035 here](https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.2) and [there](https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1). – Bergi Jul 12 '22 at 22:09