construct icon indicating copy to clipboard operation
construct copied to clipboard

MetaFiled requires length attribute to be present on build

Open ruletko opened this issue 12 years ago • 3 comments

This is more like a question rather an issue. I'd expect MEtaField's related length fiekd to be autopopulated on building a structure, however this does not happen:

>>> from construct import *
>>> foo = Struct("foo",
... Byte("length"),
... MetaField("data", lambda ctx: ctx["length"])
... )

>>> foo.build(Container(data="test"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/pymodules/python2.6/construct/core.py", line 206, in build
    self.build_stream(obj, stream)
  File "/usr/lib/pymodules/python2.6/construct/core.py", line 214, in build_stream
    self._build(obj, stream, Container())
  File "/usr/lib/pymodules/python2.6/construct/core.py", line 662, in _build
    subobj = getattr(obj, sc.name)
AttributeError: 'Container' object has no attribute 'length'

Could you explain correct behavior?

ruletko avatar Mar 01 '12 04:03 ruletko

Part of me wants to say "use a PascalString," but that's not a good long-term solution, is it? The problem is mostly that there's no indication of the dependency of length on data in this Struct, so Construct is a bit bamboozled about how to build this. PascalString gets it right though.

MostAwesomeDude avatar Mar 06 '12 02:03 MostAwesomeDude

Interesting question, I haven't looked at PascalString yet to see how that works. Haven't really had to do any serious 'building' yet, all parsing

ZiglioUK avatar Mar 25 '12 12:03 ZiglioUK

In a library I created (not open sourced, unfortunately, as it is for my employer), I ended up doing the following. Unlike construct, the syntax for the DSL we ended up creating is more heavily metaclass based:

class PascalString(BaseMessage):
    length = LengthField(UBInt8())
    payload = VariableRawPayload(length)

Variable length fields require either a static length or a length_field parameter. These two are couple on message instances and I can pack a message with the correct length automatically:

>>> m = PascalString()
>>> m.payload = "Hello, world"
>>> m.pack()
"\x0cHello, World"

The invariant in our system is that when unpacking the length of the next field being unpacked must always be known immediately before it will be unpacked. In most cases this is the case. In other cases (for instance, a delimiter in a chunk) we currently just handle that outside of our library or create a special construct.

posborne avatar Mar 25 '12 16:03 posborne