Skip to content

Commit

Permalink
support big-endian in uint16 and uint32 coders (#3477)
Browse files Browse the repository at this point in the history
* support big-endian in uint16 and uint32 coders

* removed unused conditional

* Update libs/message-coder/src/uint16_coder.ts

Co-authored-by: Brian Donovan <[email protected]>

* Update libs/message-coder/src/uint32_coder.ts

Co-authored-by: Brian Donovan <[email protected]>

* fix reference to params

---------

Co-authored-by: Brian Donovan <[email protected]>
  • Loading branch information
kshen0 and eventualbuddha authored May 19, 2023
1 parent 850a112 commit f094e51
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 8 deletions.
23 changes: 23 additions & 0 deletions libs/message-coder/src/uint16_coder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof field>;

expect(field.encodeInto(value, buffer, bitOffset)).toEqual(
ok(bitOffset + 16)
);
expect(buffer.readUInt16BE(byteOffset)).toEqual(value);
expect(field.decodeFrom(buffer, bitOffset)).toEqual(
typedAs<DecodeResult<field>>(ok({ value, bitOffset: bitOffset + 16 }))
);
}
)
);
});

test('uint16 with enumeration', () => {
enum Enum {
A = 1,
Expand Down
29 changes: 25 additions & 4 deletions libs/message-coder/src/uint16_coder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uint16CoderOptions> = {}
) {
super(enumeration);
this.littleEndian = littleEndian;
}

bitLength(): BitLength {
return 16;
}
Expand All @@ -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<Uint16> {
return this.decodeUsing(buffer, bitOffset, (byteOffset) =>
this.validateValue(buffer.readUInt16LE(byteOffset))
this.validateValue(
this.littleEndian
? buffer.readUInt16LE(byteOffset)
: buffer.readUInt16BE(byteOffset)
)
);
}
}
Expand All @@ -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<T extends number = Uint16>(
enumeration?: unknown
enumeration?: unknown,
options?: Uint16CoderOptions
): Coder<T> {
return new Uint16Coder(enumeration) as unknown as Coder<T>;
return new Uint16Coder(enumeration, options) as unknown as Coder<T>;
}
23 changes: 23 additions & 0 deletions libs/message-coder/src/uint32_coder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof field>;

expect(field.encodeInto(value, buffer, bitOffset)).toEqual(
ok(bitOffset + 32)
);
expect(buffer.readUInt32BE(byteOffset)).toEqual(value);
expect(field.decodeFrom(buffer, bitOffset)).toEqual(
typedAs<DecodeResult<field>>(ok({ value, bitOffset: bitOffset + 32 }))
);
}
)
);
});

test('uint32 with enumeration', () => {
enum Enum {
A = 1,
Expand Down
29 changes: 25 additions & 4 deletions libs/message-coder/src/uint32_coder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Uint32CoderOptions> = {}
) {
super(enumeration);
this.littleEndian = littleEndian;
}

bitLength(): BitLength {
return 32;
}
Expand All @@ -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<Uint32> {
return this.decodeUsing(buffer, bitOffset, (byteOffset) =>
this.validateValue(buffer.readUInt32LE(byteOffset))
this.validateValue(
this.littleEndian
? buffer.readUInt32LE(byteOffset)
: buffer.readUInt32BE(byteOffset)
)
);
}
}
Expand All @@ -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<T extends number = Uint32>(
enumeration?: unknown
enumeration?: unknown,
options?: Uint32CoderOptions
): Coder<T> {
return new Uint32Coder(enumeration) as unknown as Coder<T>;
return new Uint32Coder(enumeration, options) as unknown as Coder<T>;
}

0 comments on commit f094e51

Please sign in to comment.