Skip to content

Commit 3819684

Browse files
committed
Issue keichi#19 Little endianess incorrect
1 parent 43893ca commit 3819684

File tree

4 files changed

+167
-3
lines changed

4 files changed

+167
-3
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,21 @@ var parser = new Parser()
338338
.int32("c");
339339
```
340340

341+
### setEncoderOptions(opts)
342+
Set specific options for encoding.
343+
Current supported `opts` object may contain:
344+
- bitEndianess: true|false (default false) When true, tell the encoder to respect endianess BITs order, so that
345+
encoding is exactly the reverse of the parsing process for bits fields.
346+
347+
```javascript
348+
var parser = new Parser()
349+
.endianess("little")
350+
.setEncoderOpts({bitEndianess: true}) // Use BITs endianess for bits fields
351+
.bit4("a")
352+
.bit4("b")
353+
.uint16("c");
354+
```
355+
341356
### namely(alias)
342357
Set an alias to this parser, so there will be an opportunity to refer to it by
343358
name in methods like `.array`, `.nest` and `.choice`, instead of requirement

lib/binary_parser.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ var Parser = function(opts) {
7676
opts && typeof opts === "object" && opts.smartBufferSize
7777
? opts.smartBufferSize
7878
: 256;
79+
this.encoderOpts = {
80+
bitEndianess: false
81+
};
7982
};
8083

8184
//----------------------------------------------------------------------------------------
@@ -247,6 +250,11 @@ Parser.prototype.endianess = function(endianess) {
247250
return this;
248251
};
249252

253+
Parser.prototype.encoderSetOptions = function(opts) {
254+
Object.assign(this.encoderOpts, opts);
255+
return this;
256+
};
257+
250258
Parser.prototype.create = function(constructorFn) {
251259
if (!(constructorFn instanceof Function)) {
252260
throw new Error("Constructor must be a Function object.");
@@ -466,6 +474,7 @@ Parser.prototype.setNextParser = function(type, varName, options) {
466474
parser.varName = varName;
467475
parser.options = options || parser.options;
468476
parser.endian = this.endian;
477+
parser.encoderOpts = this.encoderOpts;
469478

470479
if (this.head) {
471480
this.head.next = parser;
@@ -668,15 +677,25 @@ Parser.prototype.generate_encodeBit = function(ctx) {
668677
);
669678
}
670679

680+
var isBitLittleEndian =
681+
this.endian === "le" && this.encoderOpts.bitEndianess;
671682
var tmpVal = ctx.generateTmpVariable();
683+
var boundVal = ctx.generateTmpVariable();
672684
ctx.pushCode("var {0} = 0;", tmpVal);
685+
ctx.pushCode("var {0} = 0;", boundVal);
673686
var bitOffset = 0;
674687
ctx.bitFields.forEach(function(parser) {
688+
ctx.pushCode(
689+
"{0} = ({1} & {2});",
690+
boundVal,
691+
parser.varName,
692+
(1 << parser.options.length) - 1
693+
);
675694
ctx.pushCode(
676695
"{0} |= ({1} << {2});",
677696
tmpVal,
678-
parser.varName,
679-
sum - parser.options.length - bitOffset
697+
boundVal,
698+
isBitLittleEndian ? bitOffset : sum - bitOffset - parser.options.length
680699
);
681700
ctx.pushCode("{0} = {0} >>> 0;", tmpVal);
682701
bitOffset += parser.options.length;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "binary-parser-encoder",
3-
"version": "1.4.4",
3+
"version": "1.4.5",
44
"description": "Blazing-fast binary parser builder",
55
"main": "lib/binary_parser.js",
66
"devDependencies": {

test/zz_encoder_bugs.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,134 @@ describe("Specific bugs testing", function() {
219219
});
220220
});
221221
});
222+
223+
describe("Issue #19 Little endianess incorrect", function() {
224+
let binaryLiteral = function(s) {
225+
var i;
226+
var bytes = [];
227+
228+
s = s.replace(/\s/g, "");
229+
for (i = 0; i < s.length; i += 8) {
230+
bytes.push(parseInt(s.slice(i, i + 8), 2));
231+
}
232+
233+
return Buffer.from(bytes);
234+
};
235+
it("should parse 4-byte-length bit field sequence wit little endian", function() {
236+
let buf = binaryLiteral("0000000000001111 1010000110100010"); // 000F A1A2
237+
238+
// Parsed as two uint16 with little-endian (BYTES order)
239+
let parser1 = new Parser().uint16le("a").uint16le("b");
240+
241+
// Parsed as two 16 bits fields with little-endian
242+
let parser2 = new Parser()
243+
.endianess("little")
244+
.bit16("a")
245+
.bit16("b");
246+
247+
let parsed1 = parser1.parse(buf);
248+
let parsed2 = parser2.parse(buf);
249+
250+
assert.deepEqual(parsed1, {
251+
a: 0x0f00, // 000F
252+
b: 0xa2a1 // A1A2
253+
});
254+
255+
assert.deepEqual(parsed2, {
256+
a: 0xa1a2, // last 16 bits (but value coded as BE)
257+
b: 0x000f // first 16 bits (but value coded as BE)
258+
});
259+
260+
/* This is a little confusing. The endianess with bits fields affect the order of fields */
261+
});
262+
it("should encode bit ranges with little endian correctly", function() {
263+
let bigParser = Parser.start()
264+
.endianess("big")
265+
.bit4("a")
266+
.bit1("b")
267+
.bit1("c")
268+
.bit1("d")
269+
.bit1("e")
270+
.uint16("f")
271+
.array("g", { type: "uint8", readUntil: "eof" });
272+
let littleParser = Parser.start()
273+
.endianess("little")
274+
.bit4("a")
275+
.bit1("b")
276+
.bit1("c")
277+
.bit1("d")
278+
.bit1("e")
279+
.uint16("f")
280+
.array("g", { type: "uint8", readUntil: "eof" });
281+
// Parser definition for a symetric encoding/decoding of little-endian bit fields
282+
let little2Parser = Parser.start()
283+
.endianess("little")
284+
.encoderSetOptions({ bitEndianess: true })
285+
.bit4("a")
286+
.bit1("b")
287+
.bit1("c")
288+
.bit1("d")
289+
.bit1("e")
290+
.uint16("f")
291+
.array("g", { type: "uint8", readUntil: "eof" });
292+
293+
let data = binaryLiteral(
294+
"0011 0 1 0 1 0000000011111111 00000001 00000010 00000011"
295+
); // 35 00FF 01 02 03
296+
// in big endian: 3 0 1 0 1 00FF 1 2 3
297+
// in little endian: 3 0 1 0 1 FF00 1 2 3
298+
// LE with encoderBitEndianess option:
299+
// 5 1 1 0 0 FF00 1 2 3
300+
301+
//let bigDecoded = bigParser.parse(data);
302+
//let littleDecoded = littleParser.parse(data);
303+
let little2Decoded = little2Parser.parse(data);
304+
305+
//console.log(bigDecoded);
306+
//console.log(littleDecoded);
307+
//console.log(little2Decoded);
308+
309+
let big = {
310+
a: 3,
311+
b: 0,
312+
c: 1,
313+
d: 0,
314+
e: 1,
315+
f: 0x00ff,
316+
g: [1, 2, 3]
317+
};
318+
let little = {
319+
a: 3,
320+
b: 0,
321+
c: 1,
322+
d: 0,
323+
e: 1,
324+
f: 0xff00,
325+
g: [1, 2, 3]
326+
};
327+
let little2 = {
328+
a: 5,
329+
b: 1,
330+
c: 1,
331+
d: 0,
332+
e: 0,
333+
f: 0xff00,
334+
g: [1, 2, 3]
335+
};
336+
337+
assert.deepEqual(little2Decoded, little2);
338+
339+
let bigEncoded = bigParser.encode(big);
340+
let littleEncoded = littleParser.encode(little);
341+
let little2Encoded = little2Parser.encode(little2);
342+
343+
//console.log(bigEncoded);
344+
//console.log(littleEncoded);
345+
//console.log(little2Encoded);
346+
347+
assert.deepEqual(bigEncoded, data);
348+
assert.deepEqual(littleEncoded, data);
349+
assert.deepEqual(little2Encoded, data);
350+
});
351+
});
222352
});

0 commit comments

Comments
 (0)