Skip to content

Commit e6ceee7

Browse files
committed
Bump to 0.4.4
1 parent 16ec6b2 commit e6ceee7

4 files changed

Lines changed: 30 additions & 12 deletions

File tree

bench/browser/worker-vs-clone.spec.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ test.describe("Worker Transfer vs Structured Clone Benchmark", () => {
347347

348348
// Pre-encode keys outside the hot loop
349349
const enc = new TextEncoder();
350-
const keys = { name: enc.encode("name"), input: enc.encode("input"), command: enc.encode("command") };
350+
const keys = { name: enc.encode("name"), input: enc.encode("input"), command: enc.encode("command"), path: enc.encode("path") };
351351

352352
function findField(docId: number, objIdx: number, key: Uint8Array) {
353353
const ptr = me.alloc(key.length) >>> 0;
@@ -369,12 +369,18 @@ test.describe("Worker Transfer vs Structured Clone Benchmark", () => {
369369
const docId = me.doc_import_tape(ptr, bytes.length);
370370
me.dealloc(ptr, bytes.length);
371371

372-
// Read fields via tape navigation
372+
if (docId < 0) {
373+
resolve({ mainBlock: performance.now() - t0, parseMs: e.data.parseMs });
374+
return;
375+
}
376+
377+
// Read same fields as other approaches: name, input.command, input.path
373378
findField(docId, 1, keys.name);
374379
const inputIdx = findField(docId, 1, keys.input);
375380
findField(docId, inputIdx, keys.command);
381+
findField(docId, inputIdx, keys.path);
376382

377-
if (docId >= 0) me.doc_free(docId);
383+
me.doc_free(docId);
378384

379385
const mainBlock = performance.now() - t0;
380386
resolve({ mainBlock, parseMs: e.data.parseMs });

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vectorjson",
3-
"version": "0.4.3",
3+
"version": "0.4.4",
44
"description": "O(n) WASM SIMD JSON parser for AI agents — stream fields instantly, abort errors early, offload parsing to Workers",
55
"type": "module",
66
"main": "dist/index.js",

src/js/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,6 +2444,7 @@ export async function init(options?: {
24442444
},
24452445

24462446
importTape(buf: ArrayBuffer): unknown {
2447+
if (buf.byteLength < 8) throw new Error("VectorJSON: tape buffer too small");
24472448
const bytes = new Uint8Array(buf);
24482449
const wasmPtr = engine.alloc(bytes.length) >>> 0;
24492450
if (wasmPtr === 0) throw new Error("VectorJSON: allocation failed for tape import");

src/zig/main.zig

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,20 +1675,27 @@ fn docTapeDims(p: anytype) TapeDims {
16751675
};
16761676
}
16771677

1678+
/// Packed buffer total size (overflow-safe).
1679+
fn tapePackedSize(d: TapeDims) u32 {
1680+
const tape_bytes: u64 = @as(u64, d.tape_count) * 8;
1681+
const total: u64 = 8 + tape_bytes + @as(u64, d.input_len);
1682+
if (total > std.math.maxInt(u32)) return 0;
1683+
return @intCast(total);
1684+
}
1685+
16781686
/// Returns the size needed for the packed tape buffer.
16791687
export fn doc_export_tape_size(doc_id: i32) u32 {
16801688
const p = getDocParser(doc_id) orelse return 0;
1681-
const d = docTapeDims(p);
1682-
return 8 + d.tape_count * 8 + d.input_len;
1689+
return tapePackedSize(docTapeDims(p));
16831690
}
16841691

16851692
/// Write packed tape buffer to out_ptr. Returns bytes written, or 0 on error.
16861693
export fn doc_export_tape(doc_id: i32, out_ptr: [*]u8, out_cap: u32) u32 {
16871694
const p = getDocParser(doc_id) orelse return 0;
16881695
const d = docTapeDims(p);
1696+
const total = tapePackedSize(d);
1697+
if (total == 0 or out_cap < total) return 0;
16891698
const tape_bytes = d.tape_count * 8;
1690-
const total: u32 = 8 + tape_bytes + d.input_len;
1691-
if (out_cap < total) return 0;
16921699

16931700
// Header: tape_count, input_len (WASM is always little-endian)
16941701
const header: [*]align(1) u32 = @ptrCast(out_ptr);
@@ -1713,7 +1720,10 @@ export fn doc_import_tape(buf_ptr: [*]const u8, buf_len: u32) i32 {
17131720
const tape_count = header[0];
17141721
const input_len = header[1];
17151722

1716-
// Overflow-safe size validation: check tape_bytes won't overflow u32
1723+
// A valid tape has at least 2 words (root opening + closing)
1724+
if (tape_count < 2) return -1;
1725+
1726+
// Overflow-safe size validation
17171727
const tape_bytes: u64 = @as(u64, tape_count) * 8;
17181728
const expected: u64 = 8 + tape_bytes + @as(u64, input_len);
17191729
if (expected > buf_len) return -1;
@@ -1747,9 +1757,10 @@ export fn doc_import_tape(buf_ptr: [*]const u8, buf_len: u32) i32 {
17471757
// Activate slot
17481758
doc_active[uid] = true;
17491759
doc_is_json5[uid] = false;
1750-
// Mark src positions as built (empty) — imported tapes lack token data
1751-
// needed by buildDocSrcPositions, so doc_get_src_pos returns 0xFFFFFFFF.
1752-
doc_src[uid] = .{ .positions = doc_src[uid].positions, .cap = doc_src[uid].cap, .len = 0, .built = true };
1760+
// Imported tapes lack token data for buildDocSrcPositions.
1761+
// Mark as built with len=0 so doc_get_src_pos returns 0xFFFFFFFF.
1762+
doc_src[uid].len = 0;
1763+
doc_src[uid].built = true;
17531764

17541765
return @intCast(uid);
17551766
}

0 commit comments

Comments
 (0)