mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 17:59:50 +02:00
fix(struct): buffer
and struct
may not have correct size
This commit is contained in:
parent
f8b40e83b0
commit
0bcb9b804b
6 changed files with 97 additions and 30 deletions
5
.changeset/four-humans-sip.md
Normal file
5
.changeset/four-humans-sip.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@yume-chan/struct": patch
|
||||
---
|
||||
|
||||
Fix `buffer` and `struct` may not have correct size
|
|
@ -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/,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ function _buffer(
|
|||
}
|
||||
|
||||
return field(
|
||||
0,
|
||||
lengthOrField,
|
||||
"byob",
|
||||
(value, { buffer, index }) => {
|
||||
buffer.set(value.slice(0, lengthOrField), index);
|
||||
|
@ -131,7 +131,7 @@ function _buffer(
|
|||
}
|
||||
|
||||
return field(
|
||||
0,
|
||||
lengthOrField,
|
||||
"byob",
|
||||
(value, { buffer, index }) => {
|
||||
buffer.set(value.slice(0, lengthOrField), index);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
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", () => {
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
@ -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,
|
||||
|
|
|
@ -13,9 +13,6 @@ export type StructSerializeContext = Omit<
|
|||
>;
|
||||
|
||||
export interface StructSerializer<T> extends FieldSerializer<T> {
|
||||
type: "byob";
|
||||
size: number;
|
||||
|
||||
serialize(source: T): Uint8Array;
|
||||
serialize(source: T, buffer: Uint8Array): number;
|
||||
serialize(source: T, context: StructSerializeContext): number;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue