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 { buffer } from "./buffer.js";
|
||||||
import type { ExactReadable } from "./readable.js";
|
import type { ExactReadable } from "./readable.js";
|
||||||
import { ExactReadableEndedError } from "./readable.js";
|
import {
|
||||||
|
ExactReadableEndedError,
|
||||||
|
Uint8ArrayExactReadable,
|
||||||
|
} from "./readable.js";
|
||||||
import { struct } from "./struct.js";
|
import { struct } from "./struct.js";
|
||||||
|
|
||||||
describe("buffer", () => {
|
describe("buffer", () => {
|
||||||
describe("fixed size", () => {
|
describe("fixed size", () => {
|
||||||
|
it("should have correct size", () => {
|
||||||
|
const a = buffer(10);
|
||||||
|
assert.strictEqual(a.size, 10);
|
||||||
|
});
|
||||||
|
|
||||||
it("should deserialize", () => {
|
it("should deserialize", () => {
|
||||||
const A = struct({ value: buffer(10) }, { littleEndian: false });
|
const a = buffer(10);
|
||||||
const reader: ExactReadable = {
|
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
position: 0,
|
assert.deepStrictEqual(
|
||||||
readExactly() {
|
a.deserialize(new Uint8ArrayExactReadable(data), {
|
||||||
return new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
dependencies: {} as never,
|
||||||
},
|
littleEndian: true,
|
||||||
};
|
}),
|
||||||
assert.deepStrictEqual(A.deserialize(reader), {
|
data,
|
||||||
value: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw for not enough data", () => {
|
it("should throw for not enough data", () => {
|
||||||
const A = struct({ value: buffer(10) }, { littleEndian: false });
|
const a = buffer(10);
|
||||||
const reader: ExactReadable = {
|
const data = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
position: 0,
|
|
||||||
readExactly() {
|
|
||||||
(this as { position: number }).position = 5;
|
|
||||||
throw new ExactReadableEndedError();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
assert.throws(
|
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(
|
return field(
|
||||||
0,
|
lengthOrField,
|
||||||
"byob",
|
"byob",
|
||||||
(value, { buffer, index }) => {
|
(value, { buffer, index }) => {
|
||||||
buffer.set(value.slice(0, lengthOrField), index);
|
buffer.set(value.slice(0, lengthOrField), index);
|
||||||
|
@ -131,7 +131,7 @@ function _buffer(
|
||||||
}
|
}
|
||||||
|
|
||||||
return field(
|
return field(
|
||||||
0,
|
lengthOrField,
|
||||||
"byob",
|
"byob",
|
||||||
(value, { buffer, index }) => {
|
(value, { buffer, index }) => {
|
||||||
buffer.set(value.slice(0, lengthOrField), index);
|
buffer.set(value.slice(0, lengthOrField), index);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import * as assert from "node:assert";
|
import * as assert from "node:assert";
|
||||||
import { describe, it } from "node:test";
|
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 { Uint8ArrayExactReadable } from "./readable.js";
|
||||||
|
import { string } from "./string.js";
|
||||||
import { struct } from "./struct.js";
|
import { struct } from "./struct.js";
|
||||||
|
|
||||||
describe("Struct", () => {
|
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> {
|
): Struct<Fields, Extra, PostDeserialize> {
|
||||||
const fieldList = Object.entries(fields);
|
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 littleEndian = options.littleEndian;
|
||||||
const extra = options.extra
|
const extra = options.extra
|
||||||
|
@ -109,10 +117,11 @@ export function struct<
|
||||||
|
|
||||||
return {
|
return {
|
||||||
littleEndian,
|
littleEndian,
|
||||||
type: "byob",
|
|
||||||
fields,
|
fields,
|
||||||
size,
|
|
||||||
extra: options.extra,
|
extra: options.extra,
|
||||||
|
|
||||||
|
type: byob ? "byob" : "default",
|
||||||
|
size,
|
||||||
serialize(
|
serialize(
|
||||||
source: FieldsInit<Fields>,
|
source: FieldsInit<Fields>,
|
||||||
bufferOrContext?: Uint8Array | StructSerializeContext,
|
bufferOrContext?: Uint8Array | StructSerializeContext,
|
||||||
|
|
|
@ -13,9 +13,6 @@ export type StructSerializeContext = Omit<
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface StructSerializer<T> extends FieldSerializer<T> {
|
export interface StructSerializer<T> extends FieldSerializer<T> {
|
||||||
type: "byob";
|
|
||||||
size: number;
|
|
||||||
|
|
||||||
serialize(source: T): Uint8Array;
|
serialize(source: T): Uint8Array;
|
||||||
serialize(source: T, buffer: Uint8Array): number;
|
serialize(source: T, buffer: Uint8Array): number;
|
||||||
serialize(source: T, context: StructSerializeContext): number;
|
serialize(source: T, context: StructSerializeContext): number;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue