21 DEFAULT_INTEGER_ENCODING = INT32
22 DEFAULT_DECIMAL_ENCODING = DOUBLE
23 DEFAULT_STRING_ENCODING = UTF8
25 TaggedObject = collections.namedtuple('TaggedObject', ['tag', 'value'])
28 int: DEFAULT_INTEGER_ENCODING,
29 float: DEFAULT_DECIMAL_ENCODING,
31 str: DEFAULT_STRING_ENCODING,
34 collections.OrderedDict: DICTIONARY,
38 if isinstance(o, TaggedObject):
42 return TaggedObject(tag = VOID, value = o)
45 return TaggedObject(tag = TRUE, value = o)
48 return TaggedObject(tag = FALSE, value = o)
50 return TaggedObject(tag = _TYPES_TO_TAGS[type(o)], value = o)
52 def serialize_tag_only_type(o):
55 def make_serializer_from_pack_format_string(pfs):
57 return struct.pack(pfs, i)
60 def make_string_serializer_from_encoder(e):
63 return struct.pack('!I', len(encoded)) + encoded
66 def serialize_list(items):
67 # TODO Enforce that items are all the same type
68 items = [tag(i) for i in items]
73 item_tag = items[0].tag
75 item_serializer = _SERIALIZERS[item_tag]
76 items = [item_serializer(i.value) for i in items]
77 item_length = len(items)
78 items = b''.join(items)
79 byte_length = len(items)
80 return struct.pack('!BII', item_tag, byte_length, item_length) + items
82 def serialize_dict(d):
86 key_serializer = _SERIALIZERS[UTF8]
88 for key, value in d.items():
89 assert isinstance(key, str)
91 serialized += key_serializer(key) + _binary_serialize(value)
93 byte_length = len(serialized)
94 return struct.pack('!II', byte_length, item_length) + serialized
97 VOID: serialize_tag_only_type,
98 TRUE: serialize_tag_only_type,
99 FALSE: serialize_tag_only_type,
100 INT8: make_serializer_from_pack_format_string('!b'),
101 INT16: make_serializer_from_pack_format_string('!h'),
102 INT32: make_serializer_from_pack_format_string('!i'),
103 FLOAT: make_serializer_from_pack_format_string('!f'),
104 DOUBLE: make_serializer_from_pack_format_string('!d'),
105 BINARY: make_string_serializer_from_encoder(lambda b: b),
106 UTF8: make_string_serializer_from_encoder(lambda s: s.encode('utf-8')),
107 UTF16: make_string_serializer_from_encoder(lambda s: s.encode('utf-16')),
108 UTF32: make_string_serializer_from_encoder(lambda s: s.encode('utf-32')),
109 LIST: serialize_list,
110 DICTIONARY: serialize_dict,
113 def _binary_serialize(o):
115 return struct.pack('!B', o.tag) + _SERIALIZERS[o.tag](o.value)
117 ParseResult = collections.namedtuple(
126 _FAILED_PARSE_RESULT = ParseResult(success = False, value = None, remaining = None)
128 _BYTE_SIZES_TO_UNPACK_FORMATS = {
135 def make_integer_parser(size_in_bytes):
136 unpack_format = _BYTE_SIZES_TO_UNPACK_FORMATS[size_in_bytes]
138 def integer_parser(source):
139 value = struct.unpack(unpack_format, source[:size_in_bytes])[0]
140 remaining = source[size_in_bytes:]
142 return ParseResult(success = True, value = value, remaining = remaining)
144 return integer_parser
146 def binary64_parser(source):
149 value = struct.unpack('!d', source[:8])[0],
150 remaining = source[8:],
153 def make_string_parser(decoder):
154 def string_parser(source):
155 length = struct.unpack('!I', source[:4])[0]
159 value = decoder(source[:length]),
160 remaining = source[length:],
165 def list_parser(source):
167 parser = _TAGS_TO_PARSERS[tag]
170 byte_length, items_length = struct.unpack('!II', source[:8])
173 remaining = source[byte_length:]
174 source = source[:byte_length]
176 def item_iterator(source):
179 while len(source) > 0:
180 parse_result = parser(source)
182 if parse_result.success:
184 yield parse_result.value
185 source = parse_result.remaining
187 assert count == items_length
191 value = item_iterator(source),
192 remaining = remaining,
195 def dictionary_parser(source):
196 key_parser = _TAGS_TO_PARSERS[UTF8]
198 byte_length, item_length = struct.unpack('!II', source[:8])
201 remaining = source[byte_length:]
202 source = source[:byte_length]
204 def kvp_iterator(source):
207 while len(source) > 0:
209 key_parse_result = key_parser(source)
210 key, source = key_parse_result.value, key_parse_result.remaining
211 value_parse_result = _object_parser(source)
212 value, source = value_parse_result.value, value_parse_result.remaining
216 assert count == item_length
220 value = collections.OrderedDict(kvp_iterator(source)),
221 remaining = remaining,
226 VOID: lambda r: ParseResult(True, None, r),
227 TRUE: lambda r: ParseResult(True, True, r),
228 FALSE: lambda r: ParseResult(True, False, r),
229 INT8: make_integer_parser(1),
230 INT16: make_integer_parser(2),
231 INT32: make_integer_parser(4),
232 INT64: make_integer_parser(8),
233 DOUBLE: binary64_parser,
234 BINARY: make_string_parser(lambda b : b),
235 UTF8: make_string_parser(lambda b : b.decode('utf-8')),
236 UTF16: make_string_parser(lambda b : b.decode('utf-16')),
237 UTF32: make_string_parser(lambda b : b.decode('utf-32')),
239 DICTIONARY: dictionary_parser,
242 def _object_parser(source):
243 return _TAGS_TO_PARSERS[source[0]](source[1:])
245 def _parse(parser, source, consume_all = True):
246 result = parser(source)
248 if result.success and result.remaining == b'':
251 raise Exception('Unparsed trailing bytes: {}'.format(result.remaining))
253 def _binary_deserialize(b):
254 return _parse(_object_parser, b)
256 Serializer = collections.namedtuple('Serializer', ['serialize', 'deserialize'])
259 serialize = _binary_serialize,
260 deserialize = _binary_deserialize,
263 def binary_to_string(b):
264 return string.serialize(binary.deserialize(b))
266 def string_to_binary(s):
267 return binary.serialize(string.deserialize(s))