[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
5

Configure Feed

Select the types of activity you want to include in your feed.

๐Ÿ› improve pack string overflow errors

Ryan Rauh (May 10, 2026, 6:55 AM EDT) a62376d0 9470772d

+79 -4
+20 -4
ops.ts
··· 52 52 return o; 53 53 } 54 54 55 - function packString(view: DataView, bytes: Uint8Array, o: number): number { 55 + function packString( 56 + view: DataView, 57 + bytes: Uint8Array, 58 + o: number, 59 + end: number, 60 + context: string, 61 + ): number { 62 + let paddedLength = Math.ceil(bytes.length / 4) * 4; 63 + let next = o + 4 + paddedLength; 64 + if (next > end) { 65 + throw new RangeError( 66 + `clayterm transfer buffer capacity exceeded while packing ${context} ` + 67 + `(${next} byte offset, ${end} byte limit). ` + 68 + `Render a smaller visible slice or reduce frame content.`, 69 + ); 70 + } 71 + 56 72 view.setUint32(o, bytes.length, true); 57 73 o += 4; 58 74 new Uint8Array(view.buffer).set(bytes, o); 59 - o += Math.ceil(bytes.length / 4) * 4; 75 + o += paddedLength; 60 76 return o; 61 77 } 62 78 ··· 82 98 o += 4; 83 99 84 100 let bytes = encoder.encode(op.id); 85 - o = packString(view, bytes, o); 101 + o = packString(view, bytes, o, end, "element id"); 86 102 87 103 let mask = 0; 88 104 if (op.layout) mask |= PROP_LAYOUT; ··· 192 208 o += 4; 193 209 194 210 let str = encoder.encode(op.content); 195 - o = packString(view, str, o); 211 + o = packString(view, str, o, end, "text content"); 196 212 break; 197 213 } 198 214 }
+7
specs/renderer-spec.md
··· 466 466 the renderer and is not an operation the caller performs or observes. The 467 467 transfer mechanism is an implementation detail described in Section 12.1. 468 468 469 + If a frame exceeds transfer-buffer capacity while packing string content, the 470 + renderer MUST throw a descriptive `RangeError` that identifies the condition as 471 + a transfer-buffer, frame-capacity, or packing overflow. The renderer MUST NOT 472 + expose only the raw host-level TypedArray message `"offset is out of bounds"` 473 + for this condition. The error message SHOULD direct callers to render a smaller 474 + visible slice or reduce frame content. 475 + 469 476 ### 9.3 Directive identity 470 477 471 478 Each element directive carries an `id` provided by the caller via `open()`.
+52
test/pack.test.ts
··· 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 + });