From 2ba75040d25448bccf53cbb9c3fdc280060a6d5e Mon Sep 17 00:00:00 2001 From: nhjm449 Date: Mon, 21 Aug 2017 21:07:08 -0500 Subject: [PATCH] Add lengthInBytes option to array() (#19) Add lengthInBytes option to array() --- README.md | 29 +++++++++++++-- lib/binary_parser.js | 5 ++- test/composite_parser.js | 79 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d2d3f345..8d080636 100644 --- a/README.md +++ b/README.md @@ -147,9 +147,11 @@ Parse bytes as an array. `options` is an object; following options are available - `type` - (Required) Type of the array element. Can be a string or an user defined Parser object. If it's a string, you have to choose from [u]int{8, 16, 32}{le, be}. -- `length` - (either `length` or `readUntil` is required) Length of the array. Can be a number, string or a function. +- `length` - (either `length`, `lengthInBytes`, or `readUntil` is required) Length of the array. Can be a number, string or a function. Use number for statically sized arrays. -- `readUntil` - (either `length` or `readUntil` is required) If `'eof'`, then this parser +- `lengthInBytes` - (either `length`, `lengthInBytes`, or `readUntil` is required) Length of the array expressed in bytes. Can be a number, string or a function. + Use number for statically sized arrays. +- `readUntil` - (either `length`, `lengthInBytes`, or `readUntil` is required) If `'eof'`, then this parser reads until the end of `Buffer` object. If function it reads until the function returns true. ```javascript @@ -173,14 +175,33 @@ var parser = new Parser() length: function() { return this.dataLength - 1; } // other fields are available through this }); - // Dynamically sized array (with stop-check on parsed item) + // Statically sized array .array('data4', { + type: 'int32', + lengthInBytes: 16 + }) + + // Dynamically sized array (reference another variable) + .uint8('dataLengthInBytes') + .array('data5', { + type: 'int32', + lengthInBytes: 'dataLengthInBytes' + }) + + // Dynamically sized array (with some calculation) + .array('data6', { + type: 'int32', + lengthInBytes: function() { return this.dataLengthInBytes - 4; } // other fields are available through this + }) + + // Dynamically sized array (with stop-check on parsed item) + .array('data7', { type: 'int32', readUntil: function(item, buffer) { return item === 42 } // stop when specific item is parsed. buffer can be used to perform a read-ahead. }); // Use user defined parser object - .array('data5', { + .array('data8', { type: userDefinedParser, length: 'dataLength' }); diff --git a/lib/binary_parser.js b/lib/binary_parser.js index 86a8ec9b..be36a20c 100644 --- a/lib/binary_parser.js +++ b/lib/binary_parser.js @@ -137,7 +137,7 @@ Parser.prototype.buffer = function(varName, options) { }; Parser.prototype.array = function(varName, options) { - if (!options.readUntil && !options.length) { + if (!options.readUntil && !options.length && !options.lengthInBytes) { throw new Error('Length option of array is not defined.'); } if (!options.type) { @@ -524,6 +524,7 @@ Parser.prototype.generateBuffer = function(ctx) { Parser.prototype.generateArray = function(ctx) { var length = ctx.generateOption(this.options.length); + var lengthInBytes = ctx.generateOption(this.options.lengthInBytes); var type = this.options.type; var counter = ctx.generateTmpVariable(); var lhs = ctx.generateVariable(this.varName); @@ -540,6 +541,8 @@ Parser.prototype.generateArray = function(ctx) { ctx.pushCode('do {'); } else if (this.options.readUntil === 'eof') { ctx.pushCode('for (var {0} = 0; offset < buffer.length; {0}++) {', counter); + } else if (lengthInBytes !== undefined) { + ctx.pushCode('for (var {0} = offset; offset - {0} < {1}; ) {', counter, lengthInBytes); } else { ctx.pushCode('for (var {0} = 0; {0} < {1}; {0}++) {', counter, length); } diff --git a/test/composite_parser.js b/test/composite_parser.js index fcf332a9..4f98cfe3 100644 --- a/test/composite_parser.js +++ b/test/composite_parser.js @@ -19,6 +19,21 @@ describe('Composite parser', function(){ message: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] }); }); + it('should parse array of primitive types with lengthInBytes', function(){ + var parser = + Parser.start() + .uint8('length') + .array('message', { + lengthInBytes: 'length', + type: 'uint8' + }); + + var buffer = new Buffer([12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + assert.deepEqual(parser.parse(buffer), { + length: 12, + message: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + }); + }); it('should parse array of user defined types', function(){ var elementParser = new Parser() .uint8('key') @@ -41,6 +56,70 @@ describe('Composite parser', function(){ ] }); }); + it('should parse array of user defined types with lengthInBytes', function(){ + var elementParser = new Parser() + .uint8('key') + .int16le('value'); + + var parser = + Parser.start() + .uint16le('length') + .array('message', { + lengthInBytes: 'length', + type: elementParser + }); + + var buffer = new Buffer([0x06, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04]); + assert.deepEqual(parser.parse(buffer), { + length: 0x06, + message: [ + {key: 0xca, value: 1234}, + {key: 0xbe, value: 1235} + ] + }); + }); + it('should parse array of user defined types with lengthInBytes literal', function(){ + var elementParser = new Parser() + .uint8('key') + .int16le('value'); + + var parser = + Parser.start() + .array('message', { + lengthInBytes: 0x06, + type: elementParser + }); + + var buffer = new Buffer([0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04]); + assert.deepEqual(parser.parse(buffer), { + message: [ + {key: 0xca, value: 1234}, + {key: 0xbe, value: 1235} + ] + }); + }); + it('should parse array of user defined types with lengthInBytes function', function(){ + var elementParser = new Parser() + .uint8('key') + .int16le('value'); + + var parser = + Parser.start() + .uint16le('length') + .array('message', { + lengthInBytes: function() { return this.length; }, + type: elementParser + }); + + var buffer = new Buffer([0x06, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04]); + assert.deepEqual(parser.parse(buffer), { + length: 0x06, + message: [ + {key: 0xca, value: 1234}, + {key: 0xbe, value: 1235} + ] + }); + }); it('should parse array of arrays', function(){ var rowParser = Parser.start()