Skip to content

Commit 7c46a82

Browse files
committed
Added $index context variable
1 parent 56f0634 commit 7c46a82

File tree

3 files changed

+148
-26
lines changed

3 files changed

+148
-26
lines changed

README.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,8 @@ You can use some special fields while parsing to traverse your structure. These
524524
context variables will be removed after the parsing process:
525525
- `$parent` - This field references the parent structure. This variable will be
526526
`null` while parsing the root structure.
527-
```js
527+
528+
```javascript
528529
var parser = new Parser()
529530
.nest("header", {
530531
type: new Parser().uint32("length"),
@@ -534,10 +535,12 @@ context variables will be removed after the parsing process:
534535
length: function() {
535536
return this.$parent.header.length
536537
}
537-
})
538+
});
538539
```
540+
539541
- `$root` - This field references the root structure.
540-
```js
542+
543+
```javascript
541544
var parser = new Parser()
542545
.nest("header", {
543546
type: new Parser().uint32("length"),
@@ -551,8 +554,30 @@ context variables will be removed after the parsing process:
551554
return this.$root.header.length
552555
}
553556
}),
554-
})
557+
});
558+
```
555559

560+
- `$index` - This field references the actual index in array parsing. This
561+
variable will be available only when using the `length` mode for arrays.
562+
563+
```javascript
564+
var parser = new Parser()
565+
.nest("header", {
566+
type: new Parser().uint32("length"),
567+
})
568+
.nest("data", {
569+
type: new Parser()
570+
.uint32("value")
571+
.array("data", {
572+
type: new Parser().nest({
573+
type: new Parser().uint8("_tmp"),
574+
formatter: function(item) {
575+
return this.$index % 2 === 0 ? item._tmp : String.fromCharCode(item._tmp);
576+
}
577+
}),
578+
length: "$root.header.length"
579+
}),
580+
});
556581
```
557582

558583
## Examples

lib/binary_parser.ts

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -686,22 +686,23 @@ export class Parser {
686686
}
687687

