return struct.pack('!BI', tags.TUPLE, len(payload)) + payload
+def _serialize_list(to):
+ assert isinstance(to.instance, list)
+
+ # TODO Actually handle this case somehow
+ assert len(to.instance) > 0
+
+ # TODO Do this a better way
+ serialized_items = [serialize(i) for i in to.instance]
+ list_tag = serialized_items[0][0]
+
+ def check_and_strip_prefix(b):
+ item_tag = b[0]
+ assert list_tag == item_tag
+ return b[1:]
+
+ payload = b''.join(check_and_strip_prefix(si) for si in serialized_items)
+
+ fmt = '!BBI'
+
+ return struct.pack(fmt, tags.LIST, list_tag, len(payload)) + payload
_TAGS_TO_SERIALIZERS = {
tags.NULL: _make_tag_only_serializer(tags.NULL, None),
tags.UTF16: _make_string_serializer(lambda s: s.encode('utf-16')),
tags.UTF32: _make_string_serializer(lambda s: s.encode('utf-32')),
tags.TUPLE: _serialize_tuple,
+ tags.LIST: _serialize_list,
}
def serialize(to):
return bytes_read, tags.TaggedObject(tag = tags.TUPLE, instance = tuple(instance))
+def _deserialize_list(b):
+ list_tag_bytes = b.read(1)
+ assert len(list_tag_bytes) == 1
+ list_tag = list_tag_bytes[0]
+
+ 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 = _TAGS_TO_PARSERS[list_tag](payload_stream)
+ total_bytes_read += partial_bytes_read
+ instance.append(item)
+
+ # TODO Return tags = (tags.LIST, list_tag) to function like a generic type
+ return bytes_read, tags.TaggedObject(tag = tags.LIST, instance = instance)
+
_TAGS_TO_PARSERS = {
tags.NULL: _make_tag_only_parser(tags.NULL, None),
tags.TRUE: _make_tag_only_parser(tags.TRUE, True),
tags.UTF16: _make_string_deserializer(tags.UTF16, lambda b: b.decode('utf-16')),
tags.UTF32: _make_string_deserializer(tags.UTF32, lambda b: b.decode('utf-32')),
tags.TUPLE: _deserialize_tuple,
+ tags.LIST: _deserialize_list,
}
def _deserialize_partial(b):
UTF16 = 0x22
UTF32 = 0x23
TUPLE = 0x30
+LIST = 0x31
TaggedObject = collections.namedtuple(
'TaggedObject',
return serializer
+def _indent(s, depth = 2):
+ return '\n'.join(' ' * depth + line for line in s.split('\n'))
+
+def _serialize_list(to):
+ assert isinstance(to.instance, list)
+
+ return '[\n' + _indent(',\n'.join(serialize(i) for i in to.instance)) + '\n]'
+
_SERIALIZERS = {
tags.NULL: _make_literal_serializer(None, 'null'),
tags.TRUE: _make_literal_serializer(True, 'true'),
tags.UTF8: _make_string_serializer('utf8'),
tags.UTF16: _make_string_serializer('utf16'),
tags.UTF32: _make_string_serializer('utf32'),
+ tags.LIST: _serialize_list,
}
def serialize(to):
return deserializer
+def _deserialize_list(s):
+ s = s.lstrip()
+
+ if not s.startswith('['):
+ return False, None, None
+
+ instance = []
+
+ s = s[1:].lstrip()
+
+ succeeded, result, s = _deserialize_one(s)
+
+ # TODO Handle empty lists
+
+ if succeeded:
+ instance.append(result)
+
+ s = s.lstrip()
+
+ while not s.startswith(']'):
+ assert s.startswith(',')
+ s = s[1:].lstrip()
+
+ succeeded, result, s = _deserialize_one(s)
+
+ # TODO Handle trailing commas
+ assert succeeded
+ instance.append(result)
+ s = s.lstrip()
+
+ assert s.startswith(']')
+ return True, tags.TaggedObject(tag = tags.LIST, instance = instance), s[1:]
+
_DESERIALIZERS = [
_make_literal_deserializer(tags.NULL, None, 'null'),
_make_literal_deserializer(tags.TRUE, True, 'true'),
_make_string_deserializer(tags.UTF8, 'utf8'),
_make_string_deserializer(tags.UTF16, 'utf16'),
_make_string_deserializer(tags.UTF32, 'utf32'),
+ _deserialize_list,
]
-def deserialize(s):
+def _deserialize_one(s):
for deserializer in _DESERIALIZERS:
succeeded, result, remaining = deserializer(s)
if succeeded:
- assert remaining == ''
- return result
+ return succeeded, result, remaining
+
+ return False, None, None
+
+def deserialize(s):
+ succeeded, result, remaining = _deserialize_one(s)
+
+ assert succeeded
+ assert remaining == ''
- raise Exception()
+ return result
),
b'\x30\x00\x00\x00\x03\x01\x03\x07'
),
+ (
+ tags.TaggedObject(
+ tag = tags.LIST,
+ instance = [
+ tags.TaggedObject(tag = tags.UINT8, instance = 9),
+ tags.TaggedObject(tag = tags.UINT8, instance = 22),
+ tags.TaggedObject(tag = tags.UINT8, instance = 36),
+ ],
+ ),
+ b'\x31\x03\x00\x00\x00\x03\x09\x16\x24',
+ ),
]
class BinarySerializeTests(unittest.TestCase):
(tags.TaggedObject(tags.UTF8, 'Lol!'), 'utf8"Lol!"'),
(tags.TaggedObject(tags.UTF16, 'かわ'), 'utf16"かわ"'),
(tags.TaggedObject(tags.UTF32, '漢'), 'utf32"漢"'),
+ (
+ tags.TaggedObject(
+ tag = tags.LIST,
+ instance = [
+ tags.TaggedObject(tag = tags.UINT8, instance = 9),
+ tags.TaggedObject(tag = tags.UINT8, instance = 22),
+ tags.TaggedObject(tag = tags.UINT8, instance = 36),
+ ],
+ ),
+ '[\n 9u8,\n 22u8,\n 36u8\n]'
+ ),
]
class TextSerializeTests(unittest.TestCase):