From 28efa80222d4329a01620dd84fd9673e2e0d2e0c Mon Sep 17 00:00:00 2001 From: Keichi Takahashi Date: Sun, 18 Oct 2026 22:46:22 +0900 Subject: [PATCH] Fix string with both length and zeroTerminated options --- lib/binary_parser.ts | 11 ++++++----- test/primitive_parser.ts | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/binary_parser.ts b/lib/binary_parser.ts index 785c62c..f0d78ef 100644 --- a/lib/binary_parser.ts +++ b/lib/binary_parser.ts @@ -1202,17 +1202,18 @@ export class Parser { const toHex = 'b => b.toString(16).padStart(2, "0")'; if (this.options.length != null && this.options.zeroTerminated) { - const len = this.options.length; + const len = ctx.generateTmpVariable(); + ctx.pushCode(`var ${len} = ${ctx.generateOption(this.options.length)};`); ctx.pushCode(`var ${start} = offset;`); ctx.pushCode( - `while(offset - ${start} < ${len} && dataView.getUint8(offset++) !== 0);`, + `while(offset - ${start} < ${len} && dataView.getUint8(offset) !== 0) offset++;`, ); - const end = `offset - ${start} < ${len} ? offset - 1 : offset`; ctx.pushCode( isHex - ? `${name} = Array.from(buffer.subarray(${start}, ${end}), ${toHex}).join('');` - : `${name} = new TextDecoder('${encoding}').decode(buffer.subarray(${start}, ${end}));`, + ? `${name} = Array.from(buffer.subarray(${start}, offset), ${toHex}).join('');` + : `${name} = new TextDecoder('${encoding}').decode(buffer.subarray(${start}, offset));`, ); + ctx.pushCode(`if (offset - ${start} < ${len}) offset++;`); } else if (this.options.length != null) { const len = ctx.generateOption(this.options.length); ctx.pushCode( diff --git a/test/primitive_parser.ts b/test/primitive_parser.ts index 0de578b..69f4854 100644 --- a/test/primitive_parser.ts +++ b/test/primitive_parser.ts @@ -382,6 +382,27 @@ function primitiveParserTests( c: "ij", }); }); + it("should exclude null terminator at the last byte of fixed-length string", () => { + const buffer = factory(new TextEncoder().encode("abc\u0000de")); + const parser = Parser.start() + .string("a", { length: 4, zeroTerminated: true }) + .string("b", { length: 2 }); + + expect(parser.parse(buffer)).toStrictEqual({ a: "abc", b: "de" }); + }); + it("should parse zero terminated string with function/field length", () => { + const buffer = factory(new TextEncoder().encode("\u0004abcdef\u0000")); + const parser = Parser.start() + .uint8("len") + .string("a", { length: "len", zeroTerminated: true }) + .string("b", { length: () => 4, zeroTerminated: true }); + + expect(parser.parse(buffer)).toStrictEqual({ + len: 4, + a: "abcd", + b: "ef", + }); + }); it("should strip trailing null characters", () => { const buffer = hexToBuf("746573740000"); const parser1 = Parser.start().string("str", {