keichi / binary-parser

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

Formatter and assert functions called too early for bitN() #160

Closed lff5 closed 2 years ago

lff5 commented 3 years ago

Hi all.

I would like to format multi-bit field result. I define formatter function but as seen from the generated code, formatter is called before the actual bitfield is extracted. Same holds true for assert function for bitfields. I assume formatting was not intended functionality for bitfields, but for my payload parsers it would be super-duper awesome.

Original code:

var Parser = require("binary-parser").Parser;

var test_sub = new Parser()
  .endianess('little')
  .bit3("my_param", {
      formatter: function(arg) {
        console.log(arg);
        return arg*arg; // should modify the value
      }
  })
  .bit5("the_other_param")

var test_parser = new Parser()
  .endianess('little')
  .uint8("packet_type", { assert: 0x04 })
  .nest("bitfields", {"type":test_sub});

var buf = Buffer.from("0482", "hex");

var res = test_parser.parse(buf);
console.log(JSON.stringify(res, null, 2));

Compiled code:

(function anonymous(imports,TextDecoder
) {
return function (buffer, constructorFn) { var dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);
var offset = 0;
var vars = {};
vars.packet_type = dataView.getUint8(offset, false);
offset += 1;
if (4 !== vars.packet_type) {
throw new Error("Assert error: vars.packet_type is " + 4);
}
vars.bitfields = {};
vars.bitfields.my_param = imports[0].call(this, vars.bitfields.my_param); // tries to modify but its undefined here
var $tmp0 = dataView.getUint8(offset);
offset += 1;
vars.bitfields.my_param = $tmp0 >> 0 & 7; // after calling the formatter now it extracts the value
vars.bitfields.the_other_param = $tmp0 >> 3 & 31;
return vars;
 };
})

Console output:

undefined
{
  "packet_type": 4,
  "bitfields": {
    "my_param": 2,
    "the_other_param": 16
  }
}
lff5 commented 3 years ago

Duplicate of #29 Duplicate of #71 Duplicate of #56 Seems like a needed feature! Even pull request pending: https://github.com/functoid/binary-parser/commit/6ceacd4cbc8f093ad4f4a5b60ddebf07901ed953

I am going with .nest() workaround so all formatting is handled in upper layer in single function. Fortunately nest varName "" does not produce new level of depth - keeping the nest layer transparent.

var test_sub = new Parser()
  .nest("", {
    type: new Parser().endianess('little')
      .bit2("param1")
      .bit3("my_param")
      .bit2("the_other_param2"),
    formatter: function(arg) {
      console.log(arg);
      arg.param1 = arg.param1*arg.param1;
      return arg;
    },
  });