diff --git a/libs/message-coder/src/uint16_coder.test.ts b/libs/message-coder/src/uint16_coder.test.ts index 7156d7743..145539482 100644 --- a/libs/message-coder/src/uint16_coder.test.ts +++ b/libs/message-coder/src/uint16_coder.test.ts @@ -29,6 +29,29 @@ test('uint16', () => { ); }); +test('uint16 with littleEndian=false', () => { + fc.assert( + fc.property( + fc.integer(0, 65535), + fc.integer({ min: 0, max: 100 }), + (value, byteOffset) => { + const bitOffset = byteOffset * 8; + const buffer = Buffer.alloc(2 + byteOffset); + const field = uint16(undefined, { littleEndian: false }); + type field = CoderType; + + expect(field.encodeInto(value, buffer, bitOffset)).toEqual( + ok(bitOffset + 16) + ); + expect(buffer.readUInt16BE(byteOffset)).toEqual(value); + expect(field.decodeFrom(buffer, bitOffset)).toEqual( + typedAs>(ok({ value, bitOffset: bitOffset + 16 })) + ); + } + ) + ); +}); + test('uint16 with enumeration', () => { enum Enum { A = 1, diff --git a/libs/message-coder/src/uint16_coder.ts b/libs/message-coder/src/uint16_coder.ts index 53a670d1f..e95d5433e 100644 --- a/libs/message-coder/src/uint16_coder.ts +++ b/libs/message-coder/src/uint16_coder.ts @@ -11,11 +11,25 @@ import { } from './types'; import { UintCoder } from './uint_coder'; +interface Uint16CoderOptions { + littleEndian: boolean; +} + /** * Coder for a uint16, aka a 16-bit unsigned integer. Uses little-endian byte * order. */ export class Uint16Coder extends UintCoder { + private readonly littleEndian: boolean; + + constructor( + enumeration?: unknown, + { littleEndian = true }: Partial = {} + ) { + super(enumeration); + this.littleEndian = littleEndian; + } + bitLength(): BitLength { return 16; } @@ -32,14 +46,20 @@ export class Uint16Coder extends UintCoder { this.validateValue(value).okOrElse(fail); return this.encodeUsing(buffer, bitOffset, (byteOffset) => - buffer.writeUInt16LE(value, byteOffset) + this.littleEndian + ? buffer.writeUInt16LE(value, byteOffset) + : buffer.writeUInt16BE(value, byteOffset) ); }); } decodeFrom(buffer: Buffer, bitOffset: BitOffset): DecodeResult { return this.decodeUsing(buffer, bitOffset, (byteOffset) => - this.validateValue(buffer.readUInt16LE(byteOffset)) + this.validateValue( + this.littleEndian + ? buffer.readUInt16LE(byteOffset) + : buffer.readUInt16BE(byteOffset) + ) ); } } @@ -49,7 +69,8 @@ export class Uint16Coder extends UintCoder { */ // eslint-disable-next-line vx/gts-no-return-type-only-generics -- TS does not have a way of saying "I want an enum of numbers" export function uint16( - enumeration?: unknown + enumeration?: unknown, + options?: Uint16CoderOptions ): Coder { - return new Uint16Coder(enumeration) as unknown as Coder; + return new Uint16Coder(enumeration, options) as unknown as Coder; } diff --git a/libs/message-coder/src/uint32_coder.test.ts b/libs/message-coder/src/uint32_coder.test.ts index 2a2cec3e1..2ef470d74 100644 --- a/libs/message-coder/src/uint32_coder.test.ts +++ b/libs/message-coder/src/uint32_coder.test.ts @@ -29,6 +29,29 @@ test('uint32', () => { ); }); +test('uint32 with little-endian=false', () => { + fc.assert( + fc.property( + fc.integer(0, 0xffffffff), + fc.integer({ min: 0, max: 100 }), + (value, byteOffset) => { + const bitOffset = byteOffset * 8; + const buffer = Buffer.alloc(4 + byteOffset); + const field = uint32(undefined, { littleEndian: false }); + type field = CoderType; + + expect(field.encodeInto(value, buffer, bitOffset)).toEqual( + ok(bitOffset + 32) + ); + expect(buffer.readUInt32BE(byteOffset)).toEqual(value); + expect(field.decodeFrom(buffer, bitOffset)).toEqual( + typedAs>(ok({ value, bitOffset: bitOffset + 32 })) + ); + } + ) + ); +}); + test('uint32 with enumeration', () => { enum Enum { A = 1, diff --git a/libs/message-coder/src/uint32_coder.ts b/libs/message-coder/src/uint32_coder.ts index b675c5f38..3a70bffea 100644 --- a/libs/message-coder/src/uint32_coder.ts +++ b/libs/message-coder/src/uint32_coder.ts @@ -11,11 +11,25 @@ import { } from './types'; import { UintCoder } from './uint_coder'; +interface Uint32CoderOptions { + littleEndian: boolean; +} + /** * Coder for a uint32, aka a 32-bit unsigned integer. Uses little-endian byte * order. */ export class Uint32Coder extends UintCoder { + private readonly littleEndian: boolean; + + constructor( + enumeration?: unknown, + { littleEndian = true }: Partial = {} + ) { + super(enumeration); + this.littleEndian = littleEndian; + } + bitLength(): BitLength { return 32; } @@ -32,14 +46,20 @@ export class Uint32Coder extends UintCoder { this.validateValue(value).okOrElse(fail); return this.encodeUsing(buffer, bitOffset, (byteOffset) => - buffer.writeUInt32LE(value, byteOffset) + this.littleEndian + ? buffer.writeUInt32LE(value, byteOffset) + : buffer.writeUInt32BE(value, byteOffset) ); }); } decodeFrom(buffer: Buffer, bitOffset: BitOffset): DecodeResult { return this.decodeUsing(buffer, bitOffset, (byteOffset) => - this.validateValue(buffer.readUInt32LE(byteOffset)) + this.validateValue( + this.littleEndian + ? buffer.readUInt32LE(byteOffset) + : buffer.readUInt32BE(byteOffset) + ) ); } } @@ -49,7 +69,8 @@ export class Uint32Coder extends UintCoder { */ // eslint-disable-next-line vx/gts-no-return-type-only-generics -- TS does not have a way of saying "I want an enum of numbers" export function uint32( - enumeration?: unknown + enumeration?: unknown, + options?: Uint32CoderOptions ): Coder { - return new Uint32Coder(enumeration) as unknown as Coder; + return new Uint32Coder(enumeration, options) as unknown as Coder; }