keichi / binary-parser

A blazing-fast declarative parser builder for binary data
MIT License
868 stars 136 forks source link

How to parse nested T-L-V with variable field length ? #126

Closed m67hoff closed 3 years ago

m67hoff commented 5 years ago

Hi, I'm experimenting with this binary parser but was not successful. How can I parse "Type - Length - Value" binary input where each value itself is a nested T-L-V list? I was successful parsing the first T-L-V into an array of buffers. But now parsing this would need something like the length of the parent into the choice:

var field = new Parser()
    .int8("type")
    .int32be("len")
    .buffer("data", {
        length: "len"
    })

var bufList = new Parser()
    .array('fieldList', {
        type: field,
        readUntil: 'eof'
    })

workrs and gives me an array:

{ fieldList:
   [ { type: 5,
       len: 110,
       data:
        <Buffer 01 00 00 00 44 4c 69 6e 75 78 20 33 2e 31 30 2e 30 2d 39 35 37 2e 31 2e 33 2e 65 6c 37 2e 78 38 36 5f 36 34 20 23 31 20 53 4d 50 20 54 68 75 20 4e 6f ... 60 more bytes> },
     { type: 2,
       len: 744,
       data:
        <Buffer 01 00 00 00 b9 04 00 00 00 0b 64 72 2d 78 72 2d 78 72 2d 78 00 06 00 00 00 07 61 73 70 65 72 61 00 08 00 00 00 07 61 73 70 65 72 61 00 02 00 00 00 08 ... 694 more bytes> } ] }

but now the value type 5 "this means type is info" is again a nested T-L-V list:

something like:

var field = new Parser()
    .int8("type")
    .int32be("field_length")
    .choice("data", {
        tag: "type",
        choices: {
            2: Parser.start().buffer("dir", { length: 'field_length' }),
            5: Parser.start().buffer("info", { length: 'field_length' })
        }
    })

seems not to work. It seems there is no way to use the "length" for a later parser. But the "manual" explicit states that array + choice would be "good" for TLV parsing. Is there an example for this ? The examples provided did not help.

keichi commented 5 years ago

Hi, please try the following:

var field = new Parser()
    .int8("type")
    .int32be("field_length")
    .choice("data", {
        tag: "type",
        choices: {
            2: Parser.start().buffer("dir", { length: function(vars) { return vars.field_length; } }),
            5: Parser.start().buffer("info", { length: function(vars) { return vars.field_length; } })
        }
    })
m67hoff commented 5 years ago

Hi, thanks for help - but no, din't work! The buffer is still empty (it runs w/o error):

{ fieldList:
   [ { type: 5, field_length: 110, data: { info: <Buffer > } } ] }
panuhorsmalahti commented 4 years ago

The vars parameter works at least for me (e.g. it has access to the parent variables).

keichi commented 3 years ago

This should now be possible with $parent or $root introduced by #166.