X-Git-Url: https://code.kerkeslager.com/?p=sandbox;a=blobdiff_plain;f=serial%2Fbinary.py;h=fabcd472f5d2060809f3ea26497b6b2a4d280b8e;hp=7ce58b708a0f255391a40e02cd163ecfa4d4af4a;hb=07d76f810ece3b43bb837e81ceb3a8b441348ba3;hpb=dd212c690a8c472d4dabf869f9349510ac906c90 diff --git a/serial/binary.py b/serial/binary.py index 7ce58b7..fabcd47 100644 --- a/serial/binary.py +++ b/serial/binary.py @@ -18,6 +18,7 @@ TAG_BINARY = 0x20 TAG_UTF8 = 0x21 TAG_UTF16 = 0x22 TAG_UTF32 = 0x23 +TAG_TUPLE = 0x30 TaggedObject = collections.namedtuple( 'TaggedObject', @@ -54,6 +55,16 @@ def _make_string_serializer(encoder): return serializer +def _serialize_tuple(to): + assert isinstance(to.instance, tuple) + + payload = b''.join(serialize(item) for item in to.instance) + + fmt = '!BI' + + return struct.pack('!BI', TAG_TUPLE, len(payload)) + payload + + _TAGS_TO_SERIALIZERS = { TAG_NULL: _make_tag_only_serializer(TAG_NULL, None), TAG_TRUE: _make_tag_only_serializer(TAG_TRUE, True), @@ -70,6 +81,7 @@ _TAGS_TO_SERIALIZERS = { TAG_UTF8: _make_string_serializer(lambda s: s.encode('utf-8')), TAG_UTF16: _make_string_serializer(lambda s: s.encode('utf-16')), TAG_UTF32: _make_string_serializer(lambda s: s.encode('utf-32')), + TAG_TUPLE: _serialize_tuple, } def serialize(to): @@ -77,7 +89,7 @@ def serialize(to): def _make_tag_only_parser(tag, value): def parser(b): - return TaggedObject(tag = tag, instance = value) + return 0, TaggedObject(tag = tag, instance = value) return parser @@ -89,25 +101,48 @@ def _make_struct_deserializer(tag, fmt): def parser(b): b = b.read(size) assert len(b) == size - return TaggedObject(tag = tag, instance = unpacker(b)[0]) + return size, TaggedObject(tag = tag, instance = unpacker(b)[0]) return parser +_LENGTH_FMT = '!I' +_LENGTH_FMT_SIZE = struct.calcsize(_LENGTH_FMT) + +def _read_length_then_payload(b): + length_b = b.read(_LENGTH_FMT_SIZE) + assert len(length_b) == _LENGTH_FMT_SIZE + length = struct.unpack(_LENGTH_FMT, length_b)[0] + + payload = b.read(length) + assert len(payload) == length + return _LENGTH_FMT_SIZE + length, payload + def _make_string_deserializer(tag, decoder): fmt = '!I' size = struct.calcsize(fmt) unpacker = functools.partial(struct.unpack, fmt) def parser(b): - length_b = b.read(size) - assert len(length_b) == size - length = unpacker(length_b)[0] - s = b.read(length) - assert len(s) == length - return TaggedObject(tag = tag, instance = decoder(s)) + bytes_read, payload = _read_length_then_payload(b) + return bytes_read, TaggedObject(tag = tag, instance = decoder(payload)) return parser +def _deserialize_tuple(b): + bytes_read, payload = _read_length_then_payload(b) + + payload_stream = io.BytesIO(payload) + + total_bytes_read = 0 + instance = [] + + while total_bytes_read < len(payload): + partial_bytes_read, item = _deserialize_partial(payload_stream) + total_bytes_read += partial_bytes_read + instance.append(item) + + return bytes_read, TaggedObject(tag = TAG_TUPLE, instance = tuple(instance)) + _TAGS_TO_PARSERS = { TAG_NULL: _make_tag_only_parser(TAG_NULL, None), TAG_TRUE: _make_tag_only_parser(TAG_TRUE, True), @@ -124,15 +159,20 @@ _TAGS_TO_PARSERS = { TAG_UTF8: _make_string_deserializer(TAG_UTF8, lambda b: b.decode('utf-8')), TAG_UTF16: _make_string_deserializer(TAG_UTF16, lambda b: b.decode('utf-16')), TAG_UTF32: _make_string_deserializer(TAG_UTF32, lambda b: b.decode('utf-32')), + TAG_TUPLE: _deserialize_tuple, } +def _deserialize_partial(b): + tag = b.read(1) + assert len(tag) == 1 + bytes_read, to = _TAGS_TO_PARSERS[tag[0]](b) + return bytes_read + 1, to + def deserialize(b): if isinstance(b, bytes): b = io.BytesIO(b) - tag = b.read(1)[0] - - result = _TAGS_TO_PARSERS[tag](b) + bytes_read, result = _deserialize_partial(b) remainder = b.read()