diff --git a/lib/binary_parser.ts b/lib/binary_parser.ts index cf511f6..2a16b85 100644 --- a/lib/binary_parser.ts +++ b/lib/binary_parser.ts @@ -598,19 +598,19 @@ export class Parser { } string(varName: string, options: ParserOptions): this { - if (!options.zeroTerminated && !options.length && !options.greedy) { + if (!options.zeroTerminated && options.length == null && !options.greedy) { throw new Error( "One of length, zeroTerminated, or greedy must be defined for string.", ); } - if ((options.zeroTerminated || options.length) && options.greedy) { + if ((options.zeroTerminated || options.length != null) && options.greedy) { throw new Error( "greedy is mutually exclusive with length and zeroTerminated for string.", ); } - if (options.stripNull && !(options.length || options.greedy)) { + if (options.stripNull && !(options.length != null || options.greedy)) { throw new Error( "length or greedy must be defined if stripNull is enabled.", ); @@ -623,7 +623,7 @@ export class Parser { } buffer(varName: string, options: ParserOptions): this { - if (!options.length && !options.readUntil) { + if (options.length == null && !options.readUntil) { throw new Error("length or readUntil must be defined for buffer."); } @@ -640,7 +640,7 @@ export class Parser { throw new Error("Both wrapper and type must be defined for wrapped."); } - if (!options.length && !options.readUntil) { + if (options.length == null && !options.readUntil) { throw new Error("length or readUntil must be defined for wrapped."); } @@ -648,7 +648,11 @@ export class Parser { } array(varName: string, options: ParserOptions): this { - if (!options.readUntil && !options.length && !options.lengthInBytes) { + if ( + !options.readUntil && + options.length == null && + options.lengthInBytes == null + ) { throw new Error( "One of readUntil, length and lengthInBytes must be defined for array.", ); @@ -1198,11 +1202,11 @@ export class Parser { const isHex = encoding.toLowerCase() === "hex"; const toHex = 'b => b.toString(16).padStart(2, "0")'; - if (this.options.length && this.options.zeroTerminated) { + if (this.options.length != null && this.options.zeroTerminated) { const len = this.options.length; ctx.pushCode(`var ${start} = offset;`); ctx.pushCode( - `while(dataView.getUint8(offset++) !== 0 && offset - ${start} < ${len});`, + `while(offset - ${start} < ${len} && dataView.getUint8(offset++) !== 0);`, ); const end = `offset - ${start} < ${len} ? offset - 1 : offset`; ctx.pushCode( @@ -1210,7 +1214,7 @@ export class Parser { ? `${name} = Array.from(buffer.subarray(${start}, ${end}), ${toHex}).join('');` : `${name} = new TextDecoder('${encoding}').decode(buffer.subarray(${start}, ${end}));`, ); - } else if (this.options.length) { + } else if (this.options.length != null) { const len = ctx.generateOption(this.options.length); ctx.pushCode( isHex diff --git a/test/composite_parser.ts b/test/composite_parser.ts index b369b54..85f18de 100644 --- a/test/composite_parser.ts +++ b/test/composite_parser.ts @@ -13,6 +13,18 @@ function compositeParserTests( } describe("Array parser", () => { + it("should parse array with length 0", () => { + const parser = Parser.start() + .array("a", { length: 0, type: "uint8" }) + .array("b", { lengthInBytes: 0, type: "uint8" }) + .uint8("x"); + + expect(parser.parse(factory([0x41]))).toStrictEqual({ + a: [], + b: [], + x: 0x41, + }); + }); it("should parse array of primitive types", () => { const parser = Parser.start().uint8("length").array("message", { length: "length", diff --git a/test/primitive_parser.ts b/test/primitive_parser.ts index dec80c1..579f455 100644 --- a/test/primitive_parser.ts +++ b/test/primitive_parser.ts @@ -295,6 +295,20 @@ function primitiveParserTests( }); describe("String parser", () => { + it("should parse string with length 0", () => { + const parser = Parser.start() + .string("a", { length: 0 }) + .string("b", { length: 0, zeroTerminated: true }) + .string("c", { length: 0, stripNull: true }) + .uint8("x"); + + expect(parser.parse(factory([0x41]))).toStrictEqual({ + a: "", + b: "", + c: "", + x: 0x41, + }); + }); it("should parse UTF8 encoded string (ASCII only)", () => { const text = "hello, world"; const buffer = factory(new TextEncoder().encode(text)); @@ -384,6 +398,13 @@ function primitiveParserTests( }); describe("Bytes parser", () => { + it("should parse buffer with length 0", () => { + const parser = new Parser().buffer("raw", { length: 0 }).uint8("x"); + const result = parser.parse(factory([0x41])); + + expect(result.raw.length).toBe(0); + expect(result.x).toBe(0x41); + }); it("should parse as buffer", () => { const parser = new Parser().uint8("len").buffer("raw", { length: "len",