Skip to content

Commit

Permalink
struct-type: Use native byte ordering in memory, static read/write to…
Browse files Browse the repository at this point in the history
… specify other byte orderings
  • Loading branch information
rbuckton committed Jun 7, 2023
1 parent f712fca commit 5a7b56e
Show file tree
Hide file tree
Showing 13 changed files with 531 additions and 76 deletions.
43 changes: 43 additions & 0 deletions packages/struct-type/src/__tests__/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2133,3 +2133,46 @@ describe("methods", () => {
});
});
});
describe("byte order", () => {
const Int32Arrayx2 = ArrayType(int32, 2);
describe("read()", () => {
it("big-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
view.setInt32(0, 0x12345678, false);
view.setInt32(4, 0x98765432 >> 0, false);
const ar = Int32Arrayx2.read(view.buffer, 0, false, "BE");
expect(ar[0]).toBe(0x12345678);
expect(ar[1]).toBe(0x98765432 >> 0);
});
it("little-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
view.setInt32(0, 0x12345678, true);
view.setInt32(4, 0x98765432 >> 0, true);
const ar = Int32Arrayx2.read(view.buffer, 0, false, "LE");
expect(ar[0]).toBe(0x12345678);
expect(ar[1]).toBe(0x98765432 >> 0);
});
});
describe("write()", () => {
it("big-endian byte order", () => {
const ar = new Int32Arrayx2([
0x12345678,
0x98765432,
]);
const view = new DataView(new ArrayBuffer(8));
Int32Arrayx2.write(view.buffer, 0, ar, "BE");
expect(view.getInt32(0, false)).toBe(0x12345678);
expect(view.getInt32(4, false)).toBe(0x98765432 >> 0);
});
it("little-endian byte order", () => {
const ar = new Int32Arrayx2([
0x12345678,
0x98765432,
]);
const view = new DataView(new ArrayBuffer(8));
Int32Arrayx2.write(view.buffer, 0, ar, "LE");
expect(view.getInt32(0, true)).toBe(0x12345678);
expect(view.getInt32(4, true)).toBe(0x98765432 >> 0);
});
});
});
50 changes: 50 additions & 0 deletions packages/struct-type/src/__tests__/primitive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { int32 } from "..";

