Loading .changeset/four-humans-sip.md 0 → 100644 +5 −0 Original line number Diff line number Diff line --- "@yume-chan/struct": patch --- Fix `buffer` and `struct` may not have correct size libraries/struct/src/buffer.spec.ts +26 −21 Original line number Diff line number Diff line Loading @@ -3,36 +3,41 @@ import { describe, it } from "node:test"; import { buffer } from "./buffer.js"; import type { ExactReadable } from "./readable.js"; import { ExactReadableEndedError } from "./readable.js"; import { ExactReadableEndedError, Uint8ArrayExactReadable, } from "./readable.js"; import { struct } from "./struct.js"; describe("buffer", () => { describe("fixed size", () => { it("should deserialize", () => { const A = struct({ value: buffer(10) }, { littleEndian: false }); const reader: ExactReadable = { position: 0, readExactly() { return new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); }, }; assert.deepStrictEqual(A.deserialize(reader), { value: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), it("should have correct size", () => { const a = buffer(10); assert.strictEqual(a.size, 10); }); it("should deserialize", () => { const a = buffer(10); const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); assert.deepStrictEqual( a.deserialize(new Uint8ArrayExactReadable(data), { dependencies: {} as never, littleEndian: true, }), data, ); }); it("should throw for not enough data", () => { const A = struct({ value: buffer(10) }, { littleEndian: false }); const reader: ExactReadable = { position: 0, readExactly() { (this as { position: number }).position = 5; throw new ExactReadableEndedError(); }, }; const a = buffer(10); const data = new Uint8Array([1, 2, 3, 4, 5]); assert.throws( () => A.deserialize(reader), /The underlying readable was ended before the struct was fully deserialized/, () => a.deserialize(new Uint8ArrayExactReadable(data), { dependencies: {} as never, littleEndian: true, }), /Error: ExactReadable ended/, ); }); Loading libraries/struct/src/buffer.ts +2 −2 Original line number Diff line number Diff line Loading @@ -99,7 +99,7 @@ function _buffer( } return field( 0, lengthOrField, "byob", (value, { buffer, index }) => { buffer.set(value.slice(0, lengthOrField), index); Loading Loading @@ -131,7 +131,7 @@ function _buffer( } return field( 0, lengthOrField, "byob", (value, { buffer, index }) => { buffer.set(value.slice(0, lengthOrField), index); Loading libraries/struct/src/struct.spec.ts +52 −1 Original line number Diff line number Diff line import * as assert from "node:assert"; import { describe, it } from "node:test"; import { u16, u8 } from "./number.js"; import { u16, u32, u8 } from "./number.js"; import { Uint8ArrayExactReadable } from "./readable.js"; import { string } from "./string.js"; import { struct } from "./struct.js"; describe("Struct", () => { Loading Loading @@ -30,4 +31,54 @@ describe("Struct", () => { }, ); }); describe("type", () => { it("should be `byob` when empty", () => { const A = struct({}, { littleEndian: true }); assert.strictEqual(A.type, "byob"); }); it("should be `byob` if all fields are byob", () => { const A = struct({ a: u8 }, { littleEndian: true }); assert.strictEqual(A.type, "byob"); const B = struct({ a: u8, b: u16 }, { littleEndian: true }); assert.strictEqual(B.type, "byob"); const C = struct( { a: u8, b: u16, c: string(10) }, { littleEndian: true }, ); assert.strictEqual(C.type, "byob"); }); it("should be `default` if any field is default", () => { const A = struct({ a: string(u32) }, { littleEndian: true }); assert.strictEqual(A.type, "default"); const B = struct( { a: string(u32), b: u32 }, { littleEndian: true }, ); assert.strictEqual(B.type, "default"); }); }); describe("size", () => { it("should be 0 when empty", () => { const A = struct({}, { littleEndian: true }); assert.strictEqual(A.size, 0); }); it("should be sum of all fields", () => { const A = struct({ a: u8, b: u16 }, { littleEndian: true }); assert.strictEqual(A.size, 3); const B = struct({ a: string(10) }, { littleEndian: true }); assert.strictEqual(B.size, 10); const C = struct({ a: string(u32) }, { littleEndian: true }); assert.strictEqual(C.size, 4); }); }); }); libraries/struct/src/struct.ts +12 −3 Original line number Diff line number Diff line Loading @@ -100,7 +100,15 @@ export function struct< }, ): Struct<Fields, Extra, PostDeserialize> { const fieldList = Object.entries(fields); const size = fieldList.reduce((sum, [, field]) => sum + field.size, 0); let size = 0; let byob = true; for (const [, field] of fieldList) { size += field.size; if (byob && field.type !== "byob") { byob = false; } } const littleEndian = options.littleEndian; const extra = options.extra Loading @@ -109,10 +117,11 @@ export function struct< return { littleEndian, type: "byob", fields, size, extra: options.extra, type: byob ? "byob" : "default", size, serialize( source: FieldsInit<Fields>, bufferOrContext?: Uint8Array | StructSerializeContext, Loading Loading
.changeset/four-humans-sip.md 0 → 100644 +5 −0 Original line number Diff line number Diff line --- "@yume-chan/struct": patch --- Fix `buffer` and `struct` may not have correct size
libraries/struct/src/buffer.spec.ts +26 −21 Original line number Diff line number Diff line Loading @@ -3,36 +3,41 @@ import { describe, it } from "node:test"; import { buffer } from "./buffer.js"; import type { ExactReadable } from "./readable.js"; import { ExactReadableEndedError } from "./readable.js"; import { ExactReadableEndedError, Uint8ArrayExactReadable, } from "./readable.js"; import { struct } from "./struct.js"; describe("buffer", () => { describe("fixed size", () => { it("should deserialize", () => { const A = struct({ value: buffer(10) }, { littleEndian: false }); const reader: ExactReadable = { position: 0, readExactly() { return new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); }, }; assert.deepStrictEqual(A.deserialize(reader), { value: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), it("should have correct size", () => { const a = buffer(10); assert.strictEqual(a.size, 10); }); it("should deserialize", () => { const a = buffer(10); const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); assert.deepStrictEqual( a.deserialize(new Uint8ArrayExactReadable(data), { dependencies: {} as never, littleEndian: true, }), data, ); }); it("should throw for not enough data", () => { const A = struct({ value: buffer(10) }, { littleEndian: false }); const reader: ExactReadable = { position: 0, readExactly() { (this as { position: number }).position = 5; throw new ExactReadableEndedError(); }, }; const a = buffer(10); const data = new Uint8Array([1, 2, 3, 4, 5]); assert.throws( () => A.deserialize(reader), /The underlying readable was ended before the struct was fully deserialized/, () => a.deserialize(new Uint8ArrayExactReadable(data), { dependencies: {} as never, littleEndian: true, }), /Error: ExactReadable ended/, ); }); Loading
libraries/struct/src/buffer.ts +2 −2 Original line number Diff line number Diff line Loading @@ -99,7 +99,7 @@ function _buffer( } return field( 0, lengthOrField, "byob", (value, { buffer, index }) => { buffer.set(value.slice(0, lengthOrField), index); Loading Loading @@ -131,7 +131,7 @@ function _buffer( } return field( 0, lengthOrField, "byob", (value, { buffer, index }) => { buffer.set(value.slice(0, lengthOrField), index); Loading
libraries/struct/src/struct.spec.ts +52 −1 Original line number Diff line number Diff line import * as assert from "node:assert"; import { describe, it } from "node:test"; import { u16, u8 } from "./number.js"; import { u16, u32, u8 } from "./number.js"; import { Uint8ArrayExactReadable } from "./readable.js"; import { string } from "./string.js"; import { struct } from "./struct.js"; describe("Struct", () => { Loading Loading @@ -30,4 +31,54 @@ describe("Struct", () => { }, ); }); describe("type", () => { it("should be `byob` when empty", () => { const A = struct({}, { littleEndian: true }); assert.strictEqual(A.type, "byob"); }); it("should be `byob` if all fields are byob", () => { const A = struct({ a: u8 }, { littleEndian: true }); assert.strictEqual(A.type, "byob"); const B = struct({ a: u8, b: u16 }, { littleEndian: true }); assert.strictEqual(B.type, "byob"); const C = struct( { a: u8, b: u16, c: string(10) }, { littleEndian: true }, ); assert.strictEqual(C.type, "byob"); }); it("should be `default` if any field is default", () => { const A = struct({ a: string(u32) }, { littleEndian: true }); assert.strictEqual(A.type, "default"); const B = struct( { a: string(u32), b: u32 }, { littleEndian: true }, ); assert.strictEqual(B.type, "default"); }); }); describe("size", () => { it("should be 0 when empty", () => { const A = struct({}, { littleEndian: true }); assert.strictEqual(A.size, 0); }); it("should be sum of all fields", () => { const A = struct({ a: u8, b: u16 }, { littleEndian: true }); assert.strictEqual(A.size, 3); const B = struct({ a: string(10) }, { littleEndian: true }); assert.strictEqual(B.size, 10); const C = struct({ a: string(u32) }, { littleEndian: true }); assert.strictEqual(C.size, 4); }); }); });
libraries/struct/src/struct.ts +12 −3 Original line number Diff line number Diff line Loading @@ -100,7 +100,15 @@ export function struct< }, ): Struct<Fields, Extra, PostDeserialize> { const fieldList = Object.entries(fields); const size = fieldList.reduce((sum, [, field]) => sum + field.size, 0); let size = 0; let byob = true; for (const [, field] of fieldList) { size += field.size; if (byob && field.type !== "byob") { byob = false; } } const littleEndian = options.littleEndian; const extra = options.extra Loading @@ -109,10 +117,11 @@ export function struct< return { littleEndian, type: "byob", fields, size, extra: options.extra, type: byob ? "byob" : "default", size, serialize( source: FieldsInit<Fields>, bufferOrContext?: Uint8Array | StructSerializeContext, Loading