688688
private addAliasedCode(ctx: Context) {
689+
ctx.pushCode(`function ${FUNCTION_PREFIX + this.alias}(offset, context) {`);
689690
ctx.pushCode(
690-
`function ${FUNCTION_PREFIX + this.alias}(offset, parent, root) {`
691+
`var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};`
691692
);
692693
ctx.pushCode(
693-
`var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};`
694+
`var ctx = Object.assign({$parent: null, $root: vars}, context || {})`
694695
);
695-
ctx.pushCode('vars.$parent = parent || null;');
696-
ctx.pushCode('vars.$root = root || vars;');
696+
ctx.pushCode(`vars = Object.assign(vars, ctx)`);
697697

698698
this.generate(ctx);
699699

700700
ctx.markResolved(this.alias);
701701
this.resolveReferences(ctx);
702702

703-
ctx.pushCode('delete vars.$parent;');
704-
ctx.pushCode('delete vars.$root;');
703+
ctx.pushCode(
704+
'Object.keys(ctx).forEach(function (item) { delete vars[item]; });'
705+
);
705706
ctx.pushCode('return { offset: offset, result: vars };');
706707
ctx.pushCode('}');
707708

@@ -1094,11 +1095,13 @@ export class Parser {
10941095
} else {
10951096
const parentVar = ctx.generateVariable();
10961097
const tempVar = ctx.generateTmpVariable();
1097-
ctx.pushCode(
1098-
`var ${tempVar} = ${
1099-
FUNCTION_PREFIX + type
1100-
}(offset, ${parentVar}, ${parentVar}.$root);`
1101-
);
1098+
ctx.pushCode(`var ${tempVar} = ${FUNCTION_PREFIX + type}(offset, {`);
1099+
ctx.pushCode(`$parent: ${parentVar},`);
1100+
ctx.pushCode(`$root: ${parentVar}.$root,`);
1101+
if (!this.options.readUntil && lengthInBytes === undefined) {
1102+
ctx.pushCode(`$index: ${length} - ${counter},`);
1103+
}
1104+
ctx.pushCode(`});`);
11021105
ctx.pushCode(
11031106
`var ${item} = ${tempVar}.result; offset = ${tempVar}.offset;`
11041107
);
@@ -1111,9 +1114,13 @@ export class Parser {
11111114
ctx.pushScope(item);
11121115
ctx.pushCode(`${item}.$parent = ${parentVar};`);
11131116
ctx.pushCode(`${item}.$root = ${parentVar}.$root;`);
1117+
if (!this.options.readUntil && lengthInBytes === undefined) {
1118+
ctx.pushCode(`${item}.$index = ${length} - ${counter},`);
1119+
}
11141120
type.generate(ctx);
11151121
ctx.pushCode(`delete ${item}.$parent`);
11161122
ctx.pushCode(`delete ${item}.$root`);
1123+
ctx.pushCode(`delete ${item}.$index`);
11171124
ctx.popScope();
11181125
}
11191126

@@ -1150,11 +1157,10 @@ export class Parser {
11501157
ctx.pushCode(`offset += ${PRIMITIVE_SIZES[type as PrimitiveTypes]}`);
11511158
} else {
11521159
const tempVar = ctx.generateTmpVariable();
1153-
ctx.pushCode(
1154-
`var ${tempVar} = ${
1155-
FUNCTION_PREFIX + type
1156-
}(offset, ${varName}.$parent, ${varName}.$root);`
1157-
);
1160+
ctx.pushCode(`var ${tempVar} = ${FUNCTION_PREFIX + type}(offset, {`);
1161+
ctx.pushCode(`$parent: ${varName}.$parent,`);
1162+
ctx.pushCode(`$root: ${varName}.$root,`);
1163+
ctx.pushCode(`});`);
11581164
ctx.pushCode(
11591165
`${varName} = ${tempVar}.result; offset = ${tempVar}.offset;`
11601166
);
@@ -1221,10 +1227,11 @@ export class Parser {
12211227
const parentVar = ctx.generateVariable();
12221228
const tempVar = ctx.generateTmpVariable();
12231229
ctx.pushCode(
1224-
`var ${tempVar} = ${
1225-
FUNCTION_PREFIX + this.options.type
1226-
}(offset, ${parentVar}, ${parentVar}.$root);`
1230+
`var ${tempVar} = ${FUNCTION_PREFIX + this.options.type}(offset, {`
12271231
);
1232+
ctx.pushCode(`$parent: ${parentVar},`);
1233+
ctx.pushCode(`$root: ${parentVar}.$root,`);
1234+
ctx.pushCode(`});`);
12281235
ctx.pushCode(
12291236
`${nestVar} = ${tempVar}.result; offset = ${tempVar}.offset;`
12301237
);
@@ -1337,10 +1344,11 @@ export class Parser {
13371344
const parentVar = ctx.generateVariable();
13381345
const tempVar = ctx.generateTmpVariable();
13391346
ctx.pushCode(
1340-
`var ${tempVar} = ${
1341-
FUNCTION_PREFIX + this.options.type
1342-
}(offset, ${parentVar}, ${parentVar}.$root);`
1347+
`var ${tempVar} = ${FUNCTION_PREFIX + this.options.type}(offset, {`
13431348
);
1349+
ctx.pushCode(`$parent: ${parentVar},`);
1350+
ctx.pushCode(`$root: ${parentVar}.$root,`);
1351+
ctx.pushCode(`});`);
13441352
ctx.pushCode(
13451353
`${nestVar} = ${tempVar}.result; offset = ${tempVar}.offset;`
13461354
);

test/composite_parser.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const suite = (Buffer) =>
1111
hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
1212
);
1313
}
14+
1415
describe('Array parser', function () {
1516
it('should parse array of primitive types', function () {
1617
var parser = Parser.start().uint8('length').array('message', {
@@ -507,6 +508,93 @@ const suite = (Buffer) =>
507508
child: { data: { v2: 0x0304 } },
508509
});
509510
});
511+
it('should be able to access to index context variable when using length', function () {
512+
var elementParser = new Parser()
513+
.uint8('key', {
514+
formatter: function (item) {
515+
return this.$index % 2 === 0 ? item : String.fromCharCode(item);
516+
},
517+
})
518+
.nest('data', {
519+
type: new Parser().array('value', {
520+
type: 'uint8',
521+
length: '$root.valueLength',
522+
}),
523+
});
524+
525+
var parser = Parser.start()
526+
.uint16le('length')
527+
.uint16le('valueLength')
528+
.array('message', {
529+
length: 'length',
530+
type: elementParser,
531+
});
532+
533+
var buffer = Buffer.from([
534+
0x02,
535+
0x00,
536+
0x02,
537+
0x00,
538+
0x50,
539+
0xd2,
540+
0x04,
541+
0x51,
542+
0xd3,
543+
0x04,
544+
]);
545+
assert.deepStrictEqual(parser.parse(buffer), {
546+
length: 0x02,
547+
valueLength: 0x02,
548+
message: [
549+
{ key: 0x50, data: { value: [0xd2, 0x04] } },
550+
{ key: 'Q', data: { value: [0xd3, 0x04] } },
551+
],
552+
});
553+
});
554+
it('should be able to access to index context variable when using length on named parser', function () {
555+
var elementParser = new Parser()
556+
.uint8('key', {
557+
formatter: function (item) {
558+
return this.$index % 2 === 0 ? item : String.fromCharCode(item);
559+
},
560+
})
561+
.nest('data', {
562+
type: new Parser().array('value', {
563+
type: 'uint8',
564+
length: '$root.valueLength',
565+
}),
566+
})
567+
.namely('ArrayLengthIndexTest');
568+
569+
var parser = Parser.start()
570+
.uint16le('length')
571+
.uint16le('valueLength')
572+
.array('message', {
573+
length: 'length',
574+
type: 'ArrayLengthIndexTest',
575+
});
576+
577+
var buffer = Buffer.from([
578+
0x02,
579+
0x00,
580+
0x02,
581+
0x00,
582+
0x50,
583+
0xd2,
584+
0x04,
585+
0x51,
586+
0xd3,
587+
0x04,
588+
]);
589+
assert.deepStrictEqual(parser.parse(buffer), {
590+
length: 0x02,
591+
valueLength: 0x02,
592+
message: [
593+
{ key: 0x50, data: { value: [0xd2, 0x04] } },
594+
{ key: 'Q', data: { value: [0xd3, 0x04] } },
595+
],
596+
});
597+
});
510598
});
511599

512600
describe('Choice parser', function () {
@@ -1097,6 +1185,7 @@ const suite = (Buffer) =>
10971185
function Person() {
10981186
this.name = '';
10991187
}
1188+
11001189
Person.prototype.toString = function () {
11011190
return '[object Person]';
11021191
};

0 commit comments

Comments
 (0)