06244a484515e9b58df902688ef9bc7f6ac654e7
[ton] / don / tags.py
1 import collections
2
3 VOID = 0x00
4 TRUE = 0x01
5 FALSE = 0x02
6 BOOL = (TRUE, FALSE)
7 INT8 = 0x10
8 INT16 = 0x11
9 INT32 = 0x12
10 INT64 = 0x13
11 FLOAT = 0x20
12 DOUBLE = 0x21
13 BINARY = 0x30
14 UTF8 = 0x31
15 UTF16 = 0x32
16 UTF32 = 0x33
17 LIST = 0x40
18 DICTIONARY = 0x41
19
20 DEFAULT_INTEGER_ENCODING = INT32
21 DEFAULT_DECIMAL_ENCODING = DOUBLE
22 DEFAULT_STRING_ENCODING = UTF8
23
24 TaggedObject = collections.namedtuple('TaggedObject', ['tag', 'value'])
25
26 _TYPES_TO_TAGS = {
27     int: DEFAULT_INTEGER_ENCODING,
28     float: DEFAULT_DECIMAL_ENCODING,
29     bytes: BINARY,
30     str: DEFAULT_STRING_ENCODING,
31     list: LIST,
32     dict: DICTIONARY,
33     collections.OrderedDict: DICTIONARY,
34 }
35
36 def _tag(o):
37     if isinstance(o, TaggedObject):
38         return o
39
40     if o is None:
41         return TaggedObject(tag = VOID, value = o)
42
43     if o is True:
44         return TaggedObject(tag = TRUE, value = o)
45
46     if o is False:
47         return TaggedObject(tag = FALSE, value = o)
48
49     return TaggedObject(tag = _TYPES_TO_TAGS[type(o)], value = o)
50
51 _NONE = TaggedObject(tag = VOID, value = None)
52 _TRUE = TaggedObject(tag = TRUE, value = True)
53 _FALSE = TaggedObject(tag = FALSE, value = False)
54
55 _TAGS_TO_IN_RANGE_PREDICATES = collections.OrderedDict([
56     (INT8,  lambda i: -128 <= i and i <= 127),
57     (INT16, lambda i: -32768 <= i and i <= 32767),
58     (INT32, lambda i: -2147483648 <= i and i <= 2147483647),
59     (INT64, lambda i: -9223372036854775808 <= i and i <= 9223372036854775807),
60 ])
61
62 class TooWideError(Exception):
63     pass
64
65 SMALLEST = object()
66
67 def autotag(o, **kwargs):
68     preferred_integer_tag = kwargs.pop('preferred_integer_tag', DEFAULT_INTEGER_ENCODING)
69     preferred_string_tag = kwargs.pop('preferred_string_tag', DEFAULT_STRING_ENCODING)
70
71     if kwargs:
72         raise TypeError("autotag() got an unexpected keyword argument '{}'".format(
73             list(kwargs.keys())[0],
74         ))
75
76     if isinstance(o, TaggedObject):
77         return o
78
79     if o is None:
80         return _NONE
81
82     if o is True:
83         return _TRUE
84
85     if o is False:
86         return _FALSE
87
88     if isinstance(o, int):
89         if preferred_integer_tag is not SMALLEST and _TAGS_TO_IN_RANGE_PREDICATES[preferred_integer_tag](o):
90             return TaggedObject(tag = preferred_integer_tag, value = o)
91
92         else:
93             for tag, in_range_predicate in _TAGS_TO_IN_RANGE_PREDICATES.items():
94                 if in_range_predicate(o):
95                     return TaggedObject(tag = tag, value = o)
96
97             raise TooWideError("Integer {} is too wide to be serialized")
98
99     if isinstance(o, float):
100         raise Exception('Unsupported type {}'.format(type(o)))
101
102     if isinstance(o, str):
103         # TODO Support SMALLEST for preferred string tag
104         return TaggedObject(tag = preferred_string_tag, value = o)
105
106     if isinstance(o, list):
107         return TaggedObject(
108             tag = LIST,
109             value = [
110                 autotag(
111                     i,
112                     preferred_integer_tag = preferred_integer_tag,
113                     preferred_string_tag = preferred_string_tag,
114                 ) for i in o
115             ],
116         )
117
118     if isinstance(o, dict):
119         return TaggedObject(
120             tag = DICTIONARY,
121             value = collections.OrderedDict([
122                 (
123                     autotag(
124                         key,
125                         preferred_integer_tag = preferred_integer_tag,
126                         preferred_string_tag = preferred_string_tag,
127                     ),
128                     autotag(
129                         value,
130                         preferred_integer_tag = preferred_integer_tag,
131                         preferred_string_tag = preferred_string_tag,
132                     ),
133                 ) for key, value in o.items()
134             ]),
135         )
136
137     raise Exception('Unsupported type {}'.format(type(o)))