binary-parser
Advanced tools
Comparing version 0.0.1 to 0.0.2
//======================================================================================== | ||
// Globals | ||
//======================================================================================== | ||
var util = require('util'); | ||
var Parser = function() { | ||
this.varName = ''; | ||
this.type = ''; | ||
this.options = {}; | ||
this.next = null; | ||
this.head = null; | ||
this.compiled = null; | ||
}; | ||
var PRIMITIVE_TYPES = { | ||
@@ -34,2 +24,3 @@ 'UInt8' : 1, | ||
'String' : null, | ||
'Buffer' : null, | ||
'Array' : null, | ||
@@ -49,24 +40,24 @@ 'Skip' : null, | ||
//======================================================================================== | ||
// Parser builder functions | ||
// class Parser | ||
//======================================================================================== | ||
Parser.start = function() { | ||
return new Parser(); | ||
//---------------------------------------------------------------------------------------- | ||
// constructor | ||
//---------------------------------------------------------------------------------------- | ||
var Parser = function() { | ||
this.varName = ''; | ||
this.type = ''; | ||
this.options = {}; | ||
this.next = null; | ||
this.head = null; | ||
this.compiled = null; | ||
}; | ||
Parser.prototype.setNextParser = function(type, varName, options) { | ||
var parser = new Parser(); | ||
parser.type = NAME_MAP[type]; | ||
parser.varName = varName; | ||
parser.options = options || parser.options; | ||
if (this.head) { | ||
this.head.next = parser; | ||
} else { | ||
this.next = parser; | ||
} | ||
this.head = parser; | ||
//---------------------------------------------------------------------------------------- | ||
// public methods | ||
//---------------------------------------------------------------------------------------- | ||
return this; | ||
Parser.start = function() { | ||
return new Parser(); | ||
}; | ||
@@ -98,2 +89,10 @@ | ||
Parser.prototype.buffer = function(varName, options) { | ||
if (!options.length && !options.readUntil) { | ||
throw new Error('Length nor readUntil is defined in buffer parser'); | ||
} | ||
return this.setNextParser('buffer', varName, options); | ||
}; | ||
Parser.prototype.array = function(varName, options) { | ||
@@ -153,68 +152,84 @@ if (!options.readUntil && !options.length) { | ||
//======================================================================================== | ||
// Parser | ||
//======================================================================================== | ||
Parser.prototype.getCode = function() { | ||
var ctx = new Context(); | ||
// Follow the parser chain till the root and start parsing from there | ||
Parser.prototype.parse = function(buffer) { | ||
if (!Buffer.isBuffer(buffer)) { | ||
throw new Error('argument "buffer" is not a Buffer object'); | ||
} | ||
ctx.pushCode('var vars = {};'); | ||
ctx.pushCode('var iterator = 0;'); | ||
this.generate(ctx); | ||
ctx.pushCode('return vars;'); | ||
if (!this.compiled) { | ||
this.compile(); | ||
} | ||
return this.compiled(buffer); | ||
return ctx.code; | ||
}; | ||
var Context = function() { | ||
this.code = ''; | ||
this.scopes = []; | ||
Parser.prototype.compile = function() { | ||
this.compiled = new Function('buffer', this.getCode()); | ||
}; | ||
Context.prototype.generateVariable = function(name) { | ||
var arr = ['vars']; | ||
Array.prototype.push.apply(arr, this.scopes); | ||
if (name) { | ||
arr.push(name); | ||
Parser.prototype.sizeOf = function() { | ||
var size = NaN; | ||
if (Object.keys(PRIMITIVE_TYPES).indexOf(this.type) >= 0) { | ||
size = PRIMITIVE_TYPES[this.type]; | ||
// if this is a fixed length string | ||
} else if (this.type === 'String' && typeof this.options.length === 'number') { | ||
size = this.options.length; | ||
// if this is a fixed length array | ||
} else if (this.type === 'Array' && typeof this.options.length === 'number') { | ||
var elementSize = NaN; | ||
if (typeof this.options.type === 'string'){ | ||
elementSize = PRIMITIVE_TYPES[NAME_MAP[this.options.type]]; | ||
} else if (this.options.type instanceof Parser) { | ||
elementSize = this.options.type.sizeOf(); | ||
} | ||
size = this.options.length * elementSize; | ||
// if this a skip | ||
} else if (this.type === 'Skip') { | ||
size = this.options.length; | ||
} else if (!this.type) { | ||
size = 0; | ||
} | ||
return arr.join('.'); | ||
if (this.next) { | ||
size += this.next.sizeOf(); | ||
} | ||
return size; | ||
}; | ||
Context.prototype.generateOption = function(val) { | ||
if (typeof val === 'number') { | ||
return val.toString(); | ||
} else if (typeof val === 'string') { | ||
return this.generateVariable(val); | ||
} else if (typeof val === 'function') { | ||
return util.format('(%s).call(%s)', val, this.generateVariable()); | ||
// Follow the parser chain till the root and start parsing from there | ||
Parser.prototype.parse = function(buffer) { | ||
if (!Buffer.isBuffer(buffer)) { | ||
throw new Error('argument "buffer" is not a Buffer object'); | ||
} | ||
}; | ||
Context.prototype.pushCode = function(code) { | ||
var re = /{\d+}/g; | ||
var matches = code.match(re); | ||
var params = Array.prototype.slice.call(arguments, 1); | ||
if (matches) { | ||
matches.forEach(function(match) { | ||
var index = parseInt(match.substr(1, match.length - 2), 10); | ||
code = code.replace(match, params[index].toString()); | ||
}); | ||
if (!this.compiled) { | ||
this.compile(); | ||
} | ||
this.code += code + '\n'; | ||
return this.compiled(buffer); | ||
}; | ||
Parser.prototype.compile = function() { | ||
var ctx = new Context(); | ||
//---------------------------------------------------------------------------------------- | ||
// private methods | ||
//---------------------------------------------------------------------------------------- | ||
ctx.pushCode('var vars = {};'); | ||
ctx.pushCode('var iterator = 0;'); | ||
this.generate(ctx); | ||
ctx.pushCode('return vars;'); | ||
Parser.prototype.setNextParser = function(type, varName, options) { | ||
var parser = new Parser(); | ||
parser.type = NAME_MAP[type]; | ||
parser.varName = varName; | ||
parser.options = options || parser.options; | ||
if (this.head) { | ||
this.head.next = parser; | ||
} else { | ||
this.next = parser; | ||
} | ||
this.head = parser; | ||
this.compiled = new Function('buffer', ctx.code); | ||
return this; | ||
}; | ||
@@ -283,2 +298,22 @@ | ||
Parser.prototype.generateBuffer = function(ctx) { | ||
if (this.options.readUntil === 'eof') { | ||
ctx.pushCode('{0} = buffer.slice(iterator, buffer.length - 1);', | ||
ctx.generateVariable(this.varName) | ||
); | ||
} else { | ||
ctx.pushCode('{0} = buffer.slice(iterator, iterator + {1});', | ||
ctx.generateVariable(this.varName), | ||
ctx.generateOption(this.options.length) | ||
); | ||
ctx.pushCode('iterator += {0};', ctx.generateOption(this.options.length)); | ||
} | ||
if (this.options.clone) { | ||
ctx.pushCode('{0} = (function() { var buf = new Buffer({0}.length); {0}.copy(buf); return buf; })();', | ||
ctx.generateVariable(this.varName) | ||
); | ||
} | ||
}; | ||
Parser.prototype.generateArray = function(ctx) { | ||
@@ -321,13 +356,2 @@ var length = ctx.generateOption(this.options.length); | ||
Parser.prototype.generateChoiceCase = function(ctx, varName, type) { | ||
if (typeof type === 'string') { | ||
ctx.pushCode('{0} = buffer.read{1}(iterator);', ctx.generateVariable(this.varName), NAME_MAP[type]); | ||
ctx.pushCode('iterator += {0};', PRIMITIVE_TYPES[NAME_MAP[type]]); | ||
} else if (type instanceof Parser) { | ||
ctx.scopes.push(varName); | ||
type.generate(ctx); | ||
ctx.scopes.pop(); | ||
} | ||
}; | ||
Parser.prototype.generateChoice = function(ctx) { | ||
@@ -358,35 +382,48 @@ var tag = ctx.generateOption(this.options.tag); | ||
Parser.prototype.sizeOf = function() { | ||
var size = NaN; | ||
//---------------------------------------------------------------------------------------- | ||
// constructor | ||
//---------------------------------------------------------------------------------------- | ||
if (Object.keys(PRIMITIVE_TYPES).indexOf(this.type) >= 0) { | ||
size = PRIMITIVE_TYPES[this.type]; | ||
// if this is a fixed length string | ||
} else if (this.type === 'String' && typeof this.options.length === 'number') { | ||
size = this.options.length; | ||
// if this is a fixed length array | ||
} else if (this.type === 'Array' && typeof this.options.length === 'number') { | ||
var elementSize = NaN; | ||
if (typeof this.options.type === 'string'){ | ||
elementSize = PRIMITIVE_TYPES[NAME_MAP[this.options.type]]; | ||
} else if (this.options.type instanceof Parser) { | ||
elementSize = this.options.type.sizeOf(); | ||
} | ||
size = this.options.length * elementSize; | ||
// if this a skip | ||
} else if (this.type === 'Skip') { | ||
size = this.options.length; | ||
var Context = function() { | ||
this.code = ''; | ||
this.scopes = []; | ||
}; | ||
} else if (!this.type) { | ||
size = 0; | ||
//---------------------------------------------------------------------------------------- | ||
// public methods | ||
//---------------------------------------------------------------------------------------- | ||
Context.prototype.generateVariable = function(name) { | ||
var arr = ['vars']; | ||
Array.prototype.push.apply(arr, this.scopes); | ||
if (name) { | ||
arr.push(name); | ||
} | ||
if (this.next) { | ||
size += this.next.sizeOf(); | ||
return arr.join('.'); | ||
}; | ||
Context.prototype.generateOption = function(val) { | ||
if (typeof val === 'number') { | ||
return val.toString(); | ||
} else if (typeof val === 'string') { | ||
return this.generateVariable(val); | ||
} else if (typeof val === 'function') { | ||
return '(' + val + ').call(' + this.generateVariable() + ')'; | ||
} | ||
}; | ||
return size; | ||
Context.prototype.pushCode = function(code) { | ||
var re = /{\d+}/g; | ||
var matches = code.match(re); | ||
var params = Array.prototype.slice.call(arguments, 1); | ||
if (matches) { | ||
matches.forEach(function(match) { | ||
var index = parseInt(match.substr(1, match.length - 2), 10); | ||
code = code.replace(match, params[index].toString()); | ||
}); | ||
} | ||
this.code += code + '\n'; | ||
}; | ||
@@ -393,0 +430,0 @@ |
{ | ||
"name": "binary-parser", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Blazing-fast binary parser builder", | ||
@@ -5,0 +5,0 @@ "main": "lib/binary_parser.js", |
@@ -82,3 +82,3 @@ # Binary-parser | ||
are valid. See [`Buffer.toString`](http://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end) for more info. | ||
- `length `- (Required) Length of the string. Can be a number, string or a function. | ||
- `length ` - (Required) Length of the string. Can be a number, string or a function. | ||
Use number for statically sized arrays, string to reference another variable and | ||
@@ -88,2 +88,16 @@ function to do some calculation. | ||
### buffer(name [,options]) | ||
Parse bytes as a string. `name` should consist only of alpha numeric characters and start | ||
with an alphabet. `options` is an object; following options are available: | ||
- `clone` - (Optional, defaults to `false`) By default, `buffer(name [,options])` returns a new buffer which references | ||
the same memory as the parser input, but offset and cropped by a certain range. If this option is true, input buffer | ||
will be cloned and a new buffer referncing another memory is returned. | ||
- `length ` - (either `length` or `readUntil` is required) Length of the buffer. Can be a number, string or a function. | ||
Use number for statically sized buffers, string to reference another variable and | ||
function to do some calculation. | ||
- `readUntil` - (either `length` or `readUntil` is required) If `'eof'`, then this parser | ||
will read till it reaches end of the `Buffer` object. | ||
### array(name [,options]) | ||
@@ -177,2 +191,6 @@ Parse bytes as an array. `options` is an object; following options are available: | ||
### getCode() | ||
Dynamically generates the code for this parser and returns it as a string. | ||
Usually used for debugging. | ||
### Assertion | ||
@@ -179,0 +197,0 @@ You can do assertions during the parsing (useful for checking magic numbers and so on). |
@@ -83,2 +83,31 @@ var assert = require('assert'); | ||
describe('Buffer parser', function() { | ||
it('should parse as buffer', function() { | ||
var parser = new Parser() | ||
.uint8('len') | ||
.buffer('raw', { | ||
length: 'len' | ||
}); | ||
var buf = new Buffer('deadbeefdeadbeef', 'hex'); | ||
var result = parser.parse(Buffer.concat([new Buffer([8]), buf])); | ||
assert.deepEqual(result.raw, buf); | ||
}); | ||
it('should clone buffer if options.clone is true', function() { | ||
var parser = new Parser() | ||
.buffer('raw', { | ||
length: 8, | ||
clone: true | ||
}); | ||
var buf = new Buffer('deadbeefdeadbeef', 'hex'); | ||
var result = parser.parse(buf); | ||
assert.deepEqual(result.raw, buf); | ||
result.raw[0] = 0xff; | ||
assert.notDeepEqual(result.raw, buf); | ||
}); | ||
}); | ||
describe('Array parser', function() { | ||
@@ -85,0 +114,0 @@ it('should parse array of primitive types', function(){ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
271396
924
232
0