Skip to content

Support for variable-length, non-greedy Payload() #47

Description

@martinpelikan

tl;dr: It would be nice if Payload() consumed data until the first None/null-character if no length is specified, rather than the last instance of it as it appears to currently be doing.

I'm running into a problem with a Structure containing two null-delimited strings. The protocol doesn't specify information about the length of these strings. I wrote my own protocol parser before discovery this library, so I already have the protocol broken down into logical segments/Structures, but I was hoping to get rid of my hack job in favour of using Suitcase to parse the individual fields.

Reading the docs, I see this is probably an explanation for my situation:

Parameters: length_provider – The LengthField with which this variable length payload is associated. If not included, it is assumed that the length_provider should consume the remainder of the bytes available in the string. This is only valid in cases where the developer knows that they will be dealing with a fixed sequence of bytes (already boxed).

What would be the best way to work within the framework with such a constraint? Is there any way to make Payload() be lazy rather than greedy?

success.py:

fromsuitcase.structureimportStructurefromsuitcase.fieldsimport (
SBInt8,
SBInt16,
UBInt32,
SBInt64,
Payload,
Magic
)
classHeader(Structure):
a=UBInt32()
b=SBInt8()
c=Payload()
e=SBInt64()
f=SBInt16()
g=SBInt16()
h=SBInt16()
i=SBInt16()

Success output:

In [1]: import success
In [2]: s = success.Header()
In [3]: s.unpack(b'\x00\x00\x007\n123.45.67.89-8888\x00Some-String-2\x00\x00\x00\x01Z\xa5\xfb\xc8\xab\x00\x0e\x00\x15\x00\x01\x00\x00')
In [4]: s
Out[4]: Header (
a=55,
b=10,
c=b'123.45.67.89-8888\x00Some-String-2\x00',
e=1488843425963,
f=14,
g=21,
h=1,
i=0,
)

failure.py:

fromsuitcase.structureimportStructurefromsuitcase.fieldsimport (
SBInt8,
SBInt16,
UBInt32,
SBInt64,
Payload,
Magic
)
classHeader(Structure):
header_size=UBInt32()
version=SBInt8()
nis_id=Payload()
msg_id=Payload()
timestamp=SBInt64()
event_size=SBInt16()
job_discard_size=SBInt16()
num_jobs=SBInt16()
num_discards=SBInt16()

Failure output:

In [1]: importfailureIn [2]: f=failure.Header()
In [3]: f.unpack(b'\x00\x00\x007\n123.45.67.89-8888\x00Some-String-2\x00\x00\x00\x01Z\xa5\xfb\xc8\xab\x00\x0e\x00\x15\x00\x01\x00\x00')
---------------------------------------------------------------------------SuitcaseParseErrorTraceback (mostrecentcalllast)
<ipython-input-3-91ea443f72a0>in<module>()
---->1f.unpack(b'\x00\x00\x007\n123.45.67.89-8888\x00Some-String-2\x00\x00\x00\x01Z\xa5\xfb\xc8\xab\x00\x0e\x00\x15\x00\x01\x00\x00')
/home/mpelikan/.local/lib/python3.6/site-packages/suitcase/structure.pyinunpack(self, data, trailing)
339340defunpack(self, data, trailing=False):
-->341returnself._packer.unpack(data, trailing)
342343defpack(self):
/home/mpelikan/.local/lib/python3.6/site-packages/suitcase/structure.pyinunpack(self, data, trailing)
62defunpack(self, data, trailing=False):
63stream=BytesIO(data)
--->64self.unpack_stream(stream)
65stream.tell()
66iftrailing:
/home/mpelikan/.local/lib/python3.6/site-packages/suitcase/structure.pyinunpack_stream(self, stream)
150"%r we tried to read %s bytes but "151"we were only able to read %s."%-->152 (_name, length, len(data)))
153try:
154field.unpack(data)
SuitcaseParseError: Whileattemptingtoparsefield'd'wetriedtoreadNonebytesbutwewereonlyabletoread32.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions