Skip to content

Commit

Permalink
Fixes encoding array readUntil function and add encodeUntil option
Browse files Browse the repository at this point in the history
  • Loading branch information
Ericbla committed Aug 3, 2018
1 parent dd75c74 commit 4443726
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 6 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,12 @@ keys:
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.
function it reads until the function returns true. **<u>Note</u>**: When encoding,
the `buffer` second parameter of `readUntil` function is the buffer already encoded
before this array. So no *read-ahead* is possible.
- `encodeUntil` - a function (item, object), only used when encoding, that replaces
the `readUntil` function when present and allow limit the number of encoded items
by returning true based on *item* values or other *object* properies.

```javascript
var parser = new Parser()
Expand Down
20 changes: 16 additions & 4 deletions lib/binary_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,6 @@ Parser.prototype.generate_encodeArray = function(ctx) {
var item = ctx.generateTmpVariable();
var itemCounter = ctx.generateTmpVariable();
var maxItems = ctx.generateTmpVariable();
var maxOffset = ctx.generateTmpVariable();
var isHash = typeof this.options.key === "string";

if (isHash) {
Expand Down Expand Up @@ -928,7 +927,10 @@ Parser.prototype.generate_encodeArray = function(ctx) {
);

ctx.pushCode("var {0} = 0;", itemCounter);
if (typeof this.options.readUntil === "function") {
if (
typeof this.options.encodeUntil === "function" ||
this.options.readUntil
) {
ctx.pushCode("do {");
} else {
ctx.pushCode("for ( ; {0} < {1}; ) {", itemCounter, maxItems);
Expand Down Expand Up @@ -958,9 +960,19 @@ Parser.prototype.generate_encodeArray = function(ctx) {

ctx.pushCode("}"); // End of 'do {' or 'for(...) {'

if (typeof this.options.readUntil === "function") {
if (typeof this.options.encodeUntil === "function") {
ctx.pushCode(
" while (!({0}).call(this, {1}, {2}.toBuffer()));",
" while ({0} < {1} && !({2}).call(this, {3}, vars));",
itemCounter,
maxItems,
this.options.encodeUntil,
item
);
} else if (typeof this.options.readUntil === "function") {
ctx.pushCode(
" while ({0} < {1} && !({2}).call(this, {3}, {4}.toBuffer()));",
itemCounter,
maxItems,
this.options.readUntil,
item,
savSmartBuffer
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "binary-parser-encoder",
"version": "1.3.2",
"version": "1.4.0",
"description": "Blazing-fast binary parser builder",
"main": "lib/binary_parser.js",
"devDependencies": {
Expand Down
147 changes: 147 additions & 0 deletions test/zz_encoder_bugs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
var assert = require("assert");
var Parser = require("../lib/binary_parser").Parser;

describe("Specific bugs testing", function() {
describe("Array encoder with readUntil", function() {
it("should limit to array length even if readUntil is never true", function() {
var parser = Parser.start()
.uint16("len")
.array("payloads", {
type: new Parser().uint8("cmd").array("params", {
type: new Parser().uint8("param"),
readUntil: function(item, buffer) {
return buffer.length == 2; // Stop when 2 bytes left in parsed buffer
}
}),
lengthInBytes: function() {
return this.len - 4;
}
})
.uint16("crc");

var buffer = Buffer.from("0008AAB1B2B3FFFF", "hex");
var decoded = parser.parse(buffer);

assert.deepEqual(decoded, {
len: 8,
payloads: [
{
cmd: 170,
params: [
{
param: 177
},
{
param: 178
},
{
param: 179
}
]
}
],
crc: 65535
});

var encoded;
// Although readUntil is never true here, the encoding will be good
assert.doesNotThrow(function() {
encoded = parser.encode(decoded);
});
assert.deepEqual(encoded, buffer);
});

it("is not the reverse of parsing when readUntil gives false information", function() {
var parser = Parser.start()
.uint16("len")
.array("payloads", {
type: new Parser().uint8("cmd").array("params", {
type: new Parser().uint8("param"),
readUntil: function(item, buffer) {
return buffer.length <= 2; // Stop when 2 bytes left in buffer
}
}),
lengthInBytes: function() {
return this.len - 4;
}
})
.uint16("crc");

var buffer = Buffer.from("0008AAB1B2B3FFFF", "hex");
var decoded = parser.parse(buffer);

assert.deepEqual(decoded, {
len: 8,
payloads: [
{
cmd: 170,
params: [
{
param: 177
},
{
param: 178
},
{
param: 179
}
]
}
],
crc: 0xffff
});

var encoded = parser.encode(decoded);
// Missing parms 178 and 179 as readUntil will be true at first run
assert.deepEqual(encoded, Buffer.from("0008AAB1FFFF", "hex"));
});

it("should ignore readUntil when encodeUntil is provided", function() {
var parser = Parser.start()
.uint16("len")
.array("payloads", {
type: new Parser().uint8("cmd").array("params", {
type: new Parser().uint8("param"),
readUntil: function(item, buffer) {
return buffer.length == 2; // Stop when 2 bytes left in buffer
},
encodeUntil: function(item, obj) {
return item.param === 178; // Stop encoding when value 178 is reached
}
}),
lengthInBytes: function() {
return this.len - 4;
}
})
.uint16("crc");

var buffer = Buffer.from("0008AAB1B2B3FFFF", "hex");
var decoded = parser.parse(buffer);

assert.deepEqual(decoded, {
len: 8,
payloads: [
{
cmd: 170,
params: [
{
param: 177
},
{
param: 178
},
{
param: 179
}
]
}
],
crc: 0xffff
});

var encoded = parser.encode(decoded);
// Missing parms 179 as encodeUntil stops at 178
assert.deepEqual(encoded, Buffer.from("0008AAB1B2FFFF", "hex"));
});
});
});

0 comments on commit 4443726

Please sign in to comment.