diff --git a/README.md b/README.md index d902a472..1131c747 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,29 @@ executed for the first time. Dynamically generates the code for this parser and returns it as a string. Usually used for debugging. +### Internal utilities + +### sizeOf() +Get the *static* size of the parser definition. if some parser items are of type +"string", "array", or "buffer", then the size can only be determined when using *number* +as length option. If not it will return *NaN* + +### getLastOffset() +Get the number of parsed bytes. This function can only be called after parsing some buffer. + +```javascript +var parser = Parser.start() + .uint8("b1") + .uint8("b2"); + +var buffer = Buffer.from([1, 2, 3, 4, 5]); + +parser.parse(buffer); // { b1: 1, b2: 2 } +parser.getLastOffset(); // 2 +parser.parse(buffer.slice(parser.getLastOffset())); // { b1: 3, b2: 4 } +``` + + ### Common options These are common options that can be specified in all parsers. diff --git a/lib/binary_parser.js b/lib/binary_parser.js index 12ece7ee..d6a81a45 100644 --- a/lib/binary_parser.js +++ b/lib/binary_parser.js @@ -69,6 +69,7 @@ var Parser = function() { this.endian = "be"; this.constructorFn = null; this.alias = null; + this.lastOffset = 0; }; //---------------------------------------------------------------------------------------- @@ -250,6 +251,10 @@ Parser.prototype.create = function(constructorFn) { return this; }; +Parser.prototype.getLastOffset = function() { + return this.lastOffset; +}; + Parser.prototype.getCode = function() { var ctx = new Context(); @@ -264,9 +269,11 @@ Parser.prototype.getCode = function() { } if (this.alias) { - ctx.pushCode("return {0}(0).result;", FUNCTION_PREFIX + this.alias); + var tempVar = ctx.generateTmpVariable(); + ctx.pushCode("var {0} = {1}(0);", tempVar, FUNCTION_PREFIX + this.alias); + ctx.pushCode("return { offset: {0}.offset, vars:{0}.vars };", tempVar); } else { - ctx.pushCode("return vars;"); + ctx.pushCode("return { offset: offset, vars:vars };"); } return ctx.code; @@ -285,7 +292,7 @@ Parser.prototype.addRawCode = function(ctx) { this.resolveReferences(ctx); - ctx.pushCode("return vars;"); + ctx.pushCode("return { offset: offset, vars:vars };"); }; Parser.prototype.addAliasedCode = function(ctx) { @@ -302,7 +309,7 @@ Parser.prototype.addAliasedCode = function(ctx) { ctx.markResolved(this.alias); this.resolveReferences(ctx); - ctx.pushCode("return { offset: offset, result: vars };"); + ctx.pushCode("return { offset: offset, vars: vars };"); ctx.pushCode("}"); return ctx; @@ -376,7 +383,9 @@ Parser.prototype.parse = function(buffer) { this.compile(); } - return this.compiled(buffer, this.constructorFn); + var result = this.compiled(buffer, this.constructorFn); + this.lastOffset = result.offset; + return result.vars; }; //---------------------------------------------------------------------------------------- @@ -584,6 +593,7 @@ Parser.prototype.generateBuffer = function(ctx) { "{0} = buffer.slice(offset);", ctx.generateVariable(this.varName) ); + ctx.pushCode("offset = buffer.length;"); } else { ctx.pushCode( "{0} = buffer.slice(offset, offset + {1});", @@ -634,7 +644,7 @@ Parser.prototype.generateArray = function(ctx) { } else { var tempVar = ctx.generateTmpVariable(); ctx.pushCode("var {0} = {1}(offset);", tempVar, FUNCTION_PREFIX + type); - ctx.pushCode("var {0} = {1}.result; offset = {1}.offset;", item, tempVar); + ctx.pushCode("var {0} = {1}.vars; offset = {1}.offset;", item, tempVar); if (type !== this.alias) ctx.addReference(type); } } else if (type instanceof Parser) { @@ -675,7 +685,7 @@ Parser.prototype.generateChoiceCase = function(ctx, varName, type) { var tempVar = ctx.generateTmpVariable(); ctx.pushCode("var {0} = {1}(offset);", tempVar, FUNCTION_PREFIX + type); ctx.pushCode( - "{0} = {1}.result; offset = {1}.offset;", + "{0} = {1}.vars; offset = {1}.offset;", ctx.generateVariable(this.varName), tempVar ); @@ -727,7 +737,7 @@ Parser.prototype.generateNest = function(ctx) { tempVar, FUNCTION_PREFIX + this.options.type ); - ctx.pushCode("{0} = {1}.result; offset = {1}.offset;", nestVar, tempVar); + ctx.pushCode("{0} = {1}.vars; offset = {1}.offset;", nestVar, tempVar); if (this.options.type !== this.alias) ctx.addReference(this.options.type); } }; diff --git a/test/composite_parser.js b/test/composite_parser.js index 994b4a23..03cc0f69 100644 --- a/test/composite_parser.js +++ b/test/composite_parser.js @@ -958,6 +958,104 @@ describe("Composite parser", function() { assert.equal(parser.sizeOf(), 1 + 4 + 10 + 2 + 3 + 8); }); + it("should get parsed offset", function() { + var parser = Parser.start() + .uint8("b1") + .uint8("b2"); + assert.equal(parser.sizeOf(), 1 + 1); + assert.equal(parser.getLastOffset(), 0); // Not yet parsed + + var buffer = Buffer.from([1, 2, 3, 4, 5]); // More data than parser can consume + + assert.deepEqual(parser.parse(buffer), { b1: 1, b2: 2 }); // Parsed 2 first bytes + assert.equal(parser.getLastOffset(), parser.sizeOf()); // Unparsed data offset + assert.deepEqual(parser.parse(buffer.slice(parser.getLastOffset())), { + b1: 3, + b2: 4 + }); // Continue parsing + assert.equal(parser.getLastOffset(), parser.sizeOf()); // Unparsed data offset + }); + it("should get parsed offset for 'static' sized elements", function() { + var parser = Parser.start() + .int8("a") + .int32le("b") + .string("msg", { length: 4 }) + .skip(2) + .array("data", { + length: 3, + type: "uint8" + }) + .buffer("raw", { length: 4 }); + assert.equal(parser.sizeOf(), 1 + 4 + 4 + 2 + 3 + 4); + + var buffer = Buffer.from([ + 0x01, + 0x02, + 0x00, + 0x00, + 0x00, + 0x41, + 0x42, + 0x43, + 0x44, + 0xff, + 0xff, + 0xa0, + 0xa1, + 0xa2, + 0xb0, + 0xb1, + 0xb2, + 0xb3 + ]); + assert.deepEqual(parser.parse(buffer), { + a: 1, + b: 2, + msg: "ABCD", + data: [0xa0, 0xa1, 0xa2], + raw: Buffer.from([0xb0, 0xb1, 0xb2, 0xb3]) + }); + assert.equal(parser.getLastOffset(), parser.sizeOf()); + }); + it("should get parsed offset for eof buffer", function() { + var bufferParser = Parser.start() + .int8("a") + .buffer("buf", { + readUntil: "eof" + }); + assert.equal(isNaN(bufferParser.sizeOf()), true); + var buffer = Buffer.from("\0John\0Doe\0"); + assert.deepEqual(bufferParser.parse(buffer), { + a: 0x00, + buf: Buffer.from("John\0Doe\0") + }); + assert.equal(bufferParser.getLastOffset(), buffer.length); + }); + it("should get parsed offset for eof array", function() { + var bufferParser = Parser.start() + .int8("a") + .array("arr", { + type: "uint8", + readUntil: "eof" + }); + assert.equal(isNaN(bufferParser.sizeOf()), true); + + var buffer = Buffer.from([ + 0x01, + 0xa0, + 0xa1, + 0xa2, + 0xb0, + 0xb1, + 0xb2, + 0xb3 + ]); + assert.deepEqual(bufferParser.parse(buffer), { + a: 0x01, + arr: [0xa0, 0xa1, 0xa2, 0xb0, 0xb1, 0xb2, 0xb3] + }); + assert.equal(bufferParser.getLastOffset(), buffer.length); + }); it("should assert parsed values", function() { var parser = Parser.start().string("msg", { encoding: "ascii",