Skip to content

Commit

Permalink
Merge pull request keichi#9 from ddaf/master
Browse files Browse the repository at this point in the history
Added formatter to array & nest and a readUntil function
  • Loading branch information
keichi committed Jan 7, 2015
2 parents e9858f2 + a513511 commit 5d9f849
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 7 deletions.
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ Parse bytes as an array. `options` is an object; following options are available
- `length` - (either `length` 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
will read till it reaches end of the `Buffer` object.
reads until the end of `Buffer` object. If function it reads until the function returns true.
- `formatter` - (Optional) Function that transforms the parsed array into a more desired form.

```javascript
var parser = new Parser()
Expand All @@ -176,12 +177,25 @@ var parser = new Parser()
type: 'int32',
length: function() { return this.dataLength - 1; } // other fields are available through this
});

// Dynamically sized array (with stop-check on parsed item)
.array('data4', {
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('data4', {
.array('data5', {
type: userDefinedParser,
length: 'dataLength'
})
});

// Use formatter to transform parsed array
.array('ipv4', {
type: uint8,
length: '4',
formatter: function(arr) { return arr.join('.'); }
});
```

### choice(name [,options])
Expand Down Expand Up @@ -217,6 +231,7 @@ Nest a parser in this position. Parse result of the nested parser is stored in t
`name`.

- `type` - (Required) A `Parser` object.
- `formatter` - (Optional) Function that transforms the parsed nested type into a more desired form.

### skip(length)
Skip parsing for `length` bytes.
Expand Down
24 changes: 22 additions & 2 deletions lib/binary_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,9 @@ Parser.prototype.generateArray = function(ctx) {
} else {
ctx.pushCode('{0} = [];', lhs);
}
if (this.options.readUntil === 'eof') {
if (typeof this.options.readUntil === 'function') {
ctx.pushCode('do {');
} else if (this.options.readUntil === 'eof') {
ctx.pushCode('for (var {0} = 0; offset < buffer.length; {0}++) {', counter);
} else {
ctx.pushCode('for (var {0} = 0; {0} < {1}; {0}++) {', counter, length);
Expand All @@ -493,6 +495,13 @@ Parser.prototype.generateArray = function(ctx) {
}

ctx.pushCode('}');

if (typeof this.options.readUntil === 'function') {
ctx.pushCode(' while (!({0}).call(this, {1}, buffer.slice(offset)));', this.options.readUntil, item);
}
if (this.options.formatter) {
this.generateFormatter(ctx, lhs, this.options.formatter);
}
};

Parser.prototype.generateChoiceCase = function(ctx, varName, type) {
Expand Down Expand Up @@ -528,12 +537,23 @@ Parser.prototype.generateChoice = function(ctx) {
};

Parser.prototype.generateNest = function(ctx) {
ctx.pushCode('{0} = {};', ctx.generateVariable(this.varName));
var nestVar = ctx.generateVariable(this.varName);
ctx.pushCode('{0} = {};', nestVar);
ctx.pushPath(this.varName);
this.options.type.generate(ctx);
ctx.popPath();

if (this.options.formatter) {
this.generateFormatter(ctx, nestVar, this.options.formatter);
}
};

Parser.prototype.generateFormatter = function(ctx, varName, formatter) {
if (typeof formatter === 'function') {
ctx.pushCode('{0} = ({1}).call(this, {0});', varName, formatter);
}
}

Parser.prototype.isInteger = function() {
return !!this.type.match(/U?Int[8|16|32][BE|LE]?|Bit\d+/);
};
Expand Down
64 changes: 62 additions & 2 deletions test/composite_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('Composite parser', function(){
]
});
});
it('should parse until eof when readUnitl is specified', function(){
it('should parse until eof when readUntil is specified', function(){
var parser =
Parser.start()
.array('data', {
Expand All @@ -102,6 +102,32 @@ describe('Composite parser', function(){
data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
});
});
it('should parse until function returns true when readUntil is function', function(){
var parser =
Parser.start()
.array('data', {
readUntil: function (item, buf) { return item === 0 },
type: 'uint8'
});

var buffer = new Buffer([0xff, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff]);
assert.deepEqual(parser.parse(buffer), {
data: [0xff, 0xff, 0xff, 0x01, 0x00]
});
});
it('should parse until function returns true when readUntil is function (using read-ahead)', function(){
var parser =
Parser.start()
.array('data', {
readUntil: function (item, buf) { return buf.length > 0 && buf.readUInt8(0) === 0 },
type: 'uint8'
});

var buffer = new Buffer([0xff, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff]);
assert.deepEqual(parser.parse(buffer), {
data: [0xff, 0xff, 0xff, 0x01]
});
});
it('should parse associative arrays', function(){
var parser =
Parser.start()
Expand Down Expand Up @@ -136,7 +162,21 @@ describe('Composite parser', function(){
}
}
});
});
});
it('should use formatter to transform parsed array', function(){
var parser =
Parser.start()
.array('data', {
type: 'uint8',
length: 4,
formatter: function(arr) { return arr.join('.'); }
});

var buffer = new Buffer([0x0a, 0x0a, 0x01, 0x6e]);
assert.deepEqual(parser.parse(buffer), {
data: '10.10.1.110'
});
});
});

describe('Choice parser', function() {
Expand Down Expand Up @@ -252,6 +292,26 @@ describe('Composite parser', function(){
}
});
});

it('should format parsed nested parser', function() {
var nameParser = new Parser()
.string('firstName', {
zeroTerminated: true
})
.string('lastName', {
zeroTerminated: true
});
var personParser = new Parser()
.nest('name', {
type: nameParser,
formatter: function(name) { return name.firstName + ' ' + name.lastName }
})

var buffer = new Buffer('John\0Doe\0');
assert.deepEqual(personParser.parse(buffer), {
name: 'John Doe'
});
});
});

describe('Constructors', function() {
Expand Down

0 comments on commit 5d9f849

Please sign in to comment.