From e9d6c96f0cc415eb14615903c374bb91062034d0 Mon Sep 17 00:00:00 2001 From: Keichi Takahashi Date: Sat, 18 Sep 2021 23:01:46 +0900 Subject: [PATCH 1/2] Disable context variables by default --- README.md | 10 +++- lib/binary_parser.ts | 118 ++++++++++++++++++++++++++------------- lib/context.ts | 4 +- test/composite_parser.js | 8 +++ 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 93dc61af..1642b697 100644 --- a/README.md +++ b/README.md @@ -520,13 +520,17 @@ These options can be used in all parsers. ``` ### Context variables -You can use some special fields while parsing to traverse your structure. These -context variables will be removed after the parsing process: +You can use some special fields while parsing to traverse your structure. +These context variables will be removed after the parsing process. +Note that this feature is turned off by default for performance reasons, and +you need to call `.useContextVars()` to enable it. + - `$parent` - This field references the parent structure. This variable will be `null` while parsing the root structure. ```javascript var parser = new Parser() + .useContextVars() .nest("header", { type: new Parser().uint32("length"), }) @@ -542,6 +546,7 @@ context variables will be removed after the parsing process: ```javascript var parser = new Parser() + .useContextVars() .nest("header", { type: new Parser().uint32("length"), }) @@ -562,6 +567,7 @@ context variables will be removed after the parsing process: ```javascript var parser = new Parser() + .useContextVars() .nest("header", { type: new Parser().uint32("length"), }) diff --git a/lib/binary_parser.ts b/lib/binary_parser.ts index e9809a11..5129a28b 100644 --- a/lib/binary_parser.ts +++ b/lib/binary_parser.ts @@ -178,6 +178,7 @@ export class Parser { endian: Endianess = 'be'; constructorFn: Function | null = null; alias: string | null = null; + useContextVariables: boolean = false; constructor() {} @@ -637,6 +638,12 @@ export class Parser { return this; } + useContextVars(useContextVariables: boolean = true) { + this.useContextVariables = useContextVariables; + + return this; + } + create(constructorFn: Function) { if (!(constructorFn instanceof Function)) { throw new Error('Constructor must be a Function object.'); @@ -648,7 +655,7 @@ export class Parser { } private getContext(importPath?: string) { - const ctx = new Context(importPath); + const ctx = new Context(importPath, this.useContextVariables); ctx.pushCode( 'var dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);' @@ -673,15 +680,16 @@ export class Parser { ctx.pushCode( `var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};` ); + ctx.pushCode('vars.$parent = null;'); ctx.pushCode('vars.$root = vars;'); this.generate(ctx); - this.resolveReferences(ctx); ctx.pushCode('delete vars.$parent;'); ctx.pushCode('delete vars.$root;'); + ctx.pushCode('return vars;'); } @@ -691,7 +699,7 @@ export class Parser { `var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};` ); ctx.pushCode( - `var ctx = Object.assign({$parent: null, $root: vars}, context || {});` + 'var ctx = Object.assign({$parent: null, $root: vars}, context || {});' ); ctx.pushCode(`vars = Object.assign(vars, ctx);`); @@ -1093,13 +1101,15 @@ export class Parser { ); ctx.pushCode(`offset += ${PRIMITIVE_SIZES[type as PrimitiveTypes]};`); } else { - const parentVar = ctx.generateVariable(); const tempVar = ctx.generateTmpVariable(); ctx.pushCode(`var ${tempVar} = ${FUNCTION_PREFIX + type}(offset, {`); - ctx.pushCode(`$parent: ${parentVar},`); - ctx.pushCode(`$root: ${parentVar}.$root,`); - if (!this.options.readUntil && lengthInBytes === undefined) { - ctx.pushCode(`$index: ${length} - ${counter},`); + if (ctx.useContextVariables) { + const parentVar = ctx.generateVariable(); + ctx.pushCode(`$parent: ${parentVar},`); + ctx.pushCode(`$root: ${parentVar}.$root,`); + if (!this.options.readUntil && lengthInBytes === undefined) { + ctx.pushCode(`$index: ${length} - ${counter},`); + } } ctx.pushCode(`});`); ctx.pushCode( @@ -1108,19 +1118,25 @@ export class Parser { if (type !== this.alias) ctx.addReference(type); } } else if (type instanceof Parser) { - const parentVar = ctx.generateVariable(); ctx.pushCode(`var ${item} = {};`); - + const parentVar = ctx.generateVariable(); ctx.pushScope(item); - ctx.pushCode(`${item}.$parent = ${parentVar};`); - ctx.pushCode(`${item}.$root = ${parentVar}.$root;`); - if (!this.options.readUntil && lengthInBytes === undefined) { - ctx.pushCode(`${item}.$index = ${length} - ${counter};`); + + if (ctx.useContextVariables) { + ctx.pushCode(`${item}.$parent = ${parentVar};`); + ctx.pushCode(`${item}.$root = ${parentVar}.$root;`); + if (!this.options.readUntil && lengthInBytes === undefined) { + ctx.pushCode(`${item}.$index = ${length} - ${counter};`); + } } + type.generate(ctx); - ctx.pushCode(`delete ${item}.$parent;`); - ctx.pushCode(`delete ${item}.$root;`); - ctx.pushCode(`delete ${item}.$index;`); + + if (ctx.useContextVariables) { + ctx.pushCode(`delete ${item}.$parent;`); + ctx.pushCode(`delete ${item}.$root;`); + ctx.pushCode(`delete ${item}.$index;`); + } ctx.popScope(); } @@ -1158,8 +1174,10 @@ export class Parser { } else { const tempVar = ctx.generateTmpVariable(); ctx.pushCode(`var ${tempVar} = ${FUNCTION_PREFIX + type}(offset, {`); - ctx.pushCode(`$parent: ${varName}.$parent,`); - ctx.pushCode(`$root: ${varName}.$root,`); + if (ctx.useContextVariables) { + ctx.pushCode(`$parent: ${varName}.$parent,`); + ctx.pushCode(`$root: ${varName}.$root,`); + } ctx.pushCode(`});`); ctx.pushCode( `${varName} = ${tempVar}.result; offset = ${tempVar}.offset;` @@ -1180,9 +1198,11 @@ export class Parser { if (this.varName) { ctx.pushCode(`${nestVar} = {};`); - const parentVar = ctx.generateVariable(); - ctx.pushCode(`${nestVar}.$parent = ${parentVar};`); - ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`); + if (ctx.useContextVariables) { + const parentVar = ctx.generateVariable(); + ctx.pushCode(`${nestVar}.$parent = ${parentVar};`); + ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`); + } } ctx.pushCode(`switch(${tag}) {`); Object.keys(this.options.choices).forEach((tag) => { @@ -1200,7 +1220,7 @@ export class Parser { } ctx.pushCode('}'); - if (this.varName) { + if (this.varName && ctx.useContextVariables) { ctx.pushCode(`delete ${nestVar}.$parent;`); ctx.pushCode(`delete ${nestVar}.$root;`); } @@ -1211,26 +1231,35 @@ export class Parser { if (this.options.type instanceof Parser) { if (this.varName) { - const parentVar = ctx.generateVariable(); ctx.pushCode(`${nestVar} = {};`); - ctx.pushCode(`${nestVar}.$parent = ${parentVar};`); - ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`); + + if (ctx.useContextVariables) { + const parentVar = ctx.generateVariable(); + ctx.pushCode(`${nestVar}.$parent = ${parentVar};`); + ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`); + } } + ctx.pushPath(this.varName); this.options.type.generate(ctx); ctx.popPath(this.varName); - if (this.varName) { - ctx.pushCode(`delete ${nestVar}.$parent;`); - ctx.pushCode(`delete ${nestVar}.$root;`); + + if (this.varName && ctx.useContextVariables) { + if (ctx.useContextVariables) { + ctx.pushCode(`delete ${nestVar}.$parent;`); + ctx.pushCode(`delete ${nestVar}.$root;`); + } } } else if (aliasRegistry[this.options.type]) { - const parentVar = ctx.generateVariable(); const tempVar = ctx.generateTmpVariable(); ctx.pushCode( `var ${tempVar} = ${FUNCTION_PREFIX + this.options.type}(offset, {` ); - ctx.pushCode(`$parent: ${parentVar},`); - ctx.pushCode(`$root: ${parentVar}.$root,`); + if (ctx.useContextVariables) { + const parentVar = ctx.generateVariable(); + ctx.pushCode(`$parent: ${parentVar},`); + ctx.pushCode(`$root: ${parentVar}.$root,`); + } ctx.pushCode(`});`); ctx.pushCode( `${nestVar} = ${tempVar}.result; offset = ${tempVar}.offset;` @@ -1331,23 +1360,32 @@ export class Parser { ctx.pushCode(`offset = ${offset};`); if (this.options.type instanceof Parser) { - const parentVar = ctx.generateVariable(); ctx.pushCode(`${nestVar} = {};`); - ctx.pushCode(`${nestVar}.$parent = ${parentVar};`); - ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`); + + if (ctx.useContextVariables) { + const parentVar = ctx.generateVariable(); + ctx.pushCode(`${nestVar}.$parent = ${parentVar};`); + ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`); + } + ctx.pushPath(this.varName); this.options.type.generate(ctx); ctx.popPath(this.varName); - ctx.pushCode(`delete ${nestVar}.$parent;`); - ctx.pushCode(`delete ${nestVar}.$root;`); + + if (ctx.useContextVariables) { + ctx.pushCode(`delete ${nestVar}.$parent;`); + ctx.pushCode(`delete ${nestVar}.$root;`); + } } else if (aliasRegistry[this.options.type]) { - const parentVar = ctx.generateVariable(); const tempVar = ctx.generateTmpVariable(); ctx.pushCode( `var ${tempVar} = ${FUNCTION_PREFIX + this.options.type}(offset, {` ); - ctx.pushCode(`$parent: ${parentVar},`); - ctx.pushCode(`$root: ${parentVar}.$root,`); + if (ctx.useContextVariables) { + const parentVar = ctx.generateVariable(); + ctx.pushCode(`$parent: ${parentVar},`); + ctx.pushCode(`$root: ${parentVar}.$root,`); + } ctx.pushCode(`});`); ctx.pushCode( `${nestVar} = ${tempVar}.result; offset = ${tempVar}.offset;` diff --git a/lib/context.ts b/lib/context.ts index 185714ec..b87e9c1c 100644 --- a/lib/context.ts +++ b/lib/context.ts @@ -9,9 +9,11 @@ export class Context { importPath: string; imports: any[] = []; reverseImports = new Map(); + useContextVariables: boolean = false; - constructor(importPath?: string) { + constructor(importPath?: string, useContextVariables?: boolean) { this.importPath = importPath; + this.useContextVariables = useContextVariables; } generateVariable(name?: string) { diff --git a/test/composite_parser.js b/test/composite_parser.js index 50c54f60..cbc4bd1e 100644 --- a/test/composite_parser.js +++ b/test/composite_parser.js @@ -72,6 +72,7 @@ const suite = (Buffer) => }); var parser = Parser.start() + .useContextVars() .uint16le('length') .uint16le('valueLength') .array('message', { @@ -109,6 +110,7 @@ const suite = (Buffer) => }); var parser = Parser.start() + .useContextVars() .uint16le('length') .uint16le('valueLength') .array('message', { @@ -444,6 +446,7 @@ const suite = (Buffer) => // / // 0 + // prettier-ignore var buffer = Buffer.from([ 2, /* 0 */ 3, @@ -523,6 +526,7 @@ const suite = (Buffer) => }); var parser = Parser.start() + .useContextVars() .uint16le('length') .uint16le('valueLength') .array('message', { @@ -567,6 +571,7 @@ const suite = (Buffer) => .namely('ArrayLengthIndexTest'); var parser = Parser.start() + .useContextVars() .uint16le('length') .uint16le('valueLength') .array('message', { @@ -845,6 +850,7 @@ const suite = (Buffer) => // / // 0 + // prettier-ignore var buffer = Buffer.from([ 2, /* left -> */ 3, @@ -1009,6 +1015,7 @@ const suite = (Buffer) => }); it('should be able to use parsing context', function () { var parser = Parser.start() + .useContextVars() .uint8('tag') .uint8('items') .choice('data', { @@ -1141,6 +1148,7 @@ const suite = (Buffer) => it('should be able to use parsing context', function () { var parser = Parser.start() + .useContextVars() .uint8('items') .nest('data', { type: Parser.start() From 4951d4051f7c40692b491e3f9e226488bd2d5c14 Mon Sep 17 00:00:00 2001 From: Keichi Takahashi Date: Sat, 18 Sep 2021 23:04:15 +0900 Subject: [PATCH 2/2] Use prettier's default formatting style --- .prettierrc | 7 - benchmark/bench.js | 113 ++--- example/bmp.js | 46 +- example/classfile.js | 70 +-- example/elf32.js | 94 ++-- example/ip.js | 34 +- example/jpg.js | 106 ++--- example/mbr.js | 30 +- example/tar.js | 42 +- example/tcp.js | 40 +- lib/binary_parser.ts | 420 +++++++++--------- lib/context.ts | 20 +- package.json | 4 +- test/composite_parser.js | 903 +++++++++++++++------------------------ test/primitive_parser.js | 357 +++++++--------- 15 files changed, 1007 insertions(+), 1279 deletions(-) delete mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 8fb3cf3f..00000000 --- a/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "parser": "typescript", - "trailingComma": "es5", - "tabWidth": 2, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/benchmark/bench.js b/benchmark/bench.js index 891fe015..6c338ac0 100644 --- a/benchmark/bench.js +++ b/benchmark/bench.js @@ -1,51 +1,54 @@ -var binary = require('binary'); -var Benchmark = require('benchmark'); -var bp = require('binparse').bp; -var Parser = require('binary-parser').Parser; -var Destruct = require('destruct-js'); -const Struct = require('structron'); +var binary = require("binary"); +var Benchmark = require("benchmark"); +var bp = require("binparse").bp; +var Parser = require("../dist/binary_parser").Parser; +var Destruct = require("destruct-js"); +const Struct = require("structron"); -var suite = new Benchmark.Suite; +var suite = new Benchmark.Suite(); // binparse -const PointParser = bp.object('Point', { - x: bp.lu16, - y: bp.lu16, - z: bp.lu16, +const PointParser = bp.object("Point", { + x: bp.lu16, + y: bp.lu16, + z: bp.lu16, }); -const PointsParser = bp.object('SimpleObject', { - length: bp.variable('len', bp.lu32), - points: bp.array('Points', PointParser, 'len'), +const PointsParser = bp.object("SimpleObject", { + length: bp.variable("len", bp.lu32), + points: bp.array("Points", PointParser, "len"), }); // binary-parser -const Points = new Parser() - .uint32le('len') - .array('points', { - length: 'len', - type: new Parser() - .uint16le('x') - .uint16le('y') - .uint16le('z') - }); +const Points = new Parser().uint32le("len").array("points", { + length: "len", + type: new Parser().uint16le("x").uint16le("y").uint16le("z"), +}); // destruct-js -const spec = new Destruct.Spec({mode: Destruct.Mode.LE}); -spec.field('len', Destruct.UInt32) - .loop('points', (r) => r.len, new Destruct.Spec({mode: Destruct.Mode.LE}) - .field('x', Destruct.UInt16) - .field('y', Destruct.UInt16) - .field('z', Destruct.UInt16)); +const spec = new Destruct.Spec({ mode: Destruct.Mode.LE }); +spec + .field("len", Destruct.UInt32) + .loop( + "points", + (r) => r.len, + new Destruct.Spec({ mode: Destruct.Mode.LE }) + .field("x", Destruct.UInt16) + .field("y", Destruct.UInt16) + .field("z", Destruct.UInt16) + ); // structron const PointsStruct = new Struct() - .addMember(Struct.TYPES.UINT_LE, 'len') + .addMember(Struct.TYPES.UINT_LE, "len") .addArray( - new Struct().addMember(Struct.TYPES.USHORT_LE, 'x') - .addMember(Struct.TYPES.USHORT_LE, 'y') - .addMember(Struct.TYPES.USHORT_LE, 'z'), - 'points', 0, 'len' + new Struct() + .addMember(Struct.TYPES.USHORT_LE, "x") + .addMember(Struct.TYPES.USHORT_LE, "y") + .addMember(Struct.TYPES.USHORT_LE, "z"), + "points", + 0, + "len" ); // Prepare input @@ -54,29 +57,29 @@ var buf = Buffer.alloc(4 + n * 2 * 3); buf.writeUInt32LE(n, 0); for (var i = 0; i < n; i++) { - buf.writeUInt16LE(123, i * 6 + 0 + 4); - buf.writeUInt16LE(456, i * 6 + 2 + 4); - buf.writeUInt16LE(789, i * 6 + 4 + 4); + buf.writeUInt16LE(123, i * 6 + 0 + 4); + buf.writeUInt16LE(456, i * 6 + 2 + 4); + buf.writeUInt16LE(789, i * 6 + 4 + 4); } // Run benchmarks suite -.add('binparse', function() { - const points = PointsParser.read(buf); -}) -.add('binary-parser', function() { - const points = Points.parse(buf); -}) -.add('destruct-js', function() { - const points = spec.read(buf); -}) -.add('structron', function() { - const points = PointsStruct.read(buf); -}) -.on('cycle', function(event) { + .add("binparse", function () { + const points = PointsParser.read(buf); + }) + .add("binary-parser", function () { + const points = Points.parse(buf); + }) + .add("destruct-js", function () { + const points = spec.read(buf); + }) + .add("structron", function () { + const points = PointsStruct.read(buf); + }) + .on("cycle", function (event) { console.log(String(event.target)); -}) -.on('complete', function() { - console.log('Fastest is ' + this.filter('fastest').map('name')); -}) -.run({ 'async': true }); + }) + .on("complete", function () { + console.log("Fastest is " + this.filter("fastest").map("name")); + }) + .run({ async: true }); diff --git a/example/bmp.js b/example/bmp.js index 92a8d9eb..3026bed3 100644 --- a/example/bmp.js +++ b/example/bmp.js @@ -1,4 +1,4 @@ -var Parser = require('../dist/binary_parser').Parser; +var Parser = require("../dist/binary_parser").Parser; // C structure BITMAPFILEHEADER // typedef struct tagBITMAPFILEHEADER { @@ -9,15 +9,15 @@ var Parser = require('../dist/binary_parser').Parser; // DWORD bfOffBits; // } BITMAPFILEHEADER, *PBITMAPFILEHEADER; var bmpFileHeader = new Parser() - .endianess('little') - .string('type', { + .endianess("little") + .string("type", { length: 2, - assert: 'BM', + assert: "BM", }) - .uint32('size') - .uint16('reserved1') - .uint16('reserved2') - .uint32('offBits'); + .uint32("size") + .uint16("reserved1") + .uint16("reserved2") + .uint32("offBits"); // C structure BITMAPINFOHEADER definition // typedef struct tagBITMAPINFOHEADER { @@ -34,27 +34,27 @@ var bmpFileHeader = new Parser() // DWORD biClrImportant; // } BITMAPINFOHEADER; var bmpInfoHeader = new Parser() - .endianess('little') - .uint32('size') - .int32('width') - .int32('height') - .uint16('planes') - .uint16('bitCount') - .uint32('compression') - .uint32('sizeImage') - .int32('xPelsPerMeter') - .int32('yPelsPerMeter') - .uint32('clrUsed') - .uint32('clrImportant'); + .endianess("little") + .uint32("size") + .int32("width") + .int32("height") + .uint16("planes") + .uint16("bitCount") + .uint32("compression") + .uint32("sizeImage") + .int32("xPelsPerMeter") + .int32("yPelsPerMeter") + .uint32("clrUsed") + .uint32("clrImportant"); var bmpFile = new Parser() - .nest('fileHeader', { + .nest("fileHeader", { type: bmpFileHeader, }) - .nest('infoHeader', { + .nest("infoHeader", { type: bmpInfoHeader, }); -require('fs').readFile('test.bmp', function (err, data) { +require("fs").readFile("test.bmp", function (err, data) { console.log(bmpFile.parse(data)); }); diff --git a/example/classfile.js b/example/classfile.js index 08126df9..329e10fa 100644 --- a/example/classfile.js +++ b/example/classfile.js @@ -1,55 +1,55 @@ -var Parser = require('../dist/binary_parser').Parser; +var Parser = require("../dist/binary_parser").Parser; -var ConstantClassInfo = Parser.start().uint16be('name_index'); +var ConstantClassInfo = Parser.start().uint16be("name_index"); var ConstantFieldrefInfo = Parser.start() - .uint16be('class_index') - .uint16be('name_and_type_index'); + .uint16be("class_index") + .uint16be("name_and_type_index"); var ConstantMethodrefInfo = Parser.start() - .uint16be('class_index') - .uint16be('name_and_type_index'); + .uint16be("class_index") + .uint16be("name_and_type_index"); var ConstantInterfaceMethodrefInfo = Parser.start() - .uint16be('class_index') - .uint16be('name_and_type_index'); + .uint16be("class_index") + .uint16be("name_and_type_index"); -var ConstantStringInfo = Parser.start().uint16be('string_index'); +var ConstantStringInfo = Parser.start().uint16be("string_index"); -var ConstantIntegerInfo = Parser.start().uint32be('bytes'); +var ConstantIntegerInfo = Parser.start().uint32be("bytes"); -var ConstantFloatInfo = Parser.start().uint32be('bytes'); +var ConstantFloatInfo = Parser.start().uint32be("bytes"); var ConstantLongInfo = Parser.start() - .uint32be('high_bytes') - .uint32be('low_bytes'); + .uint32be("high_bytes") + .uint32be("low_bytes"); var ConstantDoubleInfo = Parser.start() - .uint32be('high_bytes') - .uint32be('low_bytes'); + .uint32be("high_bytes") + .uint32be("low_bytes"); var ConstantNameAndTypeInfo = Parser.start() - .uint16be('name_index') - .uint16be('descriptor_index'); + .uint16be("name_index") + .uint16be("descriptor_index"); var ConstantUtf8Info = Parser.start() - .uint16be('len') - .string('bytes', { length: 'len' }); + .uint16be("len") + .string("bytes", { length: "len" }); var ConstantMethodHandleInfo = Parser.start() - .uint8('reference_kind') - .uint16be('reference_index'); + .uint8("reference_kind") + .uint16be("reference_index"); -var ConstantMethodTypeInfo = Parser.start().uint16be('descriptor_index'); +var ConstantMethodTypeInfo = Parser.start().uint16be("descriptor_index"); var ConstantInvokeDynamicInfo = Parser.start() - .uint16be('bootstrap_method_attr_index') - .uint16be('name_and_type_index'); + .uint16be("bootstrap_method_attr_index") + .uint16be("name_and_type_index"); var CpInfo = Parser.start() - .uint8('tag') - .choice('info', { - tag: 'tag', + .uint8("tag") + .choice("info", { + tag: "tag", choices: { 7: ConstantClassInfo, 9: ConstantFieldrefInfo, @@ -68,18 +68,18 @@ var CpInfo = Parser.start() }); var ClassFile = Parser.start() - .endianess('big') - .uint32('magic', { assert: 0xcafebabe }) - .uint16('minor_version') - .uint16('major_version') - .uint16('constant_pool_count') - .array('cp_info', { + .endianess("big") + .uint32("magic", { assert: 0xcafebabe }) + .uint16("minor_version") + .uint16("major_version") + .uint16("constant_pool_count") + .array("cp_info", { type: CpInfo, length: function () { return this.constant_pool_count - 1; }, }); -require('fs').readFile('Hello.class', function (err, data) { - console.log(require('util').inspect(ClassFile.parse(data), { depth: null })); +require("fs").readFile("Hello.class", function (err, data) { + console.log(require("util").inspect(ClassFile.parse(data), { depth: null })); }); diff --git a/example/elf32.js b/example/elf32.js index 314fe5e6..1920b90c 100644 --- a/example/elf32.js +++ b/example/elf32.js @@ -1,17 +1,17 @@ -var Parser = require('../dist/binary_parser').Parser; +var Parser = require("../dist/binary_parser").Parser; var ELF32ProgramHeader = new Parser() - .endianess('little') - .uint32('type') - .uint32('offset') - .uint32('vaddr') - .uint32('paddr') - .uint32('filesz') - .uint32('memsz') - .uint32('flags') - .uint32('align'); + .endianess("little") + .uint32("type") + .uint32("offset") + .uint32("vaddr") + .uint32("paddr") + .uint32("filesz") + .uint32("memsz") + .uint32("flags") + .uint32("align"); -var ELF32ProgramHeaderTable = new Parser().array('items', { +var ELF32ProgramHeaderTable = new Parser().array("items", { type: ELF32ProgramHeader, length: function (vars) { return vars.phnum; @@ -19,27 +19,27 @@ var ELF32ProgramHeaderTable = new Parser().array('items', { }); var ELF32SectionHeader = new Parser() - .endianess('little') - .uint32('name') - .uint32('type') - .uint32('flags') - .uint32('address') - .uint32('offset') - .uint32('size') - .uint32('link') - .uint32('info') - .uint32('addralign') - .uint32('entsize'); + .endianess("little") + .uint32("name") + .uint32("type") + .uint32("flags") + .uint32("address") + .uint32("offset") + .uint32("size") + .uint32("link") + .uint32("info") + .uint32("addralign") + .uint32("entsize"); -var ELF32SectionHeaderTable = new Parser().array('items', { +var ELF32SectionHeaderTable = new Parser().array("items", { type: ELF32SectionHeader, length: function (vars) { return vars.shnum; }, }); -var ELF32SectionHeaderStringTable = new Parser().seek(1).array('items', { - type: new Parser().string('name', { zeroTerminated: true }), +var ELF32SectionHeaderStringTable = new Parser().seek(1).array("items", { + type: new Parser().string("name", { zeroTerminated: true }), lengthInBytes: function (vars) { var shstr = vars.section_headers.items[vars.shstrndx]; return shstr.size - 1; @@ -47,30 +47,30 @@ var ELF32SectionHeaderStringTable = new Parser().seek(1).array('items', { }); var ELF32Header = new Parser() - .endianess('little') - .buffer('ident', { length: 16 }) - .uint16('type') - .uint16('machine') - .uint32('version') - .uint32('entry') - .uint32('phoff') - .uint32('shoff') - .uint32('flags') - .uint16('ehsize') - .uint16('phentsize') - .uint16('phnum') - .uint16('shentsize') - .uint16('shnum') - .uint16('shstrndx') - .pointer('program_headers', { + .endianess("little") + .buffer("ident", { length: 16 }) + .uint16("type") + .uint16("machine") + .uint32("version") + .uint32("entry") + .uint32("phoff") + .uint32("shoff") + .uint32("flags") + .uint16("ehsize") + .uint16("phentsize") + .uint16("phnum") + .uint16("shentsize") + .uint16("shnum") + .uint16("shstrndx") + .pointer("program_headers", { type: ELF32ProgramHeaderTable, - offset: 'phoff', + offset: "phoff", }) - .pointer('section_headers', { + .pointer("section_headers", { type: ELF32SectionHeaderTable, - offset: 'shoff', + offset: "shoff", }) - .pointer('strings', { + .pointer("strings", { type: ELF32SectionHeaderStringTable, offset: function () { var shstr = vars.section_headers.items[vars.shstrndx]; @@ -78,7 +78,7 @@ var ELF32Header = new Parser() }, }); -require('fs').readFile('hello', function (err, data) { +require("fs").readFile("hello", function (err, data) { var result = ELF32Header.parse(data); - console.log(require('util').inspect(result, { depth: null })); + console.log(require("util").inspect(result, { depth: null })); }); diff --git a/example/ip.js b/example/ip.js index b3c9cee7..15a35213 100644 --- a/example/ip.js +++ b/example/ip.js @@ -1,26 +1,26 @@ -var Parser = require('../dist/binary_parser').Parser; +var Parser = require("../dist/binary_parser").Parser; var ipHeader = new Parser() - .endianess('big') - .bit4('version') - .bit4('headerLength') - .uint8('tos') - .uint16('packetLength') - .uint16('id') - .bit3('offset') - .bit13('fragOffset') - .uint8('ttl') - .uint8('protocol') - .uint16('checksum') - .array('src', { - type: 'uint8', + .endianess("big") + .bit4("version") + .bit4("headerLength") + .uint8("tos") + .uint16("packetLength") + .uint16("id") + .bit3("offset") + .bit13("fragOffset") + .uint8("ttl") + .uint8("protocol") + .uint16("checksum") + .array("src", { + type: "uint8", length: 4, }) - .array('dst', { - type: 'uint8', + .array("dst", { + type: "uint8", length: 4, }); -var buf = Buffer.from('450002c5939900002c06ef98adc24f6c850186d1', 'hex'); +var buf = Buffer.from("450002c5939900002c06ef98adc24f6c850186d1", "hex"); console.log(ipHeader.parse(buf)); diff --git a/example/jpg.js b/example/jpg.js index 87d3208d..4227c4bd 100644 --- a/example/jpg.js +++ b/example/jpg.js @@ -1,58 +1,58 @@ -var Parser = require('../dist/binary_parser').Parser; +var Parser = require("../dist/binary_parser").Parser; var SOI = Parser.start(); var EOI = Parser.start(); var APP0 = Parser.start() - .endianess('big') - .uint16('length') - .string('id', { - encoding: 'ascii', + .endianess("big") + .uint16("length") + .string("id", { + encoding: "ascii", zeroTerminated: true, - assert: 'JFIF', + assert: "JFIF", }) - .uint16('version') - .uint8('unit') - .uint16('xDensity') - .uint16('yDensity') - .uint8('thumbWidth') - .uint8('thumbHeight') - .array('thumbData', { - type: 'uint8', + .uint16("version") + .uint8("unit") + .uint16("xDensity") + .uint16("yDensity") + .uint8("thumbWidth") + .uint8("thumbHeight") + .array("thumbData", { + type: "uint8", length: function () { return this.Xt * this.Yt * 3; }, }); var COM = Parser.start() - .endianess('big') - .uint16('length') - .string('comment', { - encoding: 'ascii', + .endianess("big") + .uint16("length") + .string("comment", { + encoding: "ascii", length: function () { return this.length - 2; }, }); var SOS = Parser.start() - .endianess('big') - .uint16('length') - .uint8('componentCount') - .array('components', { - type: Parser.start().uint8('id').uint8('dht'), - length: 'componentCount', + .endianess("big") + .uint16("length") + .uint8("componentCount") + .array("components", { + type: Parser.start().uint8("id").uint8("dht"), + length: "componentCount", }) - .uint8('spectrumStart') - .uint8('spectrumEnd') - .uint8('spectrumSelect'); + .uint8("spectrumStart") + .uint8("spectrumEnd") + .uint8("spectrumSelect"); var DQT = Parser.start() - .endianess('big') - .uint16('length') - .array('tables', { - type: Parser.start().uint8('precisionAndTableId').array('table', { - type: 'uint8', + .endianess("big") + .uint16("length") + .array("tables", { + type: Parser.start().uint8("precisionAndTableId").array("table", { + type: "uint8", length: 64, }), length: function () { @@ -61,32 +61,32 @@ var DQT = Parser.start() }); var SOF0 = Parser.start() - .endianess('big') - .uint16('length') - .uint8('precision') - .uint16('width') - .uint16('height') - .uint8('componentCount') - .array('components', { + .endianess("big") + .uint16("length") + .uint8("precision") + .uint16("width") + .uint16("height") + .uint8("componentCount") + .array("components", { type: Parser.start() - .uint8('id') - .uint8('samplingFactor') - .uint8('quantizationTableId'), - length: 'componentCount', + .uint8("id") + .uint8("samplingFactor") + .uint8("quantizationTableId"), + length: "componentCount", }); var Ignore = Parser.start() - .endianess('big') - .uint16('length') + .endianess("big") + .uint16("length") .seek(function () { return this.length - 2; }); var Segment = Parser.start() - .endianess('big') - .uint16('marker') - .choice('segment', { - tag: 'marker', + .endianess("big") + .uint16("marker") + .choice("segment", { + tag: "marker", choices: { 0xffd8: SOI, 0xffd9: EOI, @@ -98,11 +98,11 @@ var Segment = Parser.start() defaultChoice: Ignore, }); -var JPEG = Parser.start().array('segments', { +var JPEG = Parser.start().array("segments", { type: Segment, - readUntil: 'eof', + readUntil: "eof", }); -require('fs').readFile('test.jpg', function (err, data) { - console.log(require('util').inspect(JPEG.parse(data), { depth: null })); +require("fs").readFile("test.jpg", function (err, data) { + console.log(require("util").inspect(JPEG.parse(data), { depth: null })); }); diff --git a/example/mbr.js b/example/mbr.js index 374efbb7..ad09539b 100644 --- a/example/mbr.js +++ b/example/mbr.js @@ -1,5 +1,5 @@ -var Parser = require('../dist/binary_parser').Parser; -var fs = require('fs'); +var Parser = require("../dist/binary_parser").Parser; +var fs = require("fs"); var chs = new Parser({ formatter: function (val) { @@ -7,41 +7,41 @@ var chs = new Parser({ return val; }, }) - .uint8('head') - .bit2('cylinderHigh') - .bit6('sector') - .uint8('cylinder'); + .uint8("head") + .bit2("cylinderHigh") + .bit6("sector") + .uint8("cylinder"); var partitionTable = new Parser() - .uint8('bootFlag') - .nest('startCHS', { + .uint8("bootFlag") + .nest("startCHS", { type: chs, formatter: function (val) { delete val.cylinderHigh; return val; }, }) - .uint8('type') - .nest('endCHS', { + .uint8("type") + .nest("endCHS", { type: chs, formatter: function (val) { delete val.cylinderHigh; return val; }, }) - .uint32le('startLBA') - .uint32le('endLBA'); + .uint32le("startLBA") + .uint32le("endLBA"); var mbrParser = new Parser() .seek(446) - .array('partitionTables', { + .array("partitionTables", { type: partitionTable, length: 4, }) - .int16be('signature', { + .int16be("signature", { assert: 0x55aa, }); -fs.readFile('raspbian.img', function (err, data) { +fs.readFile("raspbian.img", function (err, data) { console.dir(mbrParser.parse(data), { depth: null, colors: true }); }); diff --git a/example/tar.js b/example/tar.js index ff3531ca..b0ec1db0 100644 --- a/example/tar.js +++ b/example/tar.js @@ -1,27 +1,27 @@ -var Parser = require('../dist/binary_parser').Parser; -var fs = require('fs'); +var Parser = require("../dist/binary_parser").Parser; +var fs = require("fs"); var oct2int = function (s) { return parseInt(s, 8); }; var tarHeader = new Parser() - .string('name', { length: 100, stripNull: true }) - .string('mode', { length: 8, stripNull: true, formatter: oct2int }) - .string('uid', { length: 8, stripNull: true, formatter: oct2int }) - .string('gid', { length: 8, stripNull: true, formatter: oct2int }) - .string('size', { length: 12, stripNull: true, formatter: oct2int }) - .string('mtime', { length: 12, stripNull: true, formatter: oct2int }) - .string('chksum', { length: 8, stripNull: true, formatter: oct2int }) - .string('typeflag', { length: 1, stripNull: true, formatter: oct2int }) - .string('linkname', { length: 100, stripNull: true }) - .string('magic', { length: 6, stripNull: true }) - .string('version', { length: 2, stripNull: true, formatter: oct2int }) - .string('uname', { length: 32, stripNull: true }) - .string('gname', { length: 32, stripNull: true }) - .string('devmajor', { length: 8, stripNull: true, formatter: oct2int }) - .string('devminor', { length: 8, stripNull: true, formatter: oct2int }) - .string('prefix', { length: 155, stripNull: true }) + .string("name", { length: 100, stripNull: true }) + .string("mode", { length: 8, stripNull: true, formatter: oct2int }) + .string("uid", { length: 8, stripNull: true, formatter: oct2int }) + .string("gid", { length: 8, stripNull: true, formatter: oct2int }) + .string("size", { length: 12, stripNull: true, formatter: oct2int }) + .string("mtime", { length: 12, stripNull: true, formatter: oct2int }) + .string("chksum", { length: 8, stripNull: true, formatter: oct2int }) + .string("typeflag", { length: 1, stripNull: true, formatter: oct2int }) + .string("linkname", { length: 100, stripNull: true }) + .string("magic", { length: 6, stripNull: true }) + .string("version", { length: 2, stripNull: true, formatter: oct2int }) + .string("uname", { length: 32, stripNull: true }) + .string("gname", { length: 32, stripNull: true }) + .string("devmajor", { length: 8, stripNull: true, formatter: oct2int }) + .string("devminor", { length: 8, stripNull: true, formatter: oct2int }) + .string("prefix", { length: 155, stripNull: true }) .seek(12); var tarItem = new Parser() @@ -32,11 +32,11 @@ var tarItem = new Parser() return Math.ceil(this.size / 512) * 512; }); -var tarArchive = new Parser().array('files', { +var tarArchive = new Parser().array("files", { type: tarItem, - readUntil: 'eof', + readUntil: "eof", }); -fs.readFile('test.tar', function (err, data) { +fs.readFile("test.tar", function (err, data) { console.dir(tarArchive.parse(data), { depth: null, colors: true }); }); diff --git a/example/tcp.js b/example/tcp.js index 95e8ca78..6178403f 100644 --- a/example/tcp.js +++ b/example/tcp.js @@ -1,29 +1,29 @@ -var Parser = require('../dist/binary_parser').Parser; +var Parser = require("../dist/binary_parser").Parser; var tcpHeader = new Parser() - .endianess('big') - .uint16('srcPort') - .uint16('dstPort') - .uint32('seq') - .uint32('ack') - .bit4('dataOffset') - .bit6('reserved') - .nest('flags', { + .endianess("big") + .uint16("srcPort") + .uint16("dstPort") + .uint32("seq") + .uint32("ack") + .bit4("dataOffset") + .bit6("reserved") + .nest("flags", { type: new Parser() - .bit1('urg') - .bit1('ack') - .bit1('psh') - .bit1('rst') - .bit1('syn') - .bit1('fin'), + .bit1("urg") + .bit1("ack") + .bit1("psh") + .bit1("rst") + .bit1("syn") + .bit1("fin"), }) - .uint16('windowSize') - .uint16('checksum') - .uint16('urgentPointer'); + .uint16("windowSize") + .uint16("checksum") + .uint16("urgentPointer"); var buf = Buffer.from( - 'e8a203e108e177e13d20756b801829d3004100000101080a2ea486ba793310bc', - 'hex' + "e8a203e108e177e13d20756b801829d3004100000101080a2ea486ba793310bc", + "hex" ); console.log(tcpHeader.parse(buf)); diff --git a/lib/binary_parser.ts b/lib/binary_parser.ts index 5129a28b..4dfac535 100644 --- a/lib/binary_parser.ts +++ b/lib/binary_parser.ts @@ -1,8 +1,8 @@ -import 'fast-text-encoding'; -import { Context } from './context'; +import "fast-text-encoding"; +import { Context } from "./context"; const aliasRegistry: { [key: string]: Parser } = {}; -const FUNCTION_PREFIX = '___parser_'; +const FUNCTION_PREFIX = "___parser_"; interface ParserOptions { length?: number | string | ((item: any) => number); @@ -11,7 +11,7 @@ interface ParserOptions { type?: string | Parser; formatter?: (item: any) => any; encoding?: string; - readUntil?: 'eof' | ((item: any, buffer: Buffer) => boolean); + readUntil?: "eof" | ((item: any, buffer: Buffer) => boolean); greedy?: boolean; choices?: { [key: number]: string | Parser }; defaultChoice?: string | Parser; @@ -27,49 +27,49 @@ interface ParserOptions { type Types = PrimitiveTypes | ComplexTypes; type ComplexTypes = - | 'bit' - | 'string' - | 'buffer' - | 'array' - | 'choice' - | 'nest' - | 'seek' - | 'pointer' - | 'saveOffset' - | 'wrapper' - | ''; - -type Endianess = 'be' | 'le'; + | "bit" + | "string" + | "buffer" + | "array" + | "choice" + | "nest" + | "seek" + | "pointer" + | "saveOffset" + | "wrapper" + | ""; + +type Endianess = "be" | "le"; type PrimitiveTypes = - | 'uint8' - | 'uint16le' - | 'uint16be' - | 'uint32le' - | 'uint32be' - | 'uint64le' - | 'uint64be' - | 'int8' - | 'int16le' - | 'int16be' - | 'int32le' - | 'int32be' - | 'int64le' - | 'int64be' - | 'floatle' - | 'floatbe' - | 'doublele' - | 'doublebe'; + | "uint8" + | "uint16le" + | "uint16be" + | "uint32le" + | "uint32be" + | "uint64le" + | "uint64be" + | "int8" + | "int16le" + | "int16be" + | "int32le" + | "int32be" + | "int64le" + | "int64be" + | "floatle" + | "floatbe" + | "doublele" + | "doublebe"; type PrimitiveTypesWithoutEndian = - | 'uint8' - | 'uint16' - | 'uint32' - | 'int8' - | 'int16' - | 'int32' - | 'int64' - | 'uint64'; + | "uint8" + | "uint16" + | "uint32" + | "int8" + | "int16" + | "int32" + | "int64" + | "uint64"; type BitSizes = | 1 @@ -127,24 +127,24 @@ const PRIMITIVE_SIZES: { [key in PrimitiveTypes]: number } = { }; const PRIMITIVE_NAMES: { [key in PrimitiveTypes]: string } = { - uint8: 'Uint8', - uint16le: 'Uint16', - uint16be: 'Uint16', - uint32le: 'Uint32', - uint32be: 'Uint32', - int8: 'Int8', - int16le: 'Int16', - int16be: 'Int16', - int32le: 'Int32', - int32be: 'Int32', - int64be: 'BigInt64', - int64le: 'BigInt64', - uint64be: 'BigUint64', - uint64le: 'BigUint64', - floatle: 'Float32', - floatbe: 'Float32', - doublele: 'Float64', - doublebe: 'Float64', + uint8: "Uint8", + uint16le: "Uint16", + uint16be: "Uint16", + uint32le: "Uint32", + uint32be: "Uint32", + int8: "Int8", + int16le: "Int16", + int16be: "Int16", + int32le: "Int32", + int32be: "Int32", + int64be: "BigInt64", + int64le: "BigInt64", + uint64be: "BigUint64", + uint64le: "BigUint64", + floatle: "Float32", + floatbe: "Float32", + doublele: "Float64", + doublebe: "Float64", }; const PRIMITIVE_LITTLE_ENDIANS: { [key in PrimitiveTypes]: boolean } = { @@ -169,13 +169,13 @@ const PRIMITIVE_LITTLE_ENDIANS: { [key in PrimitiveTypes]: boolean } = { }; export class Parser { - varName = ''; - type: Types = ''; + varName = ""; + type: Types = ""; options: ParserOptions = {}; next: Parser | null = null; head: Parser | null = null; compiled: Function | null = null; - endian: Endianess = 'be'; + endian: Endianess = "be"; constructorFn: Function | null = null; alias: string | null = null; useContextVariables: boolean = false; @@ -210,110 +210,110 @@ export class Parser { } uint8(varName: string, options?: ParserOptions) { - return this.primitiveN('uint8', varName, options); + return this.primitiveN("uint8", varName, options); } uint16(varName: string, options?: ParserOptions) { - return this.primitiveN(this.useThisEndian('uint16'), varName, options); + return this.primitiveN(this.useThisEndian("uint16"), varName, options); } uint16le(varName: string, options?: ParserOptions) { - return this.primitiveN('uint16le', varName, options); + return this.primitiveN("uint16le", varName, options); } uint16be(varName: string, options?: ParserOptions) { - return this.primitiveN('uint16be', varName, options); + return this.primitiveN("uint16be", varName, options); } uint32(varName: string, options?: ParserOptions) { - return this.primitiveN(this.useThisEndian('uint32'), varName, options); + return this.primitiveN(this.useThisEndian("uint32"), varName, options); } uint32le(varName: string, options?: ParserOptions) { - return this.primitiveN('uint32le', varName, options); + return this.primitiveN("uint32le", varName, options); } uint32be(varName: string, options?: ParserOptions) { - return this.primitiveN('uint32be', varName, options); + return this.primitiveN("uint32be", varName, options); } int8(varName: string, options?: ParserOptions) { - return this.primitiveN('int8', varName, options); + return this.primitiveN("int8", varName, options); } int16(varName: string, options?: ParserOptions) { - return this.primitiveN(this.useThisEndian('int16'), varName, options); + return this.primitiveN(this.useThisEndian("int16"), varName, options); } int16le(varName: string, options?: ParserOptions) { - return this.primitiveN('int16le', varName, options); + return this.primitiveN("int16le", varName, options); } int16be(varName: string, options?: ParserOptions) { - return this.primitiveN('int16be', varName, options); + return this.primitiveN("int16be", varName, options); } int32(varName: string, options?: ParserOptions) { - return this.primitiveN(this.useThisEndian('int32'), varName, options); + return this.primitiveN(this.useThisEndian("int32"), varName, options); } int32le(varName: string, options?: ParserOptions) { - return this.primitiveN('int32le', varName, options); + return this.primitiveN("int32le", varName, options); } int32be(varName: string, options?: ParserOptions) { - return this.primitiveN('int32be', varName, options); + return this.primitiveN("int32be", varName, options); } private bigIntVersionCheck() { if (!DataView.prototype.getBigInt64) - throw new Error('BigInt64 is unsupported in this runtime'); + throw new Error("BigInt64 is unsupported in this runtime"); } int64(varName: string, options?: ParserOptions) { this.bigIntVersionCheck(); - return this.primitiveN(this.useThisEndian('int64'), varName, options); + return this.primitiveN(this.useThisEndian("int64"), varName, options); } int64be(varName: string, options?: ParserOptions) { this.bigIntVersionCheck(); - return this.primitiveN('int64be', varName, options); + return this.primitiveN("int64be", varName, options); } int64le(varName: string, options?: ParserOptions) { this.bigIntVersionCheck(); - return this.primitiveN('int64le', varName, options); + return this.primitiveN("int64le", varName, options); } uint64(varName: string, options?: ParserOptions) { this.bigIntVersionCheck(); - return this.primitiveN(this.useThisEndian('uint64'), varName, options); + return this.primitiveN(this.useThisEndian("uint64"), varName, options); } uint64be(varName: string, options?: ParserOptions) { this.bigIntVersionCheck(); - return this.primitiveN('uint64be', varName, options); + return this.primitiveN("uint64be", varName, options); } uint64le(varName: string, options?: ParserOptions) { this.bigIntVersionCheck(); - return this.primitiveN('uint64le', varName, options); + return this.primitiveN("uint64le", varName, options); } floatle(varName: string, options?: ParserOptions) { - return this.primitiveN('floatle', varName, options); + return this.primitiveN("floatle", varName, options); } floatbe(varName: string, options?: ParserOptions) { - return this.primitiveN('floatbe', varName, options); + return this.primitiveN("floatbe", varName, options); } doublele(varName: string, options?: ParserOptions) { - return this.primitiveN('doublele', varName, options); + return this.primitiveN("doublele", varName, options); } doublebe(varName: string, options?: ParserOptions) { - return this.primitiveN('doublebe', varName, options); + return this.primitiveN("doublebe", varName, options); } private bitN(size: BitSizes, varName: string, options?: ParserOptions) { @@ -321,7 +321,7 @@ export class Parser { options = {}; } options.length = size; - return this.setNextParser('bit', varName, options); + return this.setNextParser("bit", varName, options); } bit1(varName: string, options?: ParserOptions) { @@ -458,70 +458,70 @@ export class Parser { return this; } - skip(length: ParserOptions['length'], options?: ParserOptions) { + skip(length: ParserOptions["length"], options?: ParserOptions) { return this.seek(length, options); } - seek(relOffset: ParserOptions['length'], options?: ParserOptions) { + seek(relOffset: ParserOptions["length"], options?: ParserOptions) { if (options && options.assert) { - throw new Error('assert option on seek is not allowed.'); + throw new Error("assert option on seek is not allowed."); } - return this.setNextParser('seek', '', { length: relOffset }); + return this.setNextParser("seek", "", { length: relOffset }); } string(varName: string, options: ParserOptions) { if (!options.zeroTerminated && !options.length && !options.greedy) { throw new Error( - 'Neither length, zeroTerminated, nor greedy is defined for string.' + "Neither length, zeroTerminated, nor greedy is defined for string." ); } if ((options.zeroTerminated || options.length) && options.greedy) { throw new Error( - 'greedy is mutually exclusive with length and zeroTerminated for string.' + "greedy is mutually exclusive with length and zeroTerminated for string." ); } if (options.stripNull && !(options.length || options.greedy)) { throw new Error( - 'Length or greedy must be defined if stripNull is defined.' + "Length or greedy must be defined if stripNull is defined." ); } - options.encoding = options.encoding || 'utf8'; + options.encoding = options.encoding || "utf8"; - return this.setNextParser('string', varName, options); + return this.setNextParser("string", varName, options); } buffer(varName: string, options: ParserOptions) { if (!options.length && !options.readUntil) { - throw new Error('Length nor readUntil is defined in buffer parser'); + throw new Error("Length nor readUntil is defined in buffer parser"); } - return this.setNextParser('buffer', varName, options); + return this.setNextParser("buffer", varName, options); } wrapped(varName: string, options: ParserOptions) { if (!options.length && !options.readUntil) { - throw new Error('Length nor readUntil is defined in buffer parser'); + throw new Error("Length nor readUntil is defined in buffer parser"); } if (!options.wrapper || !options.type) { throw new Error( - 'Both wrapper and type must be defined in wrapper parser' + "Both wrapper and type must be defined in wrapper parser" ); } - return this.setNextParser('wrapper', varName, options); + return this.setNextParser("wrapper", varName, options); } array(varName: string, options: ParserOptions) { if (!options.readUntil && !options.length && !options.lengthInBytes) { - throw new Error('Length option of array is not defined.'); + throw new Error("Length option of array is not defined."); } if (!options.type) { - throw new Error('Type option of array is not defined.'); + throw new Error("Type option of array is not defined."); } if ( - typeof options.type === 'string' && + typeof options.type === "string" && !aliasRegistry[options.type] && Object.keys(PRIMITIVE_SIZES).indexOf(options.type) < 0 ) { @@ -530,20 +530,20 @@ export class Parser { ); } - return this.setNextParser('array', varName, options); + return this.setNextParser("array", varName, options); } choice(varName: string | ParserOptions, options?: ParserOptions) { - if (typeof options !== 'object' && typeof varName === 'object') { + if (typeof options !== "object" && typeof varName === "object") { options = varName; varName = null; } if (!options.tag) { - throw new Error('Tag option of array is not defined.'); + throw new Error("Tag option of array is not defined."); } if (!options.choices) { - throw new Error('Choices option of array is not defined.'); + throw new Error("Choices option of array is not defined."); } Object.keys(options.choices).forEach((keyString: string) => { @@ -551,7 +551,7 @@ export class Parser { const value = options.choices[key]; if (isNaN(key)) { - throw new Error('Key of choices must be a number.'); + throw new Error("Key of choices must be a number."); } if (!value) { @@ -559,7 +559,7 @@ export class Parser { } if ( - typeof value === 'string' && + typeof value === "string" && !aliasRegistry[value] && Object.keys(PRIMITIVE_SIZES).indexOf(value) < 0 ) { @@ -569,38 +569,38 @@ export class Parser { } }); - return this.setNextParser('choice', varName as string, options); + return this.setNextParser("choice", varName as string, options); } nest(varName: string | ParserOptions, options?: ParserOptions) { - if (typeof options !== 'object' && typeof varName === 'object') { + if (typeof options !== "object" && typeof varName === "object") { options = varName; varName = null; } if (!options.type) { - throw new Error('Type option of nest is not defined.'); + throw new Error("Type option of nest is not defined."); } if (!(options.type instanceof Parser) && !aliasRegistry[options.type]) { - throw new Error('Type option of nest must be a Parser object.'); + throw new Error("Type option of nest must be a Parser object."); } if (!(options.type instanceof Parser) && !varName) { throw new Error( - 'options.type must be a object if variable name is omitted.' + "options.type must be a object if variable name is omitted." ); } - return this.setNextParser('nest', varName as string, options); + return this.setNextParser("nest", varName as string, options); } pointer(varName: string, options?: ParserOptions) { if (!options.offset) { - throw new Error('Offset option of pointer is not defined.'); + throw new Error("Offset option of pointer is not defined."); } if (!options.type) { - throw new Error('Type option of pointer is not defined.'); - } else if (typeof options.type === 'string') { + throw new Error("Type option of pointer is not defined."); + } else if (typeof options.type === "string") { if ( Object.keys(PRIMITIVE_SIZES).indexOf(options.type) < 0 && !aliasRegistry[options.type] @@ -612,24 +612,24 @@ export class Parser { } else if (options.type instanceof Parser) { } else { throw new Error( - 'Type option of pointer must be a string or a Parser object.' + "Type option of pointer must be a string or a Parser object." ); } - return this.setNextParser('pointer', varName, options); + return this.setNextParser("pointer", varName, options); } saveOffset(varName: string, options?: ParserOptions) { - return this.setNextParser('saveOffset', varName, options); + return this.setNextParser("saveOffset", varName, options); } - endianess(endianess: 'little' | 'big') { + endianess(endianess: "little" | "big") { switch (endianess.toLowerCase()) { - case 'little': - this.endian = 'le'; + case "little": + this.endian = "le"; break; - case 'big': - this.endian = 'be'; + case "big": + this.endian = "be"; break; default: throw new Error(`Invalid endianess: ${endianess}`); @@ -646,7 +646,7 @@ export class Parser { create(constructorFn: Function) { if (!(constructorFn instanceof Function)) { - throw new Error('Constructor must be a Function object.'); + throw new Error("Constructor must be a Function object."); } this.constructorFn = constructorFn; @@ -658,7 +658,7 @@ export class Parser { const ctx = new Context(importPath, this.useContextVariables); ctx.pushCode( - 'var dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);' + "var dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);" ); if (!this.alias) { @@ -676,30 +676,30 @@ export class Parser { } private addRawCode(ctx: Context) { - ctx.pushCode('var offset = 0;'); + ctx.pushCode("var offset = 0;"); ctx.pushCode( - `var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};` + `var vars = ${this.constructorFn ? "new constructorFn()" : "{}"};` ); - ctx.pushCode('vars.$parent = null;'); - ctx.pushCode('vars.$root = vars;'); + ctx.pushCode("vars.$parent = null;"); + ctx.pushCode("vars.$root = vars;"); this.generate(ctx); this.resolveReferences(ctx); - ctx.pushCode('delete vars.$parent;'); - ctx.pushCode('delete vars.$root;'); + ctx.pushCode("delete vars.$parent;"); + ctx.pushCode("delete vars.$root;"); - ctx.pushCode('return vars;'); + ctx.pushCode("return vars;"); } private addAliasedCode(ctx: Context) { ctx.pushCode(`function ${FUNCTION_PREFIX + this.alias}(offset, context) {`); ctx.pushCode( - `var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};` + `var vars = ${this.constructorFn ? "new constructorFn()" : "{}"};` ); ctx.pushCode( - 'var ctx = Object.assign({$parent: null, $root: vars}, context || {});' + "var ctx = Object.assign({$parent: null, $root: vars}, context || {});" ); ctx.pushCode(`vars = Object.assign(vars, ctx);`); @@ -709,10 +709,10 @@ export class Parser { this.resolveReferences(ctx); ctx.pushCode( - 'Object.keys(ctx).forEach(function (item) { delete vars[item]; });' + "Object.keys(ctx).forEach(function (item) { delete vars[item]; });" ); - ctx.pushCode('return { offset: offset, result: vars };'); - ctx.pushCode('}'); + ctx.pushCode("return { offset: offset, result: vars };"); + ctx.pushCode("}"); return ctx; } @@ -727,11 +727,11 @@ export class Parser { } compile() { - const importPath = 'imports'; + const importPath = "imports"; const ctx = this.getContext(importPath); this.compiled = new Function( importPath, - 'TextDecoder', + "TextDecoder", `return function (buffer, constructorFn) { ${ctx.code} };` )(ctx.imports, TextDecoder); } @@ -744,25 +744,25 @@ export class Parser { // if this is a fixed length string } else if ( - this.type === 'string' && - typeof this.options.length === 'number' + this.type === "string" && + typeof this.options.length === "number" ) { size = this.options.length; // if this is a fixed length buffer } else if ( - this.type === 'buffer' && - typeof this.options.length === 'number' + this.type === "buffer" && + 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' + this.type === "array" && + typeof this.options.length === "number" ) { let elementSize = NaN; - if (typeof this.options.type === 'string') { + if (typeof this.options.type === "string") { elementSize = PRIMITIVE_SIZES[this.options.type as PrimitiveTypes]; } else if (this.options.type instanceof Parser) { elementSize = this.options.type.sizeOf(); @@ -770,11 +770,11 @@ export class Parser { size = this.options.length * elementSize; // if this a skip - } else if (this.type === 'seek') { + } else if (this.type === "seek") { size = this.options.length as number; // if this is a nested parser - } else if (this.type === 'nest') { + } else if (this.type === "nest") { size = (this.options.type as Parser).sizeOf(); } else if (!this.type) { size = 0; @@ -818,54 +818,54 @@ export class Parser { private generate(ctx: Context) { if (this.type) { switch (this.type) { - case 'uint8': - case 'uint16le': - case 'uint16be': - case 'uint32le': - case 'uint32be': - case 'int8': - case 'int16le': - case 'int16be': - case 'int32le': - case 'int32be': - case 'int64be': - case 'int64le': - case 'uint64be': - case 'uint64le': - case 'floatle': - case 'floatbe': - case 'doublele': - case 'doublebe': + case "uint8": + case "uint16le": + case "uint16be": + case "uint32le": + case "uint32be": + case "int8": + case "int16le": + case "int16be": + case "int32le": + case "int32be": + case "int64be": + case "int64le": + case "uint64be": + case "uint64le": + case "floatle": + case "floatbe": + case "doublele": + case "doublebe": this.primitiveGenerateN(this.type, ctx); break; - case 'bit': + case "bit": this.generateBit(ctx); break; - case 'string': + case "string": this.generateString(ctx); break; - case 'buffer': + case "buffer": this.generateBuffer(ctx); break; - case 'seek': + case "seek": this.generateSeek(ctx); break; - case 'nest': + case "nest": this.generateNest(ctx); break; - case 'array': + case "array": this.generateArray(ctx); break; - case 'choice': + case "choice": this.generateChoice(ctx); break; - case 'pointer': + case "pointer": this.generatePointer(ctx); break; - case 'saveOffset': + case "saveOffset": this.generateSaveOffset(ctx); break; - case 'wrapper': + case "wrapper": this.generateWrapper(ctx); break; } @@ -888,25 +888,25 @@ export class Parser { const varName = ctx.generateVariable(this.varName); switch (typeof this.options.assert) { - case 'function': + case "function": const func = ctx.addImport(this.options.assert); ctx.pushCode(`if (!${func}.call(vars, ${varName})) {`); break; - case 'number': + case "number": ctx.pushCode(`if (${this.options.assert} !== ${varName}) {`); break; - case 'string': + case "string": ctx.pushCode(`if ("${this.options.assert}" !== ${varName}) {`); break; default: throw new Error( - 'Assert option supports only strings, numbers and assert functions.' + "Assert option supports only strings, numbers and assert functions." ); } ctx.generateError( `"Assert error: ${varName} is " + ${this.options.assert}` ); - ctx.pushCode('}'); + ctx.pushCode("}"); } // Recursively call code generators and append results @@ -926,7 +926,7 @@ export class Parser { if ( !this.next || - (this.next && ['bit', 'nest'].indexOf(this.next.type) < 0) + (this.next && ["bit", "nest"].indexOf(this.next.type) < 0) ) { let sum = 0; ctx.bitFields.forEach( @@ -953,13 +953,13 @@ export class Parser { sum = 32; } else { throw new Error( - 'Currently, bit field sequence longer than 4-bytes is not supported.' + "Currently, bit field sequence longer than 4-bytes is not supported." ); } ctx.pushCode(`offset += ${sum / 8};`); let bitOffset = 0; - const isBigEndian = this.endian === 'be'; + const isBigEndian = this.endian === "be"; ctx.bitFields.forEach((parser) => { const length = parser.options.length as number; @@ -983,7 +983,7 @@ export class Parser { const name = ctx.generateVariable(this.varName); const start = ctx.generateTmpVariable(); const encoding = this.options.encoding; - const isHex = encoding.toLowerCase() === 'hex'; + const isHex = encoding.toLowerCase() === "hex"; const toHex = 'b => b.toString(16).padStart(2, "0")'; if (this.options.length && this.options.zeroTerminated) { @@ -1008,7 +1008,7 @@ export class Parser { ctx.pushCode(`offset += ${len};`); } else if (this.options.zeroTerminated) { ctx.pushCode(`var ${start} = offset;`); - ctx.pushCode('while(dataView.getUint8(offset++) !== 0);'); + ctx.pushCode("while(dataView.getUint8(offset++) !== 0);"); ctx.pushCode( isHex ? `${name} = Array.from(buffer.subarray(${start}, offset - 1), ${toHex}).join('');` @@ -1016,7 +1016,7 @@ export class Parser { ); } else if (this.options.greedy) { ctx.pushCode(`var ${start} = offset;`); - ctx.pushCode('while(buffer.length > offset++);'); + ctx.pushCode("while(buffer.length > offset++);"); ctx.pushCode( isHex ? `${name} = Array.from(buffer.subarray(${start}, offset), ${toHex}).join('');` @@ -1031,7 +1031,7 @@ export class Parser { private generateBuffer(ctx: Context) { const varName = ctx.generateVariable(this.varName); - if (typeof this.options.readUntil === 'function') { + if (typeof this.options.readUntil === "function") { const pred = this.options.readUntil; const start = ctx.generateTmpVariable(); const cur = ctx.generateTmpVariable(); @@ -1047,7 +1047,7 @@ export class Parser { ctx.pushCode(`offset += 1;`); ctx.pushCode(`}`); ctx.pushCode(`${varName} = buffer.subarray(${start}, offset);`); - } else if (this.options.readUntil === 'eof') { + } else if (this.options.readUntil === "eof") { ctx.pushCode(`${varName} = buffer.subarray(offset);`); } else { const len = ctx.generateOption(this.options.length); @@ -1069,16 +1069,16 @@ export class Parser { const lhs = ctx.generateVariable(this.varName); const item = ctx.generateTmpVariable(); const key = this.options.key; - const isHash = typeof key === 'string'; + const isHash = typeof key === "string"; if (isHash) { ctx.pushCode(`${lhs} = {};`); } else { ctx.pushCode(`${lhs} = [];`); } - if (typeof this.options.readUntil === 'function') { - ctx.pushCode('do {'); - } else if (this.options.readUntil === 'eof') { + if (typeof this.options.readUntil === "function") { + ctx.pushCode("do {"); + } else if (this.options.readUntil === "eof") { ctx.pushCode( `for (var ${counter} = 0; offset < buffer.length; ${counter}++) {` ); @@ -1092,7 +1092,7 @@ export class Parser { ); } - if (typeof type === 'string') { + if (typeof type === "string") { if (!aliasRegistry[type]) { const typeName = PRIMITIVE_NAMES[type as PrimitiveTypes]; const littleEndian = PRIMITIVE_LITTLE_ENDIANS[type as PrimitiveTypes]; @@ -1146,9 +1146,9 @@ export class Parser { ctx.pushCode(`${lhs}.push(${item});`); } - ctx.pushCode('}'); + ctx.pushCode("}"); - if (typeof this.options.readUntil === 'function') { + if (typeof this.options.readUntil === "function") { const pred = this.options.readUntil; const func = ctx.addImport(pred); ctx.pushCode( @@ -1162,7 +1162,7 @@ export class Parser { varName: string, type: string | Parser ) { - if (typeof type === 'string') { + if (typeof type === "string") { const varName = ctx.generateVariable(this.varName); if (!aliasRegistry[type]) { const typeName = PRIMITIVE_NAMES[type as PrimitiveTypes]; @@ -1210,15 +1210,15 @@ export class Parser { ctx.pushCode(`case ${tag}:`); this.generateChoiceCase(ctx, this.varName, type); - ctx.pushCode('break;'); + ctx.pushCode("break;"); }); - ctx.pushCode('default:'); + ctx.pushCode("default:"); if (this.options.defaultChoice) { this.generateChoiceCase(ctx, this.varName, this.options.defaultChoice); } else { ctx.generateError(`"Met undefined tag value " + ${tag} + " at choice"`); } - ctx.pushCode('}'); + ctx.pushCode("}"); if (this.varName && ctx.useContextVariables) { ctx.pushCode(`delete ${nestVar}.$parent;`); @@ -1271,7 +1271,7 @@ export class Parser { private generateWrapper(ctx: Context) { const wrapperVar = ctx.generateVariable(this.varName); const wrappedBuf = ctx.generateTmpVariable(); - if (typeof this.options.readUntil === 'function') { + if (typeof this.options.readUntil === "function") { const pred = this.options.readUntil; const start = ctx.generateTmpVariable(); const cur = ctx.generateTmpVariable(); @@ -1287,7 +1287,7 @@ export class Parser { ctx.pushCode(`offset += 1;`); ctx.pushCode(`}`); ctx.pushCode(`${wrappedBuf} = buffer.subarray(${start}, offset);`); - } else if (this.options.readUntil === 'eof') { + } else if (this.options.readUntil === "eof") { ctx.pushCode(`${wrappedBuf} = buffer.subarray(offset);`); } else { const len = ctx.generateOption(this.options.length); @@ -1339,7 +1339,7 @@ export class Parser { varName: string, formatter: Function ) { - if (typeof formatter === 'function') { + if (typeof formatter === "function") { const func = ctx.addImport(formatter); ctx.pushCode( `${varName} = ${func}.call(${ctx.generateVariable()}, ${varName});` diff --git a/lib/context.ts b/lib/context.ts index b87e9c1c..b7639b81 100644 --- a/lib/context.ts +++ b/lib/context.ts @@ -1,8 +1,8 @@ -import { Parser } from './binary_parser'; +import { Parser } from "./binary_parser"; export class Context { - code = ''; - scopes = [['vars']]; + code = ""; + scopes = [["vars"]]; bitFields: Parser[] = []; tmpVariableCount = 0; references: { [key: string]: { resolved: boolean; requested: boolean } } = {}; @@ -25,30 +25,30 @@ export class Context { arr.push(name); } - return arr.join('.'); + return arr.join("."); } generateOption(val: number | string | Function) { switch (typeof val) { - case 'number': + case "number": return val.toString(); - case 'string': + case "string": return this.generateVariable(val); - case 'function': + case "function": return `${this.addImport(val)}.call(${this.generateVariable()}, vars)`; } } generateError(err: string) { - this.pushCode('throw new Error(' + err + ');'); + this.pushCode("throw new Error(" + err + ");"); } generateTmpVariable() { - return '$tmp' + this.tmpVariableCount++; + return "$tmp" + this.tmpVariableCount++; } pushCode(code: string) { - this.code += code + '\n'; + this.code += code + "\n"; } pushPath(name: string) { diff --git a/package.json b/package.json index b951c57d..bb0dd453 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ }, "scripts": { "build": "tsc", - "fmt": "prettier --write \"{lib,example,test}/**/*.{ts,js}\"", - "check-fmt": "prettier --list-different \"{lib,example,test}/**/*.{ts,js}\"", + "fmt": "prettier --write \"{lib,example,test,benchmark}/**/*.{ts,js}\"", + "check-fmt": "prettier --list-different \"{lib,example,test,benchmark}/**/*.{ts,js}\"", "test": "mocha", "test-browser": "parcel test/browser.html --open", "cover": "nyc --reporter html mocha", diff --git a/test/composite_parser.js b/test/composite_parser.js index cbc4bd1e..3e36acfb 100644 --- a/test/composite_parser.js +++ b/test/composite_parser.js @@ -1,8 +1,8 @@ -require('fast-text-encoding'); -var assert = require('assert'); -var Parser = require('../dist/binary_parser').Parser; +require("fast-text-encoding"); +var assert = require("assert"); +var Parser = require("../dist/binary_parser").Parser; -const zlib = require('zlib'); +const zlib = require("zlib"); const suite = (Buffer) => describe(`Composite parser (${Buffer.name})`, function () { @@ -12,11 +12,11 @@ const suite = (Buffer) => ); } - describe('Array parser', function () { - it('should parse array of primitive types', function () { - var parser = Parser.start().uint8('length').array('message', { - length: 'length', - type: 'uint8', + describe("Array parser", function () { + it("should parse array of primitive types", function () { + var parser = Parser.start().uint8("length").array("message", { + length: "length", + type: "uint8", }); var buffer = Buffer.from([12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); @@ -25,10 +25,10 @@ const suite = (Buffer) => message: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], }); }); - it('should parse array of primitive types with lengthInBytes', function () { - var parser = Parser.start().uint8('length').array('message', { - lengthInBytes: 'length', - type: 'uint8', + it("should parse array of primitive types with lengthInBytes", function () { + var parser = Parser.start().uint8("length").array("message", { + lengthInBytes: "length", + type: "uint8", }); var buffer = Buffer.from([12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); @@ -37,23 +37,16 @@ const suite = (Buffer) => message: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], }); }); - it('should parse array of user defined types', function () { - var elementParser = new Parser().uint8('key').int16le('value'); + it("should parse array of user defined types", function () { + var elementParser = new Parser().uint8("key").int16le("value"); - var parser = Parser.start().uint16le('length').array('message', { - length: 'length', + var parser = Parser.start().uint16le("length").array("message", { + length: "length", type: elementParser, }); var buffer = Buffer.from([ - 0x02, - 0x00, - 0xca, - 0xd2, - 0x04, - 0xbe, - 0xd3, - 0x04, + 0x02, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x02, @@ -63,9 +56,9 @@ const suite = (Buffer) => ], }); }); - it('should parse array of user defined types and have access to parent context', function () { - var elementParser = new Parser().uint8('key').array('value', { - type: 'uint8', + it("should parse array of user defined types and have access to parent context", function () { + var elementParser = new Parser().uint8("key").array("value", { + type: "uint8", length: function () { return this.$parent.valueLength; }, @@ -73,24 +66,15 @@ const suite = (Buffer) => var parser = Parser.start() .useContextVars() - .uint16le('length') - .uint16le('valueLength') - .array('message', { - length: 'length', + .uint16le("length") + .uint16le("valueLength") + .array("message", { + length: "length", type: elementParser, }); var buffer = Buffer.from([ - 0x02, - 0x00, - 0x02, - 0x00, - 0xca, - 0xd2, - 0x04, - 0xbe, - 0xd3, - 0x04, + 0x02, 0x00, 0x02, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x02, @@ -101,34 +85,25 @@ const suite = (Buffer) => ], }); }); - it('should parse array of user defined types and have access to root context', function () { - var elementParser = new Parser().uint8('key').nest('data', { - type: new Parser().array('value', { - type: 'uint8', - length: '$root.valueLength', + it("should parse array of user defined types and have access to root context", function () { + var elementParser = new Parser().uint8("key").nest("data", { + type: new Parser().array("value", { + type: "uint8", + length: "$root.valueLength", }), }); var parser = Parser.start() .useContextVars() - .uint16le('length') - .uint16le('valueLength') - .array('message', { - length: 'length', + .uint16le("length") + .uint16le("valueLength") + .array("message", { + length: "length", type: elementParser, }); var buffer = Buffer.from([ - 0x02, - 0x00, - 0x02, - 0x00, - 0xca, - 0xd2, - 0x04, - 0xbe, - 0xd3, - 0x04, + 0x02, 0x00, 0x02, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x02, @@ -139,23 +114,16 @@ const suite = (Buffer) => ], }); }); - it('should parse array of user defined types with lengthInBytes', function () { - var elementParser = new Parser().uint8('key').int16le('value'); + it("should parse array of user defined types with lengthInBytes", function () { + var elementParser = new Parser().uint8("key").int16le("value"); - var parser = Parser.start().uint16le('length').array('message', { - lengthInBytes: 'length', + var parser = Parser.start().uint16le("length").array("message", { + lengthInBytes: "length", type: elementParser, }); var buffer = Buffer.from([ - 0x06, - 0x00, - 0xca, - 0xd2, - 0x04, - 0xbe, - 0xd3, - 0x04, + 0x06, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x06, @@ -165,10 +133,10 @@ const suite = (Buffer) => ], }); }); - it('should parse array of user defined types with lengthInBytes literal', function () { - var elementParser = new Parser().uint8('key').int16le('value'); + it("should parse array of user defined types with lengthInBytes literal", function () { + var elementParser = new Parser().uint8("key").int16le("value"); - var parser = Parser.start().array('message', { + var parser = Parser.start().array("message", { lengthInBytes: 0x06, type: elementParser, }); @@ -181,12 +149,12 @@ const suite = (Buffer) => ], }); }); - it('should parse array of user defined types with lengthInBytes function', function () { - var elementParser = new Parser().uint8('key').int16le('value'); + it("should parse array of user defined types with lengthInBytes function", function () { + var elementParser = new Parser().uint8("key").int16le("value"); var parser = Parser.start() - .uint16le('length') - .array('message', { + .uint16le("length") + .array("message", { lengthInBytes: function () { return this.length; }, @@ -194,14 +162,7 @@ const suite = (Buffer) => }); var buffer = Buffer.from([ - 0x06, - 0x00, - 0xca, - 0xd2, - 0x04, - 0xbe, - 0xd3, - 0x04, + 0x06, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x06, @@ -211,14 +172,14 @@ const suite = (Buffer) => ], }); }); - it('should parse array of arrays', function () { - var rowParser = Parser.start().uint8('length').array('cols', { - length: 'length', - type: 'int32le', + it("should parse array of arrays", function () { + var rowParser = Parser.start().uint8("length").array("cols", { + length: "length", + type: "int32le", }); - var parser = Parser.start().uint8('length').array('rows', { - length: 'length', + var parser = Parser.start().uint8("length").array("rows", { + length: "length", type: rowParser, }); @@ -255,122 +216,65 @@ const suite = (Buffer) => ], }); }); - it('should parse until eof when readUntil is specified', function () { - var parser = Parser.start().array('data', { - readUntil: 'eof', - type: 'uint8', + it("should parse until eof when readUntil is specified", function () { + var parser = Parser.start().array("data", { + readUntil: "eof", + type: "uint8", }); var buffer = Buffer.from([ - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]); assert.deepStrictEqual(parser.parse(buffer), { data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], }); }); - it('should parse until function returns true when readUntil is function', function () { - var parser = Parser.start().array('data', { + it("should parse until function returns true when readUntil is function", function () { + var parser = Parser.start().array("data", { readUntil: function (item, buf) { return item === 0; }, - type: 'uint8', + type: "uint8", }); var buffer = Buffer.from([ - 0xff, - 0xff, - 0xff, - 0x01, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, + 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, ]); assert.deepStrictEqual(parser.parse(buffer), { data: [0xff, 0xff, 0xff, 0x01, 0x00], }); }); - it('should parse until function returns true when readUntil is function (using read-ahead)', function () { - var parser = Parser.start().array('data', { + it("should parse until function returns true when readUntil is function (using read-ahead)", function () { + var parser = Parser.start().array("data", { readUntil: function (item, buf) { return buf.length > 0 && buf[0] === 0; }, - type: 'uint8', + type: "uint8", }); var buffer = Buffer.from([ - 0xff, - 0xff, - 0xff, - 0x01, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, - 0xff, + 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, ]); assert.deepStrictEqual(parser.parse(buffer), { data: [0xff, 0xff, 0xff, 0x01], }); }); - it('should parse associative arrays', function () { + it("should parse associative arrays", function () { var parser = Parser.start() - .int8('numlumps') - .array('lumps', { + .int8("numlumps") + .array("lumps", { type: Parser.start() - .int32le('filepos') - .int32le('size') - .string('name', { length: 8, encoding: 'utf8' }), - length: 'numlumps', - key: 'name', + .int32le("filepos") + .int32le("size") + .string("name", { length: 8, encoding: "utf8" }), + length: "numlumps", + key: "name", }); var buffer = Buffer.from([ - 0x02, - 0xd2, - 0x04, - 0x00, - 0x00, - 0x2e, - 0x16, - 0x00, - 0x00, - 0x41, - 0x41, - 0x41, - 0x41, - 0x41, - 0x41, - 0x41, - 0x41, - 0x2e, - 0x16, - 0x00, - 0x00, - 0xd2, - 0x04, - 0x00, - 0x00, - 0x62, - 0x62, - 0x62, - 0x62, - 0x62, - 0x62, - 0x62, - 0x62, + 0x02, 0xd2, 0x04, 0x00, 0x00, 0x2e, 0x16, 0x00, 0x00, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x2e, 0x16, 0x00, 0x00, 0xd2, + 0x04, 0x00, 0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, ]); assert.deepStrictEqual(parser.parse(buffer), { numlumps: 2, @@ -378,37 +282,37 @@ const suite = (Buffer) => AAAAAAAA: { filepos: 1234, size: 5678, - name: 'AAAAAAAA', + name: "AAAAAAAA", }, bbbbbbbb: { filepos: 5678, size: 1234, - name: 'bbbbbbbb', + name: "bbbbbbbb", }, }, }); }); - it('should use formatter to transform parsed array', function () { - var parser = Parser.start().array('data', { - type: 'uint8', + it("should use formatter to transform parsed array", function () { + var parser = Parser.start().array("data", { + type: "uint8", length: 4, formatter: function (arr) { - return arr.join('.'); + return arr.join("."); }, }); var buffer = Buffer.from([0x0a, 0x0a, 0x01, 0x6e]); assert.deepStrictEqual(parser.parse(buffer), { - data: '10.10.1.110', + data: "10.10.1.110", }); }); - it('should be able to go into recursion', function () { + it("should be able to go into recursion", function () { var parser = Parser.start() - .namely('self') - .uint8('length') - .array('data', { - type: 'self', - length: 'length', + .namely("self") + .uint8("length") + .array("data", { + type: "self", + length: "length", }); var buffer = Buffer.from([1, 1, 1, 0]); @@ -427,13 +331,13 @@ const suite = (Buffer) => ], }); }); - it('should be able to go into even deeper recursion', function () { + it("should be able to go into even deeper recursion", function () { var parser = Parser.start() - .namely('self') - .uint8('length') - .array('data', { - type: 'self', - length: 'length', + .namely("self") + .uint8("length") + .array("data", { + type: "self", + length: "length", }); // 2 @@ -484,20 +388,20 @@ const suite = (Buffer) => ], }); }); - it('should allow parent parser attributes as choice key', function () { - var ChildParser = Parser.start().choice('data', { + it("should allow parent parser attributes as choice key", function () { + var ChildParser = Parser.start().choice("data", { tag: function (vars) { return vars.version; }, choices: { - 1: Parser.start().uint8('v1'), - 2: Parser.start().uint16('v2'), + 1: Parser.start().uint8("v1"), + 2: Parser.start().uint16("v2"), }, }); var ParentParser = Parser.start() - .uint8('version') - .nest('child', { type: ChildParser }); + .uint8("version") + .nest("child", { type: ChildParser }); var buffer = Buffer.from([0x1, 0x2]); assert.deepStrictEqual(ParentParser.parse(buffer), { @@ -511,126 +415,101 @@ const suite = (Buffer) => child: { data: { v2: 0x0304 } }, }); }); - it('should be able to access to index context variable when using length', function () { + it("should be able to access to index context variable when using length", function () { var elementParser = new Parser() - .uint8('key', { + .uint8("key", { formatter: function (item) { return this.$index % 2 === 0 ? item : String.fromCharCode(item); }, }) - .nest('data', { - type: new Parser().array('value', { - type: 'uint8', - length: '$root.valueLength', + .nest("data", { + type: new Parser().array("value", { + type: "uint8", + length: "$root.valueLength", }), }); var parser = Parser.start() .useContextVars() - .uint16le('length') - .uint16le('valueLength') - .array('message', { - length: 'length', + .uint16le("length") + .uint16le("valueLength") + .array("message", { + length: "length", type: elementParser, }); var buffer = Buffer.from([ - 0x02, - 0x00, - 0x02, - 0x00, - 0x50, - 0xd2, - 0x04, - 0x51, - 0xd3, - 0x04, + 0x02, 0x00, 0x02, 0x00, 0x50, 0xd2, 0x04, 0x51, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x02, valueLength: 0x02, message: [ { key: 0x50, data: { value: [0xd2, 0x04] } }, - { key: 'Q', data: { value: [0xd3, 0x04] } }, + { key: "Q", data: { value: [0xd3, 0x04] } }, ], }); }); - it('should be able to access to index context variable when using length on named parser', function () { + it("should be able to access to index context variable when using length on named parser", function () { var elementParser = new Parser() - .uint8('key', { + .uint8("key", { formatter: function (item) { return this.$index % 2 === 0 ? item : String.fromCharCode(item); }, }) - .nest('data', { - type: new Parser().array('value', { - type: 'uint8', - length: '$root.valueLength', + .nest("data", { + type: new Parser().array("value", { + type: "uint8", + length: "$root.valueLength", }), }) - .namely('ArrayLengthIndexTest'); + .namely("ArrayLengthIndexTest"); var parser = Parser.start() .useContextVars() - .uint16le('length') - .uint16le('valueLength') - .array('message', { - length: 'length', - type: 'ArrayLengthIndexTest', + .uint16le("length") + .uint16le("valueLength") + .array("message", { + length: "length", + type: "ArrayLengthIndexTest", }); var buffer = Buffer.from([ - 0x02, - 0x00, - 0x02, - 0x00, - 0x50, - 0xd2, - 0x04, - 0x51, - 0xd3, - 0x04, + 0x02, 0x00, 0x02, 0x00, 0x50, 0xd2, 0x04, 0x51, 0xd3, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { length: 0x02, valueLength: 0x02, message: [ { key: 0x50, data: { value: [0xd2, 0x04] } }, - { key: 'Q', data: { value: [0xd3, 0x04] } }, + { key: "Q", data: { value: [0xd3, 0x04] } }, ], }); }); }); - describe('Choice parser', function () { - it('should parse choices of primitive types', function () { + describe("Choice parser", function () { + it("should parse choices of primitive types", function () { var parser = Parser.start() - .uint8('tag1') - .choice('data1', { - tag: 'tag1', + .uint8("tag1") + .choice("data1", { + tag: "tag1", choices: { - 0: 'int32le', - 1: 'int16le', + 0: "int32le", + 1: "int16le", }, }) - .uint8('tag2') - .choice('data2', { - tag: 'tag2', + .uint8("tag2") + .choice("data2", { + tag: "tag2", choices: { - 0: 'int32le', - 1: 'int16le', + 0: "int32le", + 1: "int16le", }, }); var buffer = Buffer.from([ - 0x0, - 0x4e, - 0x61, - 0xbc, - 0x00, - 0x01, - 0xd2, - 0x04, + 0x0, 0x4e, 0x61, 0xbc, 0x00, 0x01, 0xd2, 0x04, ]); assert.deepStrictEqual(parser.parse(buffer), { tag1: 0, @@ -639,18 +518,18 @@ const suite = (Buffer) => data2: 1234, }); }); - it('should parse default choice', function () { + it("should parse default choice", function () { var parser = Parser.start() - .uint8('tag') - .choice('data', { - tag: 'tag', + .uint8("tag") + .choice("data", { + tag: "tag", choices: { - 0: 'int32le', - 1: 'int16le', + 0: "int32le", + 1: "int16le", }, - defaultChoice: 'uint8', + defaultChoice: "uint8", }) - .int32le('test'); + .int32le("test"); var buffer = Buffer.from([0x03, 0xff, 0x2f, 0xcb, 0x04, 0x0]); assert.deepStrictEqual(parser.parse(buffer), { @@ -659,40 +538,28 @@ const suite = (Buffer) => test: 314159, }); }); - it('should parse choices of user defined types', function () { + it("should parse choices of user defined types", function () { var parser = Parser.start() - .uint8('tag') - .choice('data', { - tag: 'tag', + .uint8("tag") + .choice("data", { + tag: "tag", choices: { 1: Parser.start() - .uint8('length') - .string('message', { length: 'length' }), - 3: Parser.start().int32le('number'), + .uint8("length") + .string("message", { length: "length" }), + 3: Parser.start().int32le("number"), }, }); var buffer = Buffer.from([ - 0x1, - 0xc, - 0x68, - 0x65, - 0x6c, - 0x6c, - 0x6f, - 0x2c, - 0x20, - 0x77, - 0x6f, - 0x72, - 0x6c, - 0x64, + 0x1, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, ]); assert.deepStrictEqual(parser.parse(buffer), { tag: 1, data: { length: 12, - message: 'hello, world', + message: "hello, world", }, }); buffer = Buffer.from([0x03, 0x4e, 0x61, 0xbc, 0x00]); @@ -703,17 +570,17 @@ const suite = (Buffer) => }, }); }); - it('should be able to go into recursion', function () { + it("should be able to go into recursion", function () { var stop = Parser.start(); var parser = Parser.start() - .namely('self') - .uint8('type') - .choice('data', { - tag: 'type', + .namely("self") + .uint8("type") + .choice("data", { + tag: "type", choices: { 0: stop, - 1: 'self', + 1: "self", }, }); @@ -729,20 +596,20 @@ const suite = (Buffer) => }, }); }); - it('should be able to go into recursion with simple nesting', function () { + it("should be able to go into recursion with simple nesting", function () { var stop = Parser.start(); var parser = Parser.start() - .namely('self') - .uint8('type') - .choice('data', { - tag: 'type', + .namely("self") + .uint8("type") + .choice("data", { + tag: "type", choices: { 0: stop, - 1: 'self', + 1: "self", 2: Parser.start() - .nest('left', { type: 'self' }) - .nest('right', { type: stop }), + .nest("left", { type: "self" }) + .nest("right", { type: stop }), }, }); @@ -758,22 +625,22 @@ const suite = (Buffer) => }, }); }); - it('should be able to refer to other parsers by name', function () { - var parser = Parser.start().namely('self'); + it("should be able to refer to other parsers by name", function () { + var parser = Parser.start().namely("self"); - var stop = Parser.start().namely('stop'); + var stop = Parser.start().namely("stop"); var twoCells = Parser.start() - .namely('twoCells') - .nest('left', { type: 'self' }) - .nest('right', { type: 'stop' }); + .namely("twoCells") + .nest("left", { type: "self" }) + .nest("right", { type: "stop" }); - parser.uint8('type').choice('data', { - tag: 'type', + parser.uint8("type").choice("data", { + tag: "type", choices: { - 0: 'stop', - 1: 'self', - 2: 'twoCells', + 0: "stop", + 1: "self", + 2: "twoCells", }, }); @@ -789,20 +656,20 @@ const suite = (Buffer) => }, }); }); - it('should be able to refer to other parsers both directly and by name', function () { - var parser = Parser.start().namely('self'); + it("should be able to refer to other parsers both directly and by name", function () { + var parser = Parser.start().namely("self"); var stop = Parser.start(); var twoCells = Parser.start() - .nest('left', { type: 'self' }) - .nest('right', { type: stop }); + .nest("left", { type: "self" }) + .nest("right", { type: stop }); - parser.uint8('type').choice('data', { - tag: 'type', + parser.uint8("type").choice("data", { + tag: "type", choices: { 0: stop, - 1: 'self', + 1: "self", 2: twoCells, }, }); @@ -819,24 +686,24 @@ const suite = (Buffer) => }, }); }); - it('should be able to go into recursion with complex nesting', function () { + it("should be able to go into recursion with complex nesting", function () { var stop = Parser.start(); var parser = Parser.start() - .namely('self') - .uint8('type') - .choice('data', { - tag: 'type', + .namely("self") + .uint8("type") + .choice("data", { + tag: "type", choices: { 0: stop, - 1: 'self', + 1: "self", 2: Parser.start() - .nest('left', { type: 'self' }) - .nest('right', { type: 'self' }), + .nest("left", { type: "self" }) + .nest("right", { type: "self" }), 3: Parser.start() - .nest('one', { type: 'self' }) - .nest('two', { type: 'self' }) - .nest('three', { type: 'self' }), + .nest("one", { type: "self" }) + .nest("two", { type: "self" }) + .nest("three", { type: "self" }), }, }); @@ -890,37 +757,25 @@ const suite = (Buffer) => }); it("should be able to 'flatten' choices when using null varName", function () { var parser = Parser.start() - .uint8('tag') + .uint8("tag") .choice(null, { - tag: 'tag', + tag: "tag", choices: { 1: Parser.start() - .uint8('length') - .string('message', { length: 'length' }), - 3: Parser.start().int32le('number'), + .uint8("length") + .string("message", { length: "length" }), + 3: Parser.start().int32le("number"), }, }); var buffer = Buffer.from([ - 0x1, - 0xc, - 0x68, - 0x65, - 0x6c, - 0x6c, - 0x6f, - 0x2c, - 0x20, - 0x77, - 0x6f, - 0x72, - 0x6c, - 0x64, + 0x1, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, ]); assert.deepStrictEqual(parser.parse(buffer), { tag: 1, length: 12, - message: 'hello, world', + message: "hello, world", }); buffer = Buffer.from([0x03, 0x4e, 0x61, 0xbc, 0x00]); assert.deepStrictEqual(parser.parse(buffer), { @@ -930,37 +785,25 @@ const suite = (Buffer) => }); it("should be able to 'flatten' choices when omitting varName parameter", function () { var parser = Parser.start() - .uint8('tag') + .uint8("tag") .choice({ - tag: 'tag', + tag: "tag", choices: { 1: Parser.start() - .uint8('length') - .string('message', { length: 'length' }), - 3: Parser.start().int32le('number'), + .uint8("length") + .string("message", { length: "length" }), + 3: Parser.start().int32le("number"), }, }); var buffer = Buffer.from([ - 0x1, - 0xc, - 0x68, - 0x65, - 0x6c, - 0x6c, - 0x6f, - 0x2c, - 0x20, - 0x77, - 0x6f, - 0x72, - 0x6c, - 0x64, + 0x1, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, ]); assert.deepStrictEqual(parser.parse(buffer), { tag: 1, length: 12, - message: 'hello, world', + message: "hello, world", }); buffer = Buffer.from([0x03, 0x4e, 0x61, 0xbc, 0x00]); assert.deepStrictEqual(parser.parse(buffer), { @@ -968,94 +811,65 @@ const suite = (Buffer) => number: 12345678, }); }); - it('should be able to use function as the choice selector', function () { + it("should be able to use function as the choice selector", function () { var parser = Parser.start() - .string('selector', { length: 4 }) + .string("selector", { length: 4 }) .choice(null, { tag: function () { return parseInt(this.selector, 2); // string base 2 to integer decimal }, choices: { 2: Parser.start() - .uint8('length') - .string('message', { length: 'length' }), - 7: Parser.start().int32le('number'), + .uint8("length") + .string("message", { length: "length" }), + 7: Parser.start().int32le("number"), }, }); var buffer = Buffer.from([ - 48, - 48, - 49, - 48, - 0xc, - 0x68, - 0x65, - 0x6c, - 0x6c, - 0x6f, - 0x2c, - 0x20, - 0x77, - 0x6f, - 0x72, - 0x6c, - 0x64, + 48, 48, 49, 48, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, + 0x6f, 0x72, 0x6c, 0x64, ]); assert.deepStrictEqual(parser.parse(buffer), { - selector: '0010', // -> choice 2 + selector: "0010", // -> choice 2 length: 12, - message: 'hello, world', + message: "hello, world", }); buffer = Buffer.from([48, 49, 49, 49, 0x4e, 0x61, 0xbc, 0x00]); assert.deepStrictEqual(parser.parse(buffer), { - selector: '0111', // -> choice 7 + selector: "0111", // -> choice 7 number: 12345678, }); }); - it('should be able to use parsing context', function () { + it("should be able to use parsing context", function () { var parser = Parser.start() .useContextVars() - .uint8('tag') - .uint8('items') - .choice('data', { - tag: 'tag', + .uint8("tag") + .uint8("items") + .choice("data", { + tag: "tag", choices: { 1: Parser.start() - .uint8('length') - .string('message', { length: 'length' }) - .array('value', { type: 'uint8', length: '$parent.items' }), - 3: Parser.start().int32le('number'), + .uint8("length") + .string("message", { length: "length" }) + .array("value", { + type: "uint8", + length: "$parent.items", + }), + 3: Parser.start().int32le("number"), }, }); var buffer = Buffer.from([ - 0x1, - 0x2, - 0xc, - 0x68, - 0x65, - 0x6c, - 0x6c, - 0x6f, - 0x2c, - 0x20, - 0x77, - 0x6f, - 0x72, - 0x6c, - 0x64, - 0x01, - 0x02, - 0x02, - 0x02, + 0x1, 0x2, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x01, 0x02, 0x02, 0x02, ]); assert.deepStrictEqual(parser.parse(buffer), { tag: 1, items: 2, data: { length: 12, - message: 'hello, world', + message: "hello, world", value: [0x01, 0x02], }, }); @@ -1070,32 +884,32 @@ const suite = (Buffer) => }); }); - describe('Nest parser', function () { - it('should parse nested parsers', function () { + describe("Nest parser", function () { + it("should parse nested parsers", function () { var nameParser = new Parser() - .string('firstName', { + .string("firstName", { zeroTerminated: true, }) - .string('lastName', { + .string("lastName", { zeroTerminated: true, }); - var infoParser = new Parser().uint8('age'); + var infoParser = new Parser().uint8("age"); var personParser = new Parser() - .nest('name', { + .nest("name", { type: nameParser, }) - .nest('info', { + .nest("info", { type: infoParser, }); var buffer = Buffer.from([ - ...Buffer.from(new TextEncoder().encode('John\0Doe\0')), + ...Buffer.from(new TextEncoder().encode("John\0Doe\0")), ...Buffer.from([0x20]), ]); assert.deepStrictEqual(personParser.parse(buffer), { name: { - firstName: 'John', - lastName: 'Doe', + firstName: "John", + lastName: "Doe", }, info: { age: 0x20, @@ -1103,156 +917,143 @@ const suite = (Buffer) => }); }); - it('should format parsed nested parser', function () { + it("should format parsed nested parser", function () { var nameParser = new Parser() - .string('firstName', { + .string("firstName", { zeroTerminated: true, }) - .string('lastName', { + .string("lastName", { zeroTerminated: true, }); - var personParser = new Parser().nest('name', { + var personParser = new Parser().nest("name", { type: nameParser, formatter: function (name) { - return name.firstName + ' ' + name.lastName; + return name.firstName + " " + name.lastName; }, }); - var buffer = Buffer.from(new TextEncoder().encode('John\0Doe\0')); + var buffer = Buffer.from(new TextEncoder().encode("John\0Doe\0")); assert.deepStrictEqual(personParser.parse(buffer), { - name: 'John Doe', + name: "John Doe", }); }); it("should 'flatten' output when using null varName", function () { var parser = new Parser() - .string('s1', { zeroTerminated: true }) + .string("s1", { zeroTerminated: true }) .nest(null, { - type: new Parser().string('s2', { zeroTerminated: true }), + type: new Parser().string("s2", { zeroTerminated: true }), }); - var buf = Buffer.from(new TextEncoder().encode('foo\0bar\0')); + var buf = Buffer.from(new TextEncoder().encode("foo\0bar\0")); - assert.deepStrictEqual(parser.parse(buf), { s1: 'foo', s2: 'bar' }); + assert.deepStrictEqual(parser.parse(buf), { s1: "foo", s2: "bar" }); }); it("should 'flatten' output when omitting varName", function () { - var parser = new Parser().string('s1', { zeroTerminated: true }).nest({ - type: new Parser().string('s2', { zeroTerminated: true }), + var parser = new Parser().string("s1", { zeroTerminated: true }).nest({ + type: new Parser().string("s2", { zeroTerminated: true }), }); - var buf = Buffer.from(new TextEncoder().encode('foo\0bar\0')); + var buf = Buffer.from(new TextEncoder().encode("foo\0bar\0")); - assert.deepStrictEqual(parser.parse(buf), { s1: 'foo', s2: 'bar' }); + assert.deepStrictEqual(parser.parse(buf), { s1: "foo", s2: "bar" }); }); - it('should be able to use parsing context', function () { + it("should be able to use parsing context", function () { var parser = Parser.start() .useContextVars() - .uint8('items') - .nest('data', { + .uint8("items") + .nest("data", { type: Parser.start() - .uint8('length') - .string('message', { length: 'length' }) - .array('value', { type: 'uint8', length: '$parent.items' }), + .uint8("length") + .string("message", { length: "length" }) + .array("value", { + type: "uint8", + length: "$parent.items", + }), }); var buffer = Buffer.from([ - 0x2, - 0xc, - 0x68, - 0x65, - 0x6c, - 0x6c, - 0x6f, - 0x2c, - 0x20, - 0x77, - 0x6f, - 0x72, - 0x6c, - 0x64, - 0x01, - 0x02, - 0x02, - 0x02, + 0x2, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x01, 0x02, 0x02, 0x02, ]); assert.deepStrictEqual(parser.parse(buffer), { items: 2, data: { length: 12, - message: 'hello, world', + message: "hello, world", value: [0x01, 0x02], }, }); }); }); - describe('Constructors', function () { - it('should create a custom object type', function () { + describe("Constructors", function () { + it("should create a custom object type", function () { function Person() { - this.name = ''; + this.name = ""; } Person.prototype.toString = function () { - return '[object Person]'; + return "[object Person]"; }; - var parser = Parser.start().create(Person).string('name', { + var parser = Parser.start().create(Person).string("name", { zeroTerminated: true, }); - var buffer = Buffer.from(new TextEncoder().encode('John Doe\0')); + var buffer = Buffer.from(new TextEncoder().encode("John Doe\0")); var person = parser.parse(buffer); assert.ok(person instanceof Person); - assert.strictEqual(person.name, 'John Doe'); + assert.strictEqual(person.name, "John Doe"); }); }); - describe('Pointer parser', function () { - it('should move pointer to specified offset', function () { - var parser = Parser.start().pointer('x', { type: 'uint8', offset: 2 }); + describe("Pointer parser", function () { + it("should move pointer to specified offset", function () { + var parser = Parser.start().pointer("x", { type: "uint8", offset: 2 }); var buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); assert.deepStrictEqual(parser.parse(buf), { x: 3 }); }); - it('should restore pointer to original position', function () { + it("should restore pointer to original position", function () { var parser = Parser.start() - .pointer('x', { type: 'uint8', offset: 2 }) - .uint16be('y'); + .pointer("x", { type: "uint8", offset: 2 }) + .uint16be("y"); var buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); assert.deepStrictEqual(parser.parse(buf), { x: 0x3, y: 0x0102 }); }); - it('should work with child parser', function () { + it("should work with child parser", function () { var parser = Parser.start() - .uint32le('x') - .pointer('y', { - type: Parser.start().string('s', { zeroTerminated: true }), + .uint32le("x") + .pointer("y", { + type: Parser.start().string("s", { zeroTerminated: true }), offset: 4, }); - var buf = Buffer.from(new TextEncoder().encode('\1\2\3\4hello\0\6')); + var buf = Buffer.from(new TextEncoder().encode("\1\2\3\4hello\0\6")); assert.deepStrictEqual(parser.parse(buf), { x: 0x04030201, - y: { s: 'hello' }, + y: { s: "hello" }, }); }); - it('should pass variable context to child parser', function () {}); + it("should pass variable context to child parser", function () {}); var parser = Parser.start() - .uint16be('len') - .pointer('child', { + .uint16be("len") + .pointer("child", { offset: 4, - type: Parser.start().array('a', { - type: 'uint8', + type: Parser.start().array("a", { + type: "uint8", length: function (vars) { return vars.len; }, }), }); - var buf = Buffer.from(new TextEncoder().encode('\0\6\0\0\1\2\3\4\5\6')); + var buf = Buffer.from(new TextEncoder().encode("\0\6\0\0\1\2\3\4\5\6")); assert.deepStrictEqual(parser.parse(buf), { len: 6, @@ -1260,13 +1061,13 @@ const suite = (Buffer) => }); }); - describe('SaveOffset', () => { - it('should save the offset', () => { + describe("SaveOffset", () => { + it("should save the offset", () => { const buff = Buffer.from([0x01, 0x00, 0x02]); const parser = Parser.start() - .int8('a') - .int16('b') - .saveOffset('bytesRead'); + .int8("a") + .int16("b") + .saveOffset("bytesRead"); assert.deepStrictEqual(parser.parse(buff), { a: 1, @@ -1275,12 +1076,12 @@ const suite = (Buffer) => }); }); - it('should save the offset if not at end', () => { + it("should save the offset if not at end", () => { const buff = Buffer.from([0x01, 0x00, 0x02]); const parser = Parser.start() - .int8('a') - .saveOffset('bytesRead') - .int16('b'); + .int8("a") + .saveOffset("bytesRead") + .int16("b"); assert.deepStrictEqual(parser.parse(buff), { a: 1, @@ -1289,75 +1090,75 @@ const suite = (Buffer) => }); }); - it('should save the offset with a dynamic parser', () => { + it("should save the offset with a dynamic parser", () => { const buff = Buffer.from([0x74, 0x65, 0x73, 0x74, 0x00]); const parser = Parser.start() - .string('name', { zeroTerminated: true }) - .saveOffset('bytesRead'); + .string("name", { zeroTerminated: true }) + .saveOffset("bytesRead"); assert.deepStrictEqual(parser.parse(buff), { - name: 'test', + name: "test", bytesRead: 5, }); }); }); - describe('Utilities', function () { - it('should count size for fixed size structs', function () { + describe("Utilities", function () { + it("should count size for fixed size structs", function () { var parser = Parser.start() - .int8('a') - .int32le('b') - .string('msg', { length: 10 }) + .int8("a") + .int32le("b") + .string("msg", { length: 10 }) .seek(2) - .array('data', { + .array("data", { length: 3, - type: 'int8', + type: "int8", }) - .buffer('raw', { length: 8 }); + .buffer("raw", { length: 8 }); assert.strictEqual(parser.sizeOf(), 1 + 4 + 10 + 2 + 3 + 8); }); - it('should assert parsed values', function () { - var parser = Parser.start().string('msg', { - encoding: 'utf8', + it("should assert parsed values", function () { + var parser = Parser.start().string("msg", { + encoding: "utf8", zeroTerminated: true, - assert: 'hello, world', + assert: "hello, world", }); - var buffer = hexToBuf('68656c6c6f2c20776f726c6400'); + var buffer = hexToBuf("68656c6c6f2c20776f726c6400"); assert.doesNotThrow(function () { parser.parse(buffer); }); - buffer = hexToBuf('68656c6c6f2c206a7300'); + buffer = hexToBuf("68656c6c6f2c206a7300"); assert.throws(function () { parser.parse(buffer); }); parser = new Parser() - .int16le('a') - .int16le('b') - .int16le('c', { + .int16le("a") + .int16le("b") + .int16le("c", { assert: function (x) { return this.a + this.b === x; }, }); - buffer = hexToBuf('d2042e16001b'); + buffer = hexToBuf("d2042e16001b"); assert.doesNotThrow(function () { parser.parse(buffer); }); - buffer = hexToBuf('2e16001bd204'); + buffer = hexToBuf("2e16001bd204"); assert.throws(function () { parser.parse(buffer); }); }); }); - describe('Parse other fields after bit', function () { - it('Parse uint8', function () { + describe("Parse other fields after bit", function () { + it("Parse uint8", function () { var buffer = Buffer.from([0, 1, 0, 4]); for (var i = 17; i <= 24; i++) { - var parser = Parser.start()['bit' + i]('a').uint8('b'); + var parser = Parser.start()["bit" + i]("a").uint8("b"); assert.deepStrictEqual(parser.parse(buffer), { a: 1 << (i - 16), @@ -1367,9 +1168,9 @@ const suite = (Buffer) => }); }); - describe('Wrapper', function () { - it('should parse deflated then inflated data', function () { - var text = 'This is compressible text.\0'; + describe("Wrapper", function () { + it("should parse deflated then inflated data", function () { + var text = "This is compressible text.\0"; var bufferBefore = Buffer.from([ ...Buffer.from([12]), ...Buffer.from(new TextEncoder().encode(text)), @@ -1384,22 +1185,22 @@ const suite = (Buffer) => ]); var bufferParser = Parser.start() - .uint8('a') - .string('b', { + .uint8("a") + .string("b", { zeroTerminated: true, }) - .uint8('c'); + .uint8("c"); var mainParser = Parser.start() - .uint32le('length') - .wrapped('compressedData', { - length: 'length', + .uint32le("length") + .wrapped("compressedData", { + length: "length", wrapper: function buffer(x) { return zlib.inflateRawSync(x); }, type: bufferParser, }) - .uint8('answer'); + .uint8("answer"); assert.deepStrictEqual(mainParser.parse(buffer), { length: compressedData.length, compressedData: { diff --git a/test/primitive_parser.js b/test/primitive_parser.js index 63e347b1..e69fedd7 100644 --- a/test/primitive_parser.js +++ b/test/primitive_parser.js @@ -1,6 +1,6 @@ -require('fast-text-encoding'); -var assert = require('assert'); -var Parser = require('../dist/binary_parser').Parser; +require("fast-text-encoding"); +var assert = require("assert"); +var Parser = require("../dist/binary_parser").Parser; const suite = (Buffer) => describe(`Primitive parser (${Buffer.name})`, function () { @@ -9,15 +9,15 @@ const suite = (Buffer) => hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)) ); } - describe('Primitive parsers', function () { - it('should nothing', function () { + describe("Primitive parsers", function () { + it("should nothing", function () { var parser = Parser.start(); var buffer = Buffer.from([0xa, 0x14, 0x1e, 0x28, 0x32]); assert.deepStrictEqual(parser.parse(buffer), {}); }); - it('should parse integer types', function () { - var parser = Parser.start().uint8('a').int16le('b').uint32be('c'); + it("should parse integer types", function () { + var parser = Parser.start().uint8("a").int16le("b").uint32be("c"); var buffer = Buffer.from([0x00, 0xd2, 0x04, 0x00, 0xbc, 0x61, 0x4e]); assert.deepStrictEqual(parser.parse(buffer), { @@ -26,122 +26,69 @@ const suite = (Buffer) => c: 12345678, }); }); - describe('BigInt64 parsers', () => { - const [major] = process.version.replace('v', '').split('.'); + describe("BigInt64 parsers", () => { + const [major] = process.version.replace("v", "").split("."); if (Number(major) >= 12) { - it('should parse biguints64', () => { - const parser = Parser.start().uint64be('a').uint64le('b'); + it("should parse biguints64", () => { + const parser = Parser.start().uint64be("a").uint64le("b"); // from https://nodejs.org/api/buffer.html#buffer_buf_readbiguint64le_offset const buf = Buffer.from([ - 0x00, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, - 0x00, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, ]); assert.deepStrictEqual(parser.parse(buf), { - a: BigInt('4294967295'), - b: BigInt('18446744069414584320'), + a: BigInt("4294967295"), + b: BigInt("18446744069414584320"), }); }); - it('should parse bigints64', () => { + it("should parse bigints64", () => { const parser = Parser.start() - .int64be('a') - .int64le('b') - .int64be('c') - .int64le('d'); + .int64be("a") + .int64le("b") + .int64be("c") + .int64le("d"); // from https://nodejs.org/api/buffer.html#buffer_buf_readbiguint64le_offset const buf = Buffer.from([ - 0x00, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, - 0x01, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, - 0x00, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, - 0x01, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0xff, - 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, ]); assert.deepStrictEqual(parser.parse(buf), { - a: BigInt('4294967295'), - b: BigInt('-4294967295'), - c: BigInt('4294967295'), - d: BigInt('-4294967295'), + a: BigInt("4294967295"), + b: BigInt("-4294967295"), + c: BigInt("4294967295"), + d: BigInt("-4294967295"), }); }); } else { - it('should throw when run under not v12', () => { - assert.throws(() => Parser.start().bigint64('a')); + it("should throw when run under not v12", () => { + assert.throws(() => Parser.start().bigint64("a")); }); } }); - it('should use formatter to transform parsed integer', function () { + it("should use formatter to transform parsed integer", function () { var parser = Parser.start() - .uint8('a', { + .uint8("a", { formatter: function (val) { return val * 2; }, }) - .int16le('b', { + .int16le("b", { formatter: function (val) { - return 'test' + String(val); + return "test" + String(val); }, }); var buffer = Buffer.from([0x01, 0xd2, 0x04]); - assert.deepStrictEqual(parser.parse(buffer), { a: 2, b: 'test1234' }); + assert.deepStrictEqual(parser.parse(buffer), { a: 2, b: "test1234" }); }); - it('should parse floating point types', function () { - var parser = Parser.start().floatbe('a').doublele('b'); + it("should parse floating point types", function () { + var parser = Parser.start().floatbe("a").doublele("b"); var FLT_EPSILON = 0.00001; var buffer = Buffer.from([ - 0x41, - 0x45, - 0x85, - 0x1f, - 0x7a, - 0x36, - 0xab, - 0x3e, - 0x57, - 0x5b, - 0xb1, + 0x41, 0x45, 0x85, 0x1f, 0x7a, 0x36, 0xab, 0x3e, 0x57, 0x5b, 0xb1, 0xbf, ]); var result = parser.parse(buffer); @@ -149,42 +96,26 @@ const suite = (Buffer) => assert(Math.abs(result.a - 12.345) < FLT_EPSILON); assert(Math.abs(result.b - -0.0678) < FLT_EPSILON); }); - it('should handle endianess', function () { - var parser = Parser.start().int32le('little').int32be('big'); + it("should handle endianess", function () { + var parser = Parser.start().int32le("little").int32be("big"); var buffer = Buffer.from([ - 0x4e, - 0x61, - 0xbc, - 0x00, - 0x00, - 0xbc, - 0x61, - 0x4e, + 0x4e, 0x61, 0xbc, 0x00, 0x00, 0xbc, 0x61, 0x4e, ]); assert.deepStrictEqual(parser.parse(buffer), { little: 12345678, big: 12345678, }); }); - it('should seek offset', function () { + it("should seek offset", function () { var parser = Parser.start() - .uint8('a') + .uint8("a") .seek(3) - .uint16le('b') - .uint32be('c'); + .uint16le("b") + .uint32be("c"); var buffer = Buffer.from([ - 0x00, - 0xff, - 0xff, - 0xfe, - 0xd2, - 0x04, - 0x00, - 0xbc, - 0x61, - 0x4e, + 0x00, 0xff, 0xff, 0xfe, 0xd2, 0x04, 0x00, 0xbc, 0x61, 0x4e, ]); assert.deepStrictEqual(parser.parse(buffer), { a: 0, @@ -194,12 +125,12 @@ const suite = (Buffer) => }); }); - describe('Bit field parsers', function () { + describe("Bit field parsers", function () { var binaryLiteral = function (s) { var i; var bytes = []; - s = s.replace(/\s/g, ''); + s = s.replace(/\s/g, ""); for (i = 0; i < s.length; i += 8) { bytes.push(parseInt(s.slice(i, i + 8), 2)); } @@ -207,18 +138,18 @@ const suite = (Buffer) => return Buffer.from(bytes); }; - it('binary literal helper should work', function () { - assert.deepStrictEqual(binaryLiteral('11110000'), Buffer.from([0xf0])); + it("binary literal helper should work", function () { + assert.deepStrictEqual(binaryLiteral("11110000"), Buffer.from([0xf0])); assert.deepStrictEqual( - binaryLiteral('11110000 10100101'), + binaryLiteral("11110000 10100101"), Buffer.from([0xf0, 0xa5]) ); }); - it('should parse 1-byte-length bit field sequence', function () { - var parser = new Parser().bit1('a').bit2('b').bit4('c').bit1('d'); + it("should parse 1-byte-length bit field sequence", function () { + var parser = new Parser().bit1("a").bit2("b").bit4("c").bit1("d"); - var buf = binaryLiteral('1 10 1010 0'); + var buf = binaryLiteral("1 10 1010 0"); assert.deepStrictEqual(parser.parse(buf), { a: 1, b: 2, @@ -227,11 +158,11 @@ const suite = (Buffer) => }); parser = new Parser() - .endianess('little') - .bit1('a') - .bit2('b') - .bit4('c') - .bit1('d'); + .endianess("little") + .bit1("a") + .bit2("b") + .bit4("c") + .bit1("d"); assert.deepStrictEqual(parser.parse(buf), { a: 0, @@ -240,31 +171,31 @@ const suite = (Buffer) => d: 1, }); }); - it('should parse 2-byte-length bit field sequence', function () { - var parser = new Parser().bit3('a').bit9('b').bit4('c'); + it("should parse 2-byte-length bit field sequence", function () { + var parser = new Parser().bit3("a").bit9("b").bit4("c"); - var buf = binaryLiteral('101 111000111 0111'); + var buf = binaryLiteral("101 111000111 0111"); assert.deepStrictEqual(parser.parse(buf), { a: 5, b: 455, c: 7, }); - parser = new Parser().endianess('little').bit3('a').bit9('b').bit4('c'); + parser = new Parser().endianess("little").bit3("a").bit9("b").bit4("c"); assert.deepStrictEqual(parser.parse(buf), { a: 7, b: 398, c: 11, }); }); - it('should parse 4-byte-length bit field sequence', function () { + it("should parse 4-byte-length bit field sequence", function () { var parser = new Parser() - .bit1('a') - .bit24('b') - .bit4('c') - .bit2('d') - .bit1('e'); - var buf = binaryLiteral('1 101010101010101010101010 1111 01 1'); + .bit1("a") + .bit24("b") + .bit4("c") + .bit2("d") + .bit1("e"); + var buf = binaryLiteral("1 101010101010101010101010 1111 01 1"); assert.deepStrictEqual(parser.parse(buf), { a: 1, b: 11184810, @@ -274,12 +205,12 @@ const suite = (Buffer) => }); parser = new Parser() - .endianess('little') - .bit1('a') - .bit24('b') - .bit4('c') - .bit2('d') - .bit1('e'); + .endianess("little") + .bit1("a") + .bit24("b") + .bit4("c") + .bit2("d") + .bit1("e"); assert.deepStrictEqual(parser.parse(buf), { a: 1, b: 11184829, @@ -288,12 +219,12 @@ const suite = (Buffer) => e: 1, }); }); - it('should parse nested bit fields', function () { - var parser = new Parser().bit1('a').nest('x', { - type: new Parser().bit2('b').bit4('c').bit1('d'), + it("should parse nested bit fields", function () { + var parser = new Parser().bit1("a").nest("x", { + type: new Parser().bit2("b").bit4("c").bit1("d"), }); - var buf = binaryLiteral('11010100'); + var buf = binaryLiteral("11010100"); assert.deepStrictEqual(parser.parse(buf), { a: 1, @@ -306,157 +237,157 @@ const suite = (Buffer) => }); }); - describe('String parser', function () { - it('should parse UTF8 encoded string (ASCII only)', function () { - var text = 'hello, world'; + describe("String parser", function () { + it("should parse UTF8 encoded string (ASCII only)", function () { + var text = "hello, world"; var buffer = Buffer.from(new TextEncoder().encode(text)); - var parser = Parser.start().string('msg', { + var parser = Parser.start().string("msg", { length: buffer.length, - encoding: 'utf8', + encoding: "utf8", }); assert.strictEqual(parser.parse(buffer).msg, text); }); - it('should parse UTF8 encoded string', function () { - var text = 'こんにちは、せかい。'; + it("should parse UTF8 encoded string", function () { + var text = "こんにちは、せかい。"; var buffer = Buffer.from(new TextEncoder().encode(text)); - var parser = Parser.start().string('msg', { + var parser = Parser.start().string("msg", { length: buffer.length, - encoding: 'utf8', + encoding: "utf8", }); assert.strictEqual(parser.parse(buffer).msg, text); }); - it('should parse HEX encoded string', function () { - var text = 'cafebabe'; + it("should parse HEX encoded string", function () { + var text = "cafebabe"; var buffer = hexToBuf(text); - var parser = Parser.start().string('msg', { + var parser = Parser.start().string("msg", { length: buffer.length, - encoding: 'hex', + encoding: "hex", }); assert.strictEqual(parser.parse(buffer).msg, text); }); - it('should parse variable length string', function () { - var buffer = hexToBuf('0c68656c6c6f2c20776f726c64'); + it("should parse variable length string", function () { + var buffer = hexToBuf("0c68656c6c6f2c20776f726c64"); var parser = Parser.start() - .uint8('length') - .string('msg', { length: 'length', encoding: 'utf8' }); + .uint8("length") + .string("msg", { length: "length", encoding: "utf8" }); - assert.strictEqual(parser.parse(buffer).msg, 'hello, world'); + assert.strictEqual(parser.parse(buffer).msg, "hello, world"); }); - it('should parse zero terminated string', function () { - var buffer = hexToBuf('68656c6c6f2c20776f726c6400'); - var parser = Parser.start().string('msg', { + it("should parse zero terminated string", function () { + var buffer = hexToBuf("68656c6c6f2c20776f726c6400"); + var parser = Parser.start().string("msg", { zeroTerminated: true, - encoding: 'utf8', + encoding: "utf8", }); - assert.deepStrictEqual(parser.parse(buffer), { msg: 'hello, world' }); + assert.deepStrictEqual(parser.parse(buffer), { msg: "hello, world" }); }); - it('should parser zero terminated fixed-length string', function () { + it("should parser zero terminated fixed-length string", function () { var buffer = Buffer.from( - new TextEncoder().encode('abc\u0000defghij\u0000') + new TextEncoder().encode("abc\u0000defghij\u0000") ); var parser = Parser.start() - .string('a', { length: 5, zeroTerminated: true }) - .string('b', { length: 5, zeroTerminated: true }) - .string('c', { length: 5, zeroTerminated: true }); + .string("a", { length: 5, zeroTerminated: true }) + .string("b", { length: 5, zeroTerminated: true }) + .string("c", { length: 5, zeroTerminated: true }); assert.deepStrictEqual(parser.parse(buffer), { - a: 'abc', - b: 'defgh', - c: 'ij', + a: "abc", + b: "defgh", + c: "ij", }); }); - it('should strip trailing null characters', function () { - var buffer = hexToBuf('746573740000'); - var parser1 = Parser.start().string('str', { + it("should strip trailing null characters", function () { + var buffer = hexToBuf("746573740000"); + var parser1 = Parser.start().string("str", { length: 7, stripNull: false, }); - var parser2 = Parser.start().string('str', { + var parser2 = Parser.start().string("str", { length: 7, stripNull: true, }); - assert.strictEqual(parser1.parse(buffer).str, 'test\u0000\u0000'); - assert.strictEqual(parser2.parse(buffer).str, 'test'); + assert.strictEqual(parser1.parse(buffer).str, "test\u0000\u0000"); + assert.strictEqual(parser2.parse(buffer).str, "test"); }); - it('should parse string greedily with zero-bytes internally', function () { + it("should parse string greedily with zero-bytes internally", function () { var buffer = Buffer.from( - new TextEncoder().encode('abc\u0000defghij\u0000') + new TextEncoder().encode("abc\u0000defghij\u0000") ); - var parser = Parser.start().string('a', { greedy: true }); + var parser = Parser.start().string("a", { greedy: true }); assert.deepStrictEqual(parser.parse(buffer), { - a: 'abc\u0000defghij\u0000', + a: "abc\u0000defghij\u0000", }); }); }); - describe('Buffer parser', function () { - it('should parse as buffer', function () { - var parser = new Parser().uint8('len').buffer('raw', { - length: 'len', + describe("Buffer parser", function () { + it("should parse as buffer", function () { + var parser = new Parser().uint8("len").buffer("raw", { + length: "len", }); - var buf = hexToBuf('deadbeefdeadbeef'); + var buf = hexToBuf("deadbeefdeadbeef"); var result = parser.parse(Buffer.from([...Buffer.from([8]), ...buf])); assert.deepStrictEqual(result.raw, buf); }); - it('should clone buffer if options.clone is true', function () { - var parser = new Parser().buffer('raw', { + it("should clone buffer if options.clone is true", function () { + var parser = new Parser().buffer("raw", { length: 8, clone: true, }); - var buf = hexToBuf('deadbeefdeadbeef'); + var buf = hexToBuf("deadbeefdeadbeef"); var result = parser.parse(buf); assert.deepStrictEqual(result.raw, buf); result.raw[0] = 0xff; assert.notDeepStrictEqual(result.raw, buf); }); - it('should parse until function returns true when readUntil is function', function () { + it("should parse until function returns true when readUntil is function", function () { var parser = new Parser() - .endianess('big') - .uint8('cmd') - .buffer('data', { + .endianess("big") + .uint8("cmd") + .buffer("data", { readUntil: function (item) { return item === 2; }, }); - var result = parser.parse(hexToBuf('aa')); + var result = parser.parse(hexToBuf("aa")); assert.deepStrictEqual(result, { cmd: 0xaa, data: Buffer.from([]) }); - var result = parser.parse(hexToBuf('aabbcc')); - assert.deepStrictEqual(result, { cmd: 0xaa, data: hexToBuf('bbcc') }); + var result = parser.parse(hexToBuf("aabbcc")); + assert.deepStrictEqual(result, { cmd: 0xaa, data: hexToBuf("bbcc") }); - var result = parser.parse(hexToBuf('aa02bbcc')); + var result = parser.parse(hexToBuf("aa02bbcc")); assert.deepStrictEqual(result, { cmd: 0xaa, data: Buffer.from([]) }); - var result = parser.parse(hexToBuf('aabbcc02')); - assert.deepStrictEqual(result, { cmd: 0xaa, data: hexToBuf('bbcc') }); + var result = parser.parse(hexToBuf("aabbcc02")); + assert.deepStrictEqual(result, { cmd: 0xaa, data: hexToBuf("bbcc") }); - var result = parser.parse(hexToBuf('aabbcc02dd')); - assert.deepStrictEqual(result, { cmd: 0xaa, data: hexToBuf('bbcc') }); + var result = parser.parse(hexToBuf("aabbcc02dd")); + assert.deepStrictEqual(result, { cmd: 0xaa, data: hexToBuf("bbcc") }); }); // this is a test for testing a fix of a bug, that removed the last byte // of the buffer parser - it('should return a buffer with same size', function () { - var bufferParser = new Parser().buffer('buf', { - readUntil: 'eof', + it("should return a buffer with same size", function () { + var bufferParser = new Parser().buffer("buf", { + readUntil: "eof", formatter: function (buffer) { return buffer; }, }); - var buffer = Buffer.from('John\0Doe\0'); + var buffer = Buffer.from("John\0Doe\0"); assert.deepStrictEqual(bufferParser.parse(buffer), { buf: buffer }); }); });