Skip to content

Commit 4d09e0c

Browse files
committed
Merge PR #33 descriptive pack errors
2 parents bb508c7 + a62376d commit 4d09e0c

3 files changed

Lines changed: 79 additions & 4 deletions

File tree

ops.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,27 @@ function packAxis(view: DataView, offset: number, axis: SizingAxis): number {
5656
return o;
5757
}
5858

59-
function packString(view: DataView, bytes: Uint8Array, o: number): number {
59+
function packString(
60+
view: DataView,
61+
bytes: Uint8Array,
62+
o: number,
63+
end: number,
64+
context: string,
65+
): number {
66+
let paddedLength = Math.ceil(bytes.length / 4) * 4;
67+
let next = o + 4 + paddedLength;
68+
if (next > end) {
69+
throw new RangeError(
70+
`clayterm transfer buffer capacity exceeded while packing ${context} ` +
71+
`(${next} byte offset, ${end} byte limit). ` +
72+
`Render a smaller visible slice or reduce frame content.`,
73+
);
74+
}
75+
6076
view.setUint32(o, bytes.length, true);
6177
o += 4;
6278
new Uint8Array(view.buffer).set(bytes, o);
63-
o += Math.ceil(bytes.length / 4) * 4;
79+
o += paddedLength;
6480
return o;
6581
}
6682

@@ -86,7 +102,7 @@ export function pack(
86102
o += 4;
87103

88104
let bytes = encoder.encode(op.id);
89-
o = packString(view, bytes, o);
105+
o = packString(view, bytes, o, end, "element id");
90106

91107
let mask = 0;
92108
if (op.layout) mask |= PROP_LAYOUT;
@@ -224,7 +240,7 @@ export function pack(
224240
o += 4;
225241

226242
let str = encoder.encode(op.content);
227-
o = packString(view, str, o);
243+
o = packString(view, str, o, end, "text content");
228244
break;
229245
}
230246
}

specs/renderer-spec.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,13 @@ form that the WASM module can process. This transfer is handled internally by
469469
the renderer and is not an operation the caller performs or observes. The
470470
transfer mechanism is an implementation detail described in Section 12.1.
471471

472+
If a frame exceeds transfer-buffer capacity while packing string content, the
473+
renderer MUST throw a descriptive `RangeError` that identifies the condition as
474+
a transfer-buffer, frame-capacity, or packing overflow. The renderer MUST NOT
475+
expose only the raw host-level TypedArray message `"offset is out of bounds"`
476+
for this condition. The error message SHOULD direct callers to render a smaller
477+
visible slice or reduce frame content.
478+
472479
### 9.3 Directive identity
473480

474481
Each element directive carries an `id` provided by the caller via `open()`.

test/pack.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, expect, it } from "./suite.ts";
2+
import { close, open, pack, text } from "../ops.ts";
3+
4+
describe("pack", () => {
5+
it("throws a descriptive RangeError when text exceeds the transfer buffer", () => {
6+
let memory = new ArrayBuffer(64);
7+
let error: unknown;
8+
9+
try {
10+
pack(
11+
[
12+
open("root"),
13+
text("x".repeat(128)),
14+
close(),
15+
],
16+
memory,
17+
0,
18+
memory.byteLength,
19+
);
20+
} catch (caught) {
21+
error = caught;
22+
}
23+
24+
expect(error).toBeInstanceOf(RangeError);
25+
expect((error as Error).message).toMatch(
26+
/transfer buffer|capacity|packing/,
27+
);
28+
expect((error as Error).message).toContain("text content");
29+
expect((error as Error).message).not.toBe("offset is out of bounds");
30+
expect((error as Error).message).toMatch(
31+
/smaller visible slice|reduce frame content/,
32+
);
33+
});
34+
35+
it("throws a descriptive RangeError when an element id exceeds the transfer buffer", () => {
36+
let memory = new ArrayBuffer(16);
37+
let error: unknown;
38+
39+
try {
40+
pack([open("x".repeat(64)), close()], memory, 0, memory.byteLength);
41+
} catch (caught) {
42+
error = caught;
43+
}
44+
45+
expect(error).toBeInstanceOf(RangeError);
46+
expect((error as Error).message).toMatch(
47+
/transfer buffer|capacity|packing/,
48+
);
49+
expect((error as Error).message).toContain("element id");
50+
expect((error as Error).message).not.toBe("offset is out of bounds");
51+
});
52+
});

0 commit comments

Comments
 (0)