refactor(protocol): THP getControlBit returns both ack and sequence bits

This commit is contained in:
Szymon Lesisz
2025-11-19 17:00:01 +01:00
committed by Szymon Lesisz
parent 3d948f28ff
commit 10c54bd35d
3 changed files with 27 additions and 14 deletions

View File

@@ -207,11 +207,11 @@ export const encodeAck = (bytesOrState: Buffer | ThpState) => {
// 1 byte
const magic = bytesOrState.readUInt8();
// sequence bit
const recvBit = getControlBit(magic);
const { ackBit } = getControlBit(magic);
// 2 bytes channel id
const channel = bytesOrState.subarray(1, 3);
return encodeReadAck(channel, recvBit);
return encodeReadAck(channel, ackBit);
}
const { channel, recvBit } = bytesOrState;

View File

@@ -31,11 +31,11 @@ export const addSequenceBit = (magic: number, seqBit: number) => {
// clear 4th (ack) and 5th (sequence) bit
export const clearControlBit = (magic: number) => magic & ~(1 << 3) & ~(1 << 4);
export const getControlBit = (magic: number): ThpMessageSyncBit => {
const ackBit = (magic & (1 << 3)) === 0 ? 0 : 1;
const sequenceBit = (magic & (1 << 4)) === 0 ? 0 : 1;
export const getControlBit = (magic: number) => {
const ackBit: ThpMessageSyncBit = (magic & (1 << 3)) === 0 ? 0 : 1;
const sequenceBit: ThpMessageSyncBit = (magic & (1 << 4)) === 0 ? 0 : 1;
return ackBit || sequenceBit;
return { ackBit, sequenceBit };
};
// transform protocol-v2 message header to ThpHeader object
@@ -43,13 +43,14 @@ export const readThpHeader = (bytes: Buffer) => {
// 1 byte
const magic = bytes.readUInt8();
// sequence bit
const controlBit = getControlBit(magic);
const { ackBit, sequenceBit } = getControlBit(magic);
// 2 bytes channel id
const channel = bytes.subarray(1, 3);
return {
magic,
controlBit,
ackBit,
sequenceBit,
channel,
};
};
@@ -101,7 +102,7 @@ export const getExpectedHeaders = (state: ThpState): Buffer[] =>
case THP_CONTINUATION_PACKET:
return Buffer.from([resp]); // THP_CONTINUATION_PACKET is not masked with sequence bit
case THP_READ_ACK_HEADER_BYTE:
return addAckBit(resp, state.sendBit);
return addAckBit(resp, state.sendAckBit);
default:
return addSequenceBit(resp, state.recvBit);
}
@@ -130,7 +131,10 @@ export const isExpectedResponse = (bytes: Buffer, state: ThpState) => {
for (let i = 0; i < expectedResponses.length; i++) {
if (magic === expectedResponses[i]) {
// continuation packet is not masked by controlBit
if (magic !== THP_CONTINUATION_PACKET && header.controlBit !== state?.recvBit) {
if (
magic !== THP_CONTINUATION_PACKET &&
(header.sequenceBit !== state?.recvBit || header.ackBit !== state?.recvAckBit)
) {
console.warn('Unexpected control bit');
return false;

View File

@@ -9,22 +9,31 @@ describe('controlBit', () => {
it('ackBit', () => {
expect(addAckBit(0x20, 0).readUint8()).toEqual(0x20);
expect(addAckBit(0x20, 1).readUint8()).toEqual(0x28);
expect(addAckBit(0x04, 1).readUint8()).toEqual(0x0c);
expect(addAckBit(0x14, 1).readUint8()).toEqual(0x1c); // with sequenceBit
expect(getControlBit(0x20)).toEqual(0);
expect(getControlBit(0x28)).toEqual(1);
expect(getControlBit(0x20).ackBit).toEqual(0);
expect(getControlBit(0x28).ackBit).toEqual(1);
expect(getControlBit(0x0c).ackBit).toEqual(1);
expect(getControlBit(0x1c).ackBit).toEqual(1); // with sequenceBit
expect(clearControlBit(0x20)).toEqual(0x20);
expect(clearControlBit(0x28)).toEqual(0x20);
expect(clearControlBit(0x1c)).toEqual(0x04); // with sequenceBit
});
it('sequenceBit', () => {
expect(addSequenceBit(0x03, 0).readUint8()).toEqual(0x03);
expect(addSequenceBit(0x03, 1).readUint8()).toEqual(0x13);
expect(addSequenceBit(0x0c, 1).readUint8()).toEqual(0x1c); // with ackBit
expect(getControlBit(0x03)).toEqual(0);
expect(getControlBit(0x13)).toEqual(1);
expect(getControlBit(0x03).sequenceBit).toEqual(0);
expect(getControlBit(0x13).sequenceBit).toEqual(1);
expect(getControlBit(0x0c).sequenceBit).toEqual(0); // with ackBit
expect(getControlBit(0x1c).sequenceBit).toEqual(1); // with ackBit and sequenceBit
expect(clearControlBit(0x03)).toEqual(0x03);
expect(clearControlBit(0x13)).toEqual(0x03);
expect(clearControlBit(0x1c)).toEqual(0x04); // with ackBit
});
});