Skip to content

Commit f571a59

Browse files
committed
Fixes #20 String padded with space (0x20) instead of null (0x0) when reencoding
1 parent e34dc64 commit f571a59

File tree

6 files changed

+100
-46
lines changed

6 files changed

+100
-46
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,25 +166,26 @@ the following keys:
166166
Supported encodings include `"utf8"`, `"ascii"` and `"hex"`. See
167167
[`Buffer.toString`](http://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end)
168168
for more info.
169-
- `length ` - (Optional) (Bytes)Length of the string. Can be a number, string or a
169+
- `length` - (Optional) (Bytes)Length of the string. Can be a number, string or a
170170
function. Use number for statically sized arrays, string to reference
171171
another variable and function to do some calculation.
172-
Note: when encoding the string is padded with spaces (0x20) at end to fit the length requirement.
172+
Note: When encoding the string is padded with a `padd` charecter to fit the length requirement.
173173
- `zeroTerminated` - (Optional, defaults to `false`) If true, then this parser
174-
reads until it reaches zero.
174+
reads until it reaches zero (or the specified `length`). When encoding, a *null* character is inserted at end of
175+
the string (if the optional `length` allows it).
175176
- `greedy` - (Optional, defaults to `false`) If true, then this parser reads
176177
until it reaches the end of the buffer. Will consume zero-bytes. (Note: has
177178
no effect on encoding function)
178179
- `stripNull` - (Optional, must be used with `length`) If true, then strip
179-
null characters from end of the string. (Note: has no effect on encoding, but
180-
when used, then the parse() and encode() functions are not the exact opposite)
180+
null characters from end of the string. (Note: When encoding, this will also set the **default** `padd` character
181+
to null instead of space)
181182
- `trim` - (Optional, default to `false`) If true, then trim() (remove leading and trailing spaces)
182183
the parsed string.
183184
- `padding` - (Optional, Only used for encoding, default to `right`) If `left` then the string
184185
will be right aligned (padding left with `padd` char or space) depending of the `length` option
185186
- `padd` - (Optional, Only used for encoding with `length` specified) A string from which first character (1 Byte)
186187
is used as a padding char if necessary (provided string length is less than `length` option). Note: Only 'ascii'
187-
or utf8 < 0x80 are alowed (fallback to 'space' padding else).
188+
or utf8 < 0x80 are alowed. Note: The default padd character is *space* (or *null* when `stripNull` is used).
188189

189190
### buffer(name[, options])
190191
Parse bytes as a buffer. `name` should consist only of alpha numeric

lib/binary_parser.ts

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,8 +1156,11 @@ export class Parser {
11561156
ctx.pushCode(
11571157
`while(buffer.readUInt8(offset++) !== 0 && offset - ${start} < ${len});`
11581158
);
1159+
//ctx.pushCode(
1160+
// `${name} = buffer.toString('${encoding}', ${start}, offset - ${start} < ${len} ? offset - 1 : offset);`
1161+
//);
11591162
ctx.pushCode(
1160-
`${name} = buffer.toString('${encoding}', ${start}, offset - ${start} < ${len} ? offset - 1 : offset);`
1163+
`${name} = buffer.toString('${encoding}', ${start}, buffer.readUInt8(offset -1) == 0 ? offset - 1 : offset);`
11611164
);
11621165
} else if (this.options.length) {
11631166
const len = ctx.generateOption(this.options.length);
@@ -1202,36 +1205,41 @@ export class Parser {
12021205
// Compute padding length
12031206
const padLen = ctx.generateTmpVariable();
12041207
ctx.pushCode(`${padLen} = ${optLength} - ${tmpBuf}.length;`);
1205-
const padCharVar = ctx.generateTmpVariable();
1206-
let padChar = ' ';
1207-
if (this.options.padd && typeof this.options.padd === 'string') {
1208-
const code = this.options.padd.charCodeAt(0);
1209-
if (code < 0x80) {
1210-
padChar = String.fromCharCode(code);
1208+
if (this.options.zeroTerminated) {
1209+
ctx.pushCode(`smartBuffer.writeBuffer(${tmpBuf});`);
1210+
ctx.pushCode(`if (${padLen} > 0) { smartBuffer.writeUInt8(0x00); }`);
1211+
} else {
1212+
const padCharVar = ctx.generateTmpVariable();
1213+
let padChar = this.options.stripNull ? '\u0000' : ' ';
1214+
if (this.options.padd && typeof this.options.padd === 'string') {
1215+
const code = this.options.padd.charCodeAt(0);
1216+
if (code < 0x80) {
1217+
padChar = String.fromCharCode(code);
1218+
}
1219+
}
1220+
ctx.pushCode(`${padCharVar} = "${padChar}";`);
1221+
if (this.options.padding === 'left') {
1222+
// Add heading padding spaces
1223+
ctx.pushCode(
1224+
`if (${padLen} > 0) {smartBuffer.writeString(${padCharVar}.repeat(${padLen}));}`
1225+
);
1226+
}
1227+
// Copy the temporary string buffer to current smartBuffer
1228+
ctx.pushCode(`smartBuffer.writeBuffer(${tmpBuf});`);
1229+
if (this.options.padding !== 'left') {
1230+
// Add trailing padding spaces
1231+
ctx.pushCode(
1232+
`if (${padLen} > 0) {smartBuffer.writeString(${padCharVar}.repeat(${padLen}));}`
1233+
);
12111234
}
1212-
}
1213-
ctx.pushCode(`${padCharVar} = "${padChar}";`);
1214-
if (this.options.padding === 'left') {
1215-
// Add heading padding spaces
1216-
ctx.pushCode(
1217-
`if (${padLen} > 0) {smartBuffer.writeString(${padCharVar}.repeat(${padLen}));}`
1218-
);
1219-
}
1220-
// Copy the temporary string buffer to current smartBuffer
1221-
ctx.pushCode(`smartBuffer.writeBuffer(${tmpBuf});`);
1222-
if (this.options.padding !== 'left') {
1223-
// Add trailing padding spaces
1224-
ctx.pushCode(
1225-
`if (${padLen} > 0) {smartBuffer.writeString(${padCharVar}.repeat(${padLen}));}`
1226-
);
12271235
}
12281236
} else {
12291237
ctx.pushCode(
12301238
`smartBuffer.writeString(${name}, "${this.options.encoding}");`
12311239
);
1232-
}
1233-
if (this.options.zeroTerminated) {
1234-
ctx.pushCode('smartBuffer.writeUInt8(0x00);');
1240+
if (this.options.zeroTerminated) {
1241+
ctx.pushCode('smartBuffer.writeUInt8(0x00);');
1242+
}
12351243
}
12361244
}
12371245

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.5.1",
3+
"version": "1.5.2",
44
"description": "Blazing-fast binary parser builder",
55
"main": "dist/binary_parser.js",
66
"types": "dist/binary_parser.d.ts",

test/primitive_parser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,14 @@ describe('Primitive parser', function() {
364364
assert.deepEqual(parser.parse(buffer), { msg: 'hello, world' });
365365
});
366366
it('should parser zero terminated fixed-length string', function() {
367-
var buffer = Buffer.from('abc\u0000defghij\u0000');
367+
var buffer = Buffer.from('abcd\u0000defghij\u0000');
368368
var parser = Parser.start()
369369
.string('a', { length: 5, zeroTerminated: true })
370370
.string('b', { length: 5, zeroTerminated: true })
371371
.string('c', { length: 5, zeroTerminated: true });
372372

373373
assert.deepEqual(parser.parse(buffer), {
374-
a: 'abc',
374+
a: 'abcd',
375375
b: 'defgh',
376376
c: 'ij',
377377
});

test/yy_primitive_encoder.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,6 @@ describe('Primitive encoder', function() {
412412
assert.deepEqual(encoded, buffer);
413413
});
414414
it('should encode zero terminated fixed-length string', function() {
415-
// In that case parsing and encoding are not the exact oposite
416415
var buffer = Buffer.from('abc\u0000defghij\u0000');
417416
var parser = Parser.start()
418417
.string('a', { length: 5, zeroTerminated: true })
@@ -425,16 +424,15 @@ describe('Primitive encoder', function() {
425424
b: 'defgh',
426425
c: 'ij',
427426
});
427+
let encoded = parser.encode(decoded);
428+
assert.deepEqual(encoded, buffer);
428429

429-
var encoded = parser.encode({
430-
a: 'abc',
431-
b: 'defghzzzzzzz',
432-
c: 'ij',
430+
encoded = parser.encode({
431+
a: 'a234',
432+
b: 'b2345',
433+
c: 'c2345678',
433434
});
434-
assert.deepEqual(
435-
encoded,
436-
Buffer.from('abc \u0000defgh\u0000ij \u0000')
437-
);
435+
assert.deepEqual(encoded, Buffer.from('a234\u0000b2345c2345'));
438436
});
439437
it('should strip trailing null characters', function() {
440438
var buffer = Buffer.from('746573740000', 'hex');
@@ -454,10 +452,8 @@ describe('Primitive encoder', function() {
454452

455453
var decoded2 = parser2.parse(buffer);
456454
assert.equal(decoded2.str, 'test');
457-
// In this case (stripNull = true) parsing and encoding are not the exact oposite
458455
var encoded2 = parser2.encode(decoded2);
459-
assert.notDeepEqual(encoded2, buffer);
460-
assert.deepEqual(encoded2, Buffer.from('test '));
456+
assert.deepEqual(encoded2, buffer);
461457
});
462458
it('should encode string with zero-bytes internally', function() {
463459
var buffer = Buffer.from('abc\u0000defghij\u0000');

test/zz_encoder_bugs.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,53 @@ describe('Specific bugs testing', function() {
349349
assert.deepEqual(little2Encoded, data);
350350
});
351351
});
352+
353+
describe('Issue #20 Encoding fixed length null terminated or strip null strings', function() {
354+
it('should encode zero terminated fixed-length string', function() {
355+
// In that case parsing and encoding are not the exact oposite
356+
let buffer = Buffer.from(
357+
'\u0000A\u0000AB\u0000ABC\u0000ABCD\u0000ABCDE\u0000'
358+
);
359+
let parser = Parser.start()
360+
.string('a', { length: 4, zeroTerminated: true })
361+
.string('b', { length: 4, zeroTerminated: true })
362+
.string('c', { length: 4, zeroTerminated: true })
363+
.string('d', { length: 4, zeroTerminated: true })
364+
.string('e', { length: 4, zeroTerminated: true })
365+
.string('f', { length: 4, zeroTerminated: true })
366+
.string('g', { length: 4, zeroTerminated: true })
367+
.string('h', { length: 4, zeroTerminated: true });
368+
369+
let decoded = parser.parse(buffer);
370+
assert.deepEqual(decoded, {
371+
a: '',
372+
b: 'A',
373+
c: 'AB',
374+
d: 'ABC',
375+
e: 'ABCD',
376+
f: '',
377+
g: 'ABCD',
378+
h: 'E',
379+
});
380+
381+
let encoded = parser.encode(decoded);
382+
assert.deepEqual(encoded, buffer);
383+
});
384+
385+
it('should encode fixed-length string with stripNull', function() {
386+
let parser = Parser.start()
387+
.string('a', { length: 8, zeroTerminated: false, stripNull: true })
388+
.string('b', { length: 8, zeroTerminated: false, stripNull: true })
389+
.string('z', { length: 2, zeroTerminated: false, stripNull: true });
390+
let buffer = Buffer.from('ABCD\u0000\u0000\u0000\u000012345678ZZ');
391+
let decoded = parser.parse(buffer);
392+
assert.deepEqual(decoded, {
393+
a: 'ABCD',
394+
b: '12345678',
395+
z: 'ZZ',
396+
});
397+
let encoded = parser.encode(decoded);
398+
assert.deepEqual(encoded, buffer);
399+
});
400+
});
352401
});

0 commit comments

Comments
 (0)