diff --git a/README.md b/README.md index c16999a7..4e3ace6d 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,30 @@ Jump to `offset`, execute parser for `type` and rewind to previous offset. start of the input buffer. Can be a string `[u]int{8, 16, 32, 64}{le, be}` or an user defined Parser object. +### saveOffset(name [,options]) +Save the current buffer offset as key `name`. This function is only useful when called after another function which would advance the internal buffer offset. + +```javascript +var parser = new Parser() + // this call advances the buffer offset by + // a variable (i.e. unknown to us) number of bytes + .string('name', { + zeroTerminated: true + }) + // this variable points to an absolute position + // in the buffer + .uint32('seekOffset') + // now, save the "current" offset in the stream + // as the variable "currentOffset" + .saveOffset('currentOffset') + // finally, use the saved offset to figure out + // how many bytes we need to skip + .skip(function() { + return this.seekOffset - this.currentOffset; + }) + ... // the parser would continue here +``` + ### skip(length) Skip parsing for `length` bytes. diff --git a/lib/binary_parser.ts b/lib/binary_parser.ts index 4a49e88b..fb5865fb 100644 --- a/lib/binary_parser.ts +++ b/lib/binary_parser.ts @@ -35,6 +35,7 @@ type ComplexTypes = | 'nest' | 'skip' | 'pointer' + | 'saveoffset' | ''; type Endianess = 'be' | 'le'; @@ -151,6 +152,7 @@ const CAPITILIZED_TYPE_NAMES: { [key in Types]: string } = { nest: 'Nest', skip: 'Skip', pointer: 'Pointer', + saveoffset: 'SaveOffset', '': '', }; @@ -539,6 +541,10 @@ export class Parser { return this.setNextParser('pointer', varName, options); } + saveoffset(varName: string, options?: ParserOptions) { + return this.setNextParser('saveoffset', varName, options); + } + endianess(endianess: 'little' | 'big') { switch (endianess.toLowerCase()) { case 'little': @@ -762,6 +768,9 @@ export class Parser { case 'pointer': this.generatePointer(ctx); break; + case 'saveoffset': + this.generateSaveOffset(ctx); + break; } this.generateAssert(ctx); } @@ -1108,4 +1117,9 @@ export class Parser { // Restore offset ctx.pushCode(`offset = ${tempVar};`); } + + private generateSaveOffset(ctx: Context) { + const varName = ctx.generateVariable(this.varName); + ctx.pushCode(`${varName} = offset`); + } } diff --git a/test/composite_parser.js b/test/composite_parser.js index e3b22d04..0f9407dd 100644 --- a/test/composite_parser.js +++ b/test/composite_parser.js @@ -995,6 +995,48 @@ describe('Composite parser', function() { }); }); + describe('SaveOffset', () => { + it('should save the offset', () => { + const buff = Buffer.from([0x01, 0x00, 0x02]); + const parser = Parser.start() + .int8('a') + .int16('b') + .saveoffset('bytesRead'); + + assert.deepEqual(parser.parse(buff), { + a: 1, + b: 2, + bytesRead: 3, + }); + }); + + 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'); + + assert.deepEqual(parser.parse(buff), { + a: 1, + b: 2, + bytesRead: 1, + }); + }); + + 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'); + + assert.deepEqual(parser.parse(buff), { + name: 'test', + bytesRead: 5, + }); + }); + }); + describe('Utilities', function() { it('should count size for fixed size structs', function() { var parser = Parser.start()