Skip to content

Commit 775553b

Browse files
committed
Replaces static Buffer with smart-buffer for internal encoding
1 parent f756d09 commit 775553b

File tree

6 files changed

+107
-134
lines changed

6 files changed

+107
-134
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ console.log(ipHeader.encode(anIpHeader).toString("hex"));
9595
Constructs a Parser object. Returned object represents a parser which parses
9696
nothing. `options` is an optional object to pass options to this declarative
9797
parser.
98-
- `bufferSize` The size of the encoding buffer (when encoding is used) (default is 256 bytes).
98+
- `smartBufferSize` The chunk size of the encoding (smart)buffer (when encoding is used) (default is 256 bytes).
9999

100100
### parse(buffer)
101101
Parse a `Buffer` object `buffer` with this parser and return the resulting
@@ -155,9 +155,10 @@ the following keys:
155155
`"utf8"`, `"ascii"`, `"hex"` and else are valid. See
156156
[`Buffer.toString`](http://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end)
157157
for more info.
158-
- `length ` - (Optional) Length of the string. Can be a number, string or a
158+
- `length ` - (Optional) (Bytes)Length of the string. Can be a number, string or a
159159
function. Use number for statically sized arrays, string to reference
160160
another variable and function to do some calculation.
161+
Note: when encoding the string is padded with spaces (0x20) at end to fit the length requirement.
161162
- `zeroTerminated` - (Optional, defaults to `false`) If true, then this parser
162163
reads until it reaches zero.
163164
- `greedy` - (Optional, defaults to `false`) If true, then this parser reads

lib/binary_parser.js

Lines changed: 87 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Globals
33
//========================================================================================
44
var vm = require("vm");
5+
const SmartBuffer = require("smart-buffer").SmartBuffer;
6+
global.SmartBuffer = SmartBuffer;
57

68
var Context = require("./context").Context;
79

@@ -70,8 +72,10 @@ var Parser = function(opts) {
7072
this.endian = "be";
7173
this.constructorFn = null;
7274
this.alias = null;
73-
this.bufferSize =
74-
opts && typeof opts === "object" && opts.bufferSize ? opts.bufferSize : 256;
75+
this.smartBufferSize =
76+
opts && typeof opts === "object" && opts.smartBufferSize
77+
? opts.smartBufferSize
78+
: 256;
7579
};
7680

7781
//----------------------------------------------------------------------------------------
@@ -310,14 +314,16 @@ Parser.prototype.addRawCode = function(ctx) {
310314

311315
Parser.prototype.addRawCodeEncode = function(ctx) {
312316
ctx.pushCode("var vars = obj;");
313-
ctx.pushCode("var offset = 0, maxOffset = {0};", this.bufferSize);
314-
ctx.pushCode("var buffer = Buffer.alloc(maxOffset);"); //TODO See how to dynamically alloc encode buffer
317+
ctx.pushCode(
318+
"var smartBuffer = SmartBuffer.fromOptions({size: {0}, encoding: 'utf8'});",
319+
this.smartBufferSize
320+
);
315321

316322
this.generateEncode(ctx);
317323

318324
this.resolveReferences(ctx, "encode");
319325

320-
ctx.pushCode("return buffer.slice(0, offset);");
326+
ctx.pushCode("return smartBuffer.toBuffer();");
321327
};
322328

323329
Parser.prototype.addAliasedCode = function(ctx) {
@@ -344,15 +350,17 @@ Parser.prototype.addAliasedCodeEncode = function(ctx) {
344350
ctx.pushCode("function {0}(obj) {", FUNCTION_ENCODE_PREFIX + this.alias);
345351

346352
ctx.pushCode("var vars = obj;");
347-
ctx.pushCode("var offset = 0, maxOffset = {0};", this.bufferSize);
348-
ctx.pushCode("var buffer = Buffer.alloc(maxOffset);"); //TODO See how to dynamically alloc encode buffer
353+
ctx.pushCode(
354+
"var smartBuffer = SmartBuffer.fromOptions({size: {0}, encoding: 'utf8'});",
355+
this.smartBufferSize
356+
);
349357

350358
this.generateEncode(ctx);
351359

352360
ctx.markResolved(this.alias);
353361
this.resolveReferences(ctx, "encode");
354362

355-
ctx.pushCode("return buffer.slice(0, offset);");
363+
ctx.pushCode("return smartBuffer.toBuffer();");
356364
ctx.pushCode("}");
357365

358366
return ctx;
@@ -377,7 +385,7 @@ Parser.prototype.compile = function() {
377385
};
378386

379387
Parser.prototype.compileEncode = function() {
380-
var src = "(function(obj, buffer) { " + this.getCodeEncode() + " })";
388+
var src = "(function(obj) { " + this.getCodeEncode() + " })";
381389
this.compiledEncode = vm.runInThisContext(src);
382390
};
383391

@@ -439,12 +447,12 @@ Parser.prototype.parse = function(buffer) {
439447
};
440448

441449
// Follow the parser chain till the root and start encoding from there
442-
Parser.prototype.encode = function(obj, buffer) {
450+
Parser.prototype.encode = function(obj) {
443451
if (!this.compiledEncode) {
444452
this.compileEncode();
445453
}
446454

447-
return this.compiledEncode(obj, buffer);
455+
return this.compiledEncode(obj);
448456
};
449457

450458
//----------------------------------------------------------------------------------------
@@ -557,8 +565,7 @@ Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
557565
};
558566
Parser.prototype["generate_encode" + type] = function(ctx) {
559567
ctx.pushCode(
560-
"if (offset + {0} <= maxOffset) offset = buffer.write{1}({2}, offset);",
561-
PRIMITIVE_TYPES[type],
568+
"smartBuffer.write{0}({1});",
562569
type,
563570
ctx.generateVariable(this.varName)
564571
);
@@ -664,11 +671,20 @@ Parser.prototype.generate_encodeBit = function(ctx) {
664671
ctx.pushCode("{0} = {0} >>> 0;", tmpVal);
665672
bitOffset += parser.options.length;
666673
});
667-
ctx.pushCode(
668-
"if (offset + {1} <= maxOffset) offset = buffer.writeUIntBE({0}, offset, {1});",
669-
tmpVal,
670-
sum / 8
671-
);
674+
if (sum == 8) {
675+
ctx.pushCode("smartBuffer.writeUInt8({0});", tmpVal);
676+
} else if (sum == 16) {
677+
ctx.pushCode("smartBuffer.writeUInt16BE({0});", tmpVal);
678+
} else if (sum == 24) {
679+
var val1 = ctx.generateTmpVariable();
680+
var val2 = ctx.generateTmpVariable();
681+
ctx.pushCode("var {0} = ({1} >>> 8);", val1, tmpVal);
682+
ctx.pushCode("var {0} = ({1} & 0x0ff);", val2, tmpVal);
683+
ctx.pushCode("smartBuffer.writeUInt16BE({0});", val1);
684+
ctx.pushCode("smartBuffer.writeUInt8({0});", val2);
685+
} else if (sum == 32) {
686+
ctx.pushCode("smartBuffer.writeUInt32BE({0});", tmpVal);
687+
}
672688

673689
ctx.bitFields = [];
674690
}
@@ -681,7 +697,8 @@ Parser.prototype.generateSkip = function(ctx) {
681697

682698
Parser.prototype.generate_encodeSkip = function(ctx) {
683699
var length = ctx.generateOption(this.options.length);
684-
ctx.pushCode("offset += {0};", length);
700+
ctx.pushCode("smartBuffer._ensureWriteable({0});", length);
701+
ctx.pushCode("smartBuffer.writeOffset += {0};", length);
685702
};
686703

687704
Parser.prototype.generateString = function(ctx) {
@@ -740,21 +757,36 @@ Parser.prototype.generate_encodeString = function(ctx) {
740757

741758
// Get the length of string to encode
742759
if (this.options.length) {
760+
var optLength = ctx.generateOption(this.options.length);
761+
// Encode the string to a temporary buffer
762+
var tmpBuf = ctx.generateTmpVariable();
743763
ctx.pushCode(
744-
"offset += buffer.write({0}, offset, {1}, '{2}');",
764+
"var {0} = Buffer.from({1}, '{2}');",
765+
tmpBuf,
745766
name,
746-
ctx.generateOption(this.options.length),
747767
this.options.encoding
748768
);
769+
// Truncate the buffer to specified (Bytes) length
770+
ctx.pushCode("{0} = {0}.slice(0, {1});", tmpBuf, optLength);
771+
// Compute padding length
772+
var padLen = ctx.generateTmpVariable();
773+
ctx.pushCode("{0} = {1} - {2}.length;", padLen, optLength, tmpBuf);
774+
// Copy the temporary string buffer to current smartBuffer
775+
ctx.pushCode("smartBuffer.writeBuffer({0});", tmpBuf);
776+
// Add padding spaces
777+
ctx.pushCode(
778+
"if ({0} > 0) {smartBuffer.writeString(' '.repeat({0}));}",
779+
padLen
780+
);
749781
} else {
750782
ctx.pushCode(
751-
"offset += buffer.write({0}, offset, '{1}');",
783+
"smartBuffer.writeString({0}, '{1}');",
752784
name,
753785
this.options.encoding
754786
);
755787
}
756788
if (this.options.zeroTerminated) {
757-
ctx.pushCode("offset = buffer.writeUInt8(0x00, offset);");
789+
ctx.pushCode("smartBuffer.writeUInt8(0x00);");
758790
}
759791
};
760792

@@ -780,7 +812,7 @@ Parser.prototype.generateBuffer = function(ctx) {
780812

781813
Parser.prototype.generate_encodeBuffer = function(ctx) {
782814
ctx.pushCode(
783-
"offset += {0}.copy(buffer, offset);",
815+
"smartBuffer.writeBuffer({0});",
784816
ctx.generateVariable(this.varName)
785817
);
786818
};
@@ -864,11 +896,7 @@ Parser.prototype.generate_encodeArray = function(ctx) {
864896
ctx.generateError('"Encoding associative array not supported"');
865897
}
866898

867-
if (lengthInBytes !== undefined) {
868-
ctx.pushCode("var {0} = offset + {1};", maxOffset, lengthInBytes);
869-
} else {
870-
ctx.pushCode("var {0} = Number.MAX_SAFE_INTEGER;", maxOffset);
871-
}
899+
// Compute the desired count of array items to encode (min of array size and length option)
872900
if (length !== undefined) {
873901
var tmpLength = ctx.generateTmpVariable();
874902
ctx.pushCode("var {0} = {1};", tmpLength, length);
@@ -882,8 +910,15 @@ Parser.prototype.generate_encodeArray = function(ctx) {
882910
ctx.pushCode("var {0} = {1}.length;", maxItems, name);
883911
}
884912

885-
ctx.pushCode("var {0} = 0;", itemCounter);
913+
// Save current encoding smartBuffer and allocate a new one
914+
var savSmartBuffer = ctx.generateTmpVariable();
915+
ctx.pushCode(
916+
"var {0} = smartBuffer; smartBuffer = SmartBuffer.fromOptions({size: {1}, encoding: 'utf8'});",
917+
savSmartBuffer,
918+
this.smartBufferSize
919+
);
886920

921+
ctx.pushCode("var {0} = 0;", itemCounter);
887922
if (typeof this.options.readUntil === "function") {
888923
ctx.pushCode("do {");
889924
} else {
@@ -895,74 +930,44 @@ Parser.prototype.generate_encodeArray = function(ctx) {
895930

896931
if (typeof type === "string") {
897932
if (!aliasRegistry[type]) {
898-
ctx.pushCode(
899-
"if (offset + {0} > {1} || offset + {0} > maxOffset) break;",
900-
PRIMITIVE_TYPES[NAME_MAP[type]],
901-
maxOffset
902-
);
903-
ctx.pushCode(
904-
"offset = buffer.write{0}({1}, offset);",
905-
NAME_MAP[type],
906-
item
907-
);
933+
ctx.pushCode("smartBuffer.write{0}({1});", NAME_MAP[type], item);
908934
} else {
909-
var tempVar = ctx.generateTmpVariable();
910935
ctx.pushCode(
911-
"var {0} = {1}({2});",
912-
tempVar,
936+
"smartBuffer.writeBuffer({0}({1}));",
913937
FUNCTION_ENCODE_PREFIX + type,
914938
item
915939
);
916-
ctx.pushCode(
917-
"if (offset + {0}.length > {1} || offset + {0}.length > maxOffset) break;",
918-
tempVar,
919-
maxOffset
920-
);
921-
ctx.pushCode("offset += {0}.copy(buffer, offset);", tempVar);
922-
if (type !== this.alias) ctx.addReference(type);
940+
if (type !== this.alias) {
941+
ctx.addReference(type);
942+
}
923943
}
924944
} else if (type instanceof Parser) {
925-
var savMaxOffset = ctx.generateTmpVariable();
926-
var savBuffer = ctx.generateTmpVariable();
927-
var savOffset = ctx.generateTmpVariable();
928-
ctx.pushCode(
929-
"var {0} = maxOffset; maxOffset = {1};",
930-
savMaxOffset,
931-
maxOffset
932-
);
933-
ctx.pushCode(
934-
"var {0} = buffer; buffer = Buffer.alloc({1});",
935-
savBuffer,
936-
this.bufferSize
937-
);
938-
ctx.pushCode("var {0} = offset; offset = 0;", savOffset);
939945
ctx.pushScope(item);
940946
type.generateEncode(ctx);
941947
ctx.popScope();
942-
ctx.pushCode(
943-
"if ({1} + offset <= {0}) {1} += buffer.copy({2}, {3}, 0, offset);",
944-
maxOffset,
945-
savOffset,
946-
savBuffer,
947-
savOffset
948-
);
949-
ctx.pushCode(
950-
"maxOffset = {0}; buffer = {1}; offset = {2};",
951-
savMaxOffset,
952-
savBuffer,
953-
savOffset
954-
);
955948
}
956949

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

959952
if (typeof this.options.readUntil === "function") {
960953
ctx.pushCode(
961-
" while (!({0}).call(this, {1}, buffer.slice(offset)));",
954+
" while (!({0}).call(this, {1}, {2}.toBuffer()));",
962955
this.options.readUntil,
963-
item
956+
item,
957+
savSmartBuffer
964958
);
965959
}
960+
961+
var tmpBuffer = ctx.generateTmpVariable();
962+
ctx.pushCode("var {0} = smartBuffer.toBuffer()", tmpBuffer);
963+
if (lengthInBytes !== undefined) {
964+
// Truncate the tmpBuffer so that it will respect the lengthInBytes option
965+
ctx.pushCode("{0} = {0}.slice(0, {1});", tmpBuffer, lengthInBytes);
966+
}
967+
// Copy tmp Buffer to saved smartBuffer
968+
ctx.pushCode("{0}.writeBuffer({1});", savSmartBuffer, tmpBuffer);
969+
// Restore current smartBuffer
970+
ctx.pushCode("smartBuffer = {0};", savSmartBuffer);
966971
};
967972

968973
Parser.prototype.generateChoiceCase = function(ctx, varName, type) {
@@ -995,7 +1000,7 @@ Parser.prototype.generate_encodeChoiceCase = function(ctx, varName, type) {
9951000
if (typeof type === "string") {
9961001
if (!aliasRegistry[type]) {
9971002
ctx.pushCode(
998-
"offset = buffer.write{0}({1}, offset);",
1003+
"smartBuffer.write{0}({1});",
9991004
NAME_MAP[type],
10001005
ctx.generateVariable(this.varName)
10011006
);
@@ -1007,7 +1012,7 @@ Parser.prototype.generate_encodeChoiceCase = function(ctx, varName, type) {
10071012
FUNCTION_ENCODE_PREFIX + type,
10081013
ctx.generateVariable(this.varName)
10091014
);
1010-
ctx.pushCode("offset += {0}.copy(buffer, offset);", tempVar);
1015+
ctx.pushCode("smartBuffer.writeBuffer({0});", tempVar);
10111016
if (type !== this.alias) ctx.addReference(type);
10121017
}
10131018
} else if (type instanceof Parser) {
@@ -1099,7 +1104,7 @@ Parser.prototype.generate_encodeNest = function(ctx) {
10991104
FUNCTION_ENCODE_PREFIX + this.options.type,
11001105
nestVar
11011106
);
1102-
ctx.pushCode("offset += {0}.copy(buffer, offset);", tempVar);
1107+
ctx.pushCode("smartBuffer.writeBuffer({0});", tempVar);
11031108
if (this.options.type !== this.alias) ctx.addReference(this.options.type);
11041109
}
11051110
};

package-lock.json

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@
3434
"url": "http://github.com/keichi/binary-parser.git"
3535
},
3636
"bugs": "http://github.com/keichi/binary-parser/issues",
37-
"dependencies": {},
37+
"dependencies": {
38+
"smart-buffer": "^4.0.1"
39+
},
3840
"engines": {
39-
"node": ">=5.10.0"
41+
"node": ">=5.10.0"
4042
}
4143
}

0 commit comments

Comments
 (0)