4 from ton import tags, _shared
6 def _binary_serialize_tag_only_type(o):
9 def _pack_format_string_to_binary_serializer(pfs):
11 return struct.pack(pfs, i)
14 def _encoder_to_binary_serializer(e):
17 return struct.pack('!I', len(encoded)) + encoded
20 def _binary_serialize_list(items):
21 # TODO Enforce that items are all the same type
22 items = [tags._tag(i) for i in items]
27 item_tag = items[0].tag
29 item_serializer = _BINARY_SERIALIZERS[item_tag]
30 items = [item_serializer(i.value) for i in items]
31 item_length = len(items)
32 items = b''.join(items)
33 byte_length = len(items)
34 return struct.pack('!BII', item_tag, byte_length, item_length) + items
36 def _serialize_key(o):
38 assert o.tag in tags.STRING_TAGS
39 return struct.pack('!B', o.tag) + _BINARY_SERIALIZERS[o.tag](o.value)
41 def _binary_serialize_dict(d):
45 key_serializer = _BINARY_SERIALIZERS[tags.UTF8]
47 for key, value in d.items():
49 serialized += _serialize_key(key) + serialize(value)
51 byte_length = len(serialized)
52 return struct.pack('!II', byte_length, item_length) + serialized
54 _BINARY_SERIALIZERS = {
55 tags.VOID: _binary_serialize_tag_only_type,
56 tags.TRUE: _binary_serialize_tag_only_type,
57 tags.FALSE: _binary_serialize_tag_only_type,
58 tags.INT8: _pack_format_string_to_binary_serializer('!b'),
59 tags.INT16: _pack_format_string_to_binary_serializer('!h'),
60 tags.INT32: _pack_format_string_to_binary_serializer('!i'),
61 tags.BINARY: _encoder_to_binary_serializer(lambda b: b),
62 tags.UTF8: _encoder_to_binary_serializer(lambda s: s.encode('utf-8')),
63 tags.UTF16: _encoder_to_binary_serializer(lambda s: s.encode('utf-16')),
64 tags.UTF32: _encoder_to_binary_serializer(lambda s: s.encode('utf-32')),
65 tags.LIST: _binary_serialize_list,
66 tags.DICTIONARY: _binary_serialize_dict,
71 return struct.pack('!B', o.tag) + _BINARY_SERIALIZERS[o.tag](o.value)
73 _BYTE_SIZES_TO_UNPACK_FORMATS = {
80 def make_integer_parser(size_in_bytes):
81 unpack_format = _BYTE_SIZES_TO_UNPACK_FORMATS[size_in_bytes]
83 def integer_parser(source):
84 value = struct.unpack(unpack_format, source[:size_in_bytes])[0]
85 remaining = source[size_in_bytes:]
87 return _shared.ParseResult(success = True, value = value, remaining = remaining)
91 def binary64_parser(source):
92 return _shared.ParseResult(
94 value = struct.unpack('!d', source[:8])[0],
95 remaining = source[8:],
98 def make_string_parser(decoder):
99 def string_parser(source):
100 length = struct.unpack('!I', source[:4])[0]
102 return _shared.ParseResult(
104 value = decoder(source[:length]),
105 remaining = source[length:],
110 def _list_parser(source):
112 parser = _TAGS_TO_PARSERS[tag]
115 byte_length, items_length = struct.unpack('!II', source[:8])
118 remaining = source[byte_length:]
119 source = source[:byte_length]
121 def item_iterator(source):
124 while len(source) > 0:
125 parse_result = parser(source)
127 if parse_result.success:
129 yield parse_result.value
130 source = parse_result.remaining
132 assert count == items_length
134 return _shared.ParseResult(
136 value = item_iterator(source),
137 remaining = remaining,
140 def dictionary_parser(source):
141 key_parser = _TAGS_TO_PARSERS[tags.UTF8]
143 byte_length, item_length = struct.unpack('!II', source[:8])
146 remaining = source[byte_length:]
147 source = source[:byte_length]
149 def kvp_iterator(source):
152 while len(source) > 0:
154 key_parse_result = key_parser(source)
155 key, source = key_parse_result.value, key_parse_result.remaining
156 value_parse_result = _object_parser(source)
157 value, source = value_parse_result.value, value_parse_result.remaining
161 assert count == item_length
163 return _shared.ParseResult(
165 value = collections.OrderedDict(kvp_iterator(source)),
166 remaining = remaining,
171 tags.VOID: lambda r: _shared.ParseResult(True, None, r),
172 tags.TRUE: lambda r: _shared.ParseResult(True, True, r),
173 tags.FALSE: lambda r: _shared.ParseResult(True, False, r),
174 tags.INT8: make_integer_parser(1),
175 tags.INT16: make_integer_parser(2),
176 tags.INT32: make_integer_parser(4),
177 tags.INT64: make_integer_parser(8),
178 tags.BINARY: make_string_parser(lambda b : b),
179 tags.UTF8: make_string_parser(lambda b : b.decode('utf-8')),
180 tags.UTF16: make_string_parser(lambda b : b.decode('utf-16')),
181 tags.UTF32: make_string_parser(lambda b : b.decode('utf-32')),
182 tags.LIST: _list_parser,
183 tags.DICTIONARY: dictionary_parser,
186 def _object_parser(source):
187 return _TAGS_TO_PARSERS[source[0]](source[1:])
189 def _parse(parser, source):
190 result = parser(source)
192 if result.success and result.remaining == b'':
195 raise Exception('Unparsed trailing bytes: {}'.format(result.remaining))
198 return _parse(_object_parser, b)