describe("int32", () => {
describe("read()", () => {
it("native byte order", () => {
const ar = new Int32Array(2);
ar[0] = 0x12345678;
ar[1] = 0x98765432 >> 0;
expect(int32.read(ar.buffer, 0)).toBe(0x12345678);
expect(int32.read(ar.buffer, 4)).toBe(0x98765432 >> 0);
});
it("big-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
view.setInt32(0, 0x12345678, false);
view.setInt32(4, 0x98765432 >> 0, true);
expect(int32.read(view.buffer, 0, "BE")).toBe(0x12345678);
expect(int32.read(view.buffer, 4, "BE")).toBe(0x32547698 >> 0);
});
it("little-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
view.setInt32(0, 0x12345678, false);
view.setInt32(4, 0x98765432 >> 0, true);
expect(int32.read(view.buffer, 0, "LE")).toBe(0x78563412);
expect(int32.read(view.buffer, 4, "LE")).toBe(0x98765432 >> 0);
});
});
describe("write()", () => {
it("native byte order", () => {
const ar = new Int32Array(2);
int32.write(ar.buffer, 0, 0x12345678);
int32.write(ar.buffer, 4, 0x98765432 >> 0);
expect(ar[0]).toBe(0x12345678);
expect(ar[1]).toBe(0x98765432 >> 0);
});
it("big-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
int32.write(view.buffer, 0, 0x12345678, "BE");
int32.write(view.buffer, 4, 0x98765432 >> 0, "BE");
expect(view.getInt32(0, false)).toBe(0x12345678);
expect(view.getInt32(4, true)).toBe(0x32547698 >> 0);
});
it("little-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
int32.write(view.buffer, 0, 0x12345678, "LE");
int32.write(view.buffer, 4, 0x98765432 >> 0, "LE");
expect(view.getInt32(0, false)).toBe(0x78563412);
expect(view.getInt32(4, true)).toBe(0x98765432 >> 0);
});
});
});
47 changes: 45 additions & 2 deletions packages/struct-type/src/__tests__/struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,49 @@ describe("baseType", () => {
expect(p.z).toBe(3);
});
});
describe("types", () => {

describe("byte order", () => {
const Point = StructType({
x: int32,
y: int32
});
describe("read()", () => {
it("big-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
view.setInt32(0, 0x12345678, false);
view.setInt32(4, 0x98765432 >> 0, false);
const p = Point.read(view.buffer, 0, false, "BE");
expect(p.x).toBe(0x12345678);
expect(p.y).toBe(0x98765432 >> 0);
});
it("little-endian byte order", () => {
const view = new DataView(new ArrayBuffer(8));
view.setInt32(0, 0x12345678, true);
view.setInt32(4, 0x98765432 >> 0, true);
const p = Point.read(view.buffer, 0, false, "LE");
expect(p.x).toBe(0x12345678);
expect(p.y).toBe(0x98765432 >> 0);
});
});
describe("write()", () => {
it("big-endian byte order", () => {
const p = new Point({
x: 0x12345678,
y: 0x98765432,
});
const view = new DataView(new ArrayBuffer(8));
Point.write(view.buffer, 0, p, "BE");
expect(view.getInt32(0, false)).toBe(0x12345678);
expect(view.getInt32(4, false)).toBe(0x98765432 >> 0);
});
it("little-endian byte order", () => {
const p = new Point({
x: 0x12345678,
y: 0x98765432,
});
const view = new DataView(new ArrayBuffer(8));
Point.write(view.buffer, 0, p, "LE");
expect(view.getInt32(0, true)).toBe(0x12345678);
expect(view.getInt32(4, true)).toBe(0x98765432 >> 0);
});
});
});
82 changes: 78 additions & 4 deletions packages/struct-type/src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
limitations under the License.
*/

import type { Endianness } from "./endianness.js";
import { ArrayTypeImpl } from "./internal/array/arrayTypeImpl.js";
import type { InitType, RuntimeType, Type } from "./type.js";

Expand All @@ -25,7 +26,7 @@ export interface TypedArray<TType extends Type, TFixedLength extends number = nu
readonly byteOffset: number;
readonly byteLength: number;

writeTo(buffer: ArrayBufferLike, byteOffset?: number): void;
writeTo(buffer: ArrayBufferLike, byteOffset?: number, byteOrder?: Endianness): void;
toArray(): RuntimeType<TType>[];
copyWithin(target: number, start: number, end?: number): this;
every(predicate: (value: RuntimeType<TType>, index: number) => unknown): boolean;
Expand Down Expand Up @@ -61,12 +62,45 @@ export interface ArrayType<TType extends Type> {
new (length: number, shared: boolean): TypedArray<TType>;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): TypedArray<TType>;
new (elements: ArrayLike<InitType<TType>>, shared?: boolean): TypedArray<TType>;
prototype: TypedArray<TType>;

/**
* The number of bytes per each element of the array.
*/
readonly BYTES_PER_ELEMENT: number;
readonly SIZE: number | undefined;
readonly fixedLength: number | undefined;
prototype: TypedArray<TType>;

/**
* Reads an array of values value from the buffer. The resulting array value will be backed by its own buffer.
* @param buffer The `ArrayBuffer` or `SharedArrayBuffer` from which to read the array.
* @param byteOffset The byte offset into {@link buffer} at which to start reading.
* @param shared When `true`, the resulting value will be backed by a `SharedArrayBuffer`.
* @param byteOrder The endianness to use when reading the array. If unspecified, the native byte order will be used.
*/
read(buffer: ArrayBufferLike, byteOffset: number, shared?: boolean, byteOrder?: Endianness): TypedArray<TType>;
/**
* Reads an array of values value from the buffer. The resulting array value will be backed by its own buffer.
* @param buffer The `ArrayBuffer` or `SharedArrayBuffer` from which to read the array.
* @param byteOffset The byte offset into {@link buffer} at which to start reading.
* @param length The number of array elements to read from the buffer.
* @param shared When `true`, the resulting value will be backed by a `SharedArrayBuffer`.
* @param byteOrder The endianness to use when reading the array. If unspecified, the native byte order will be used.
*/
read(buffer: ArrayBufferLike, byteOffset: number, length?: number, shared?: boolean, byteOrder?: Endianness): TypedArray<TType>;

/**
* Writes an array of values to a buffer.
* @param buffer The `ArrayBuffer` or `SharedArrayBuffer` into which to write the array.
* @param byteOffset The byte offset into {@link buffer} at which to start writing.
* @param value The array to write.
* @param byteOrder The endianness to use when writing the array. If unspecified, the native byte order will be used.
*/
write(buffer: ArrayBufferLike, byteOffset: number, value: TypedArray<TType>, byteOrder?: Endianness): void;

/**
* Gets a fixed-length subtype for this array type.
*/
toFixed<TFixedLength extends number>(fixedLength: TFixedLength): FixedLengthArrayType<TType, TFixedLength>;

// #region Related Types
Expand All @@ -83,12 +117,53 @@ export interface FixedLengthArrayType<TType extends Type, TFixedLength extends n
new (shared: boolean): TypedArray<TType, TFixedLength>;
new (buffer: ArrayBufferLike, byteOffset?: number): TypedArray<TType, TFixedLength>;
new (elements: ArrayLike<InitType<TType>>, shared?: boolean): TypedArray<TType, TFixedLength>;
prototype: TypedArray<TType, TFixedLength>;

/**
* The number of bytes per each element of the array.
*/
readonly BYTES_PER_ELEMENT: number;

/**
* The number of bytes for the entire array.
*/
readonly SIZE: number;

/**
* the fixed length of the array.
*/
readonly fixedLength: TFixedLength;
prototype: TypedArray<TType, TFixedLength>;

/**
* Reads an array of values value from the buffer. The resulting array value will be backed by its own buffer.
* @param buffer The `ArrayBuffer` or `SharedArrayBuffer` from which to read the array.
* @param byteOffset The byte offset into {@link buffer} at which to start reading.
* @param shared When `true`, the resulting value will be backed by a `SharedArrayBuffer`.
* @param byteOrder The endianness to use when reading the array. If unspecified, the native byte order will be used.
*/
read(buffer: ArrayBufferLike, byteOffset: number, shared?: boolean, byteOrder?: Endianness): TypedArray<TType, TFixedLength>;
/**
* Reads an array of values value from the buffer. The resulting array value will be backed by its own buffer.
* @param buffer The `ArrayBuffer` or `SharedArrayBuffer` from which to read the array.
* @param byteOffset The byte offset into {@link buffer} at which to start reading.
* @param length The number of array elements to read from the buffer.
* @param shared When `true`, the resulting value will be backed by a `SharedArrayBuffer`.
* @param byteOrder The endianness to use when reading the array. If unspecified, the native byte order will be used.
*/
read(buffer: ArrayBufferLike, byteOffset: number, length?: number, shared?: boolean, byteOrder?: Endianness): TypedArray<TType, TFixedLength>;

/**
* Writes a structured value to a buffer.
* @param buffer The `ArrayBuffer` or `SharedArrayBuffer` into which to write the value.
* @param byteOffset The byte offset into {@link buffer} at which to start writing.
* @param value The value to write.
* @param byteOrder The endianness to use when writing the value. If unspecified, the native byte order will be used.
*/
write(buffer: ArrayBufferLike, byteOffset: number, value: TypedArray<TType, TFixedLength>, byteOrder?: Endianness): void;

/**
* Gets a fixed-length subtype for this array type.
*/
toFixed<TFixedLength extends number>(fixedLength: TFixedLength): FixedLengthArrayType<TType, TFixedLength>;

// #region Related Types
Expand Down Expand Up @@ -121,7 +196,6 @@ export interface ArrayTypeConstructor {
* @param length The fixed length of the TypedArray type.
*/
new <TType extends Type, TLength extends number>(type: TType, length: TLength): FixedLengthArrayType<TType, TLength>;

prototype: Omit<ArrayType<any>, never>;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/struct-type/src/endianness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ export const isLittleEndian = new Int32Array(new Uint8Array([0x12, 0x34, 0x56, 0
export const endianness: Endianness = isLittleEndian ? "LE" : "BE";

/**
* Indicates the byte order is either big-endian (`"BE"`) or little-endian (`"LE"`).
* Indicates whether the byte representation for an integer is either big-endian (`"BE"`) or little-endian (`"LE"`).
*/
export type Endianness = "BE" | "LE";
export type Endianness = "BE" | "LE";
Loading

0 comments on commit 5a7b56e

Please sign in to comment.