-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserializer.py
More file actions
152 lines (127 loc) · 4.97 KB
/
Copy pathserializer.py
File metadata and controls
152 lines (127 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
from statistics import variance
from enum import IntEnum
from remerkleable.basic import uint8, uint16, uint64, uint128, uint256, byte, boolean, uint32
from remerkleable.complex import Container, Vector, List
from remerkleable.bitfields import Bitvector, Bitlist
from remerkleable.union import Union
BYTES_PER_LENGTH_OFFSET = 4
BITS_PER_BYTE = 8
def _basic_type(value):
typ = _type_number(value)
if typ == TypeNumber.BOOLEAN:
return value.to_bytes(1, 'little')
elif typ == TypeNumber.BYTE:
return value.to_bytes(1, 'little')
elif typ == TypeNumber.UINT8:
return value.to_bytes(1, 'little')
elif typ == TypeNumber.UINT16:
return value.to_bytes(2, 'little')
elif typ == TypeNumber.UINT32:
return value.to_bytes(4, 'little')
elif typ == TypeNumber.UINT64:
return value.to_bytes(8, 'little')
elif typ == TypeNumber.UINT128:
return value.to_bytes(16, 'little')
elif typ == TypeNumber.UINT256:
return value.to_bytes(32, 'little')
return b""
# def _is_composite_type(object):
# return isinstance(object, Container) or isinstance(object, List) or isinstance(objec)
class TypeNumber(IntEnum):
BOOLEAN = 0
BYTE = 1
UINT8 = 1
UINT16 = 2
UINT32 = 3
UINT64 = 4
UINT128 = 5
UINT256 = 6
CONTAINER = 7
VECTOR = 8
BIT_VECTOR = 9
LIST = 10
BIT_LIST = 11
UNION = 12
def _type_number(object) -> TypeNumber:
if isinstance(object, boolean):
return TypeNumber.BOOLEAN
elif isinstance(object, byte) or isinstance(object, uint8):
return TypeNumber.UINT8
elif isinstance(object, uint16):
return TypeNumber.UINT16
elif isinstance(object, uint32):
return TypeNumber.UINT32
elif isinstance(object, uint64):
return TypeNumber.UINT64
elif isinstance(object, uint128):
return TypeNumber.UINT128
elif isinstance(object, uint256):
return TypeNumber.UINT256
elif isinstance(object, Container):
return TypeNumber.CONTAINER
elif isinstance(object, List):
return TypeNumber.LIST
elif isinstance(object, Vector):
return TypeNumber.VECTOR
elif isinstance(object, Bitvector):
return TypeNumber.BIT_VECTOR
elif isinstance(object, Bitlist):
return TypeNumber.BIT_LIST
elif isinstance(object, Union):
return TypeNumber.UNION
else:
raise ValueError(f"Unsupported type: {type(object)}")
def _serialize(object) -> (bool, bytes): # returns (is_variable, serialized_data)
# print("object: ", object)
basic_type = _basic_type(object)
if basic_type:
return (False, basic_type)
typ = _type_number(object)
# composite type
if typ == TypeNumber.BIT_VECTOR:
N = len(object)
array = [0] * ((N + 7) // 8)
for i, bit in enumerate(object):
array[i // 8] |= bit << (i % 8)
return (False, bytes(array))
elif typ == TypeNumber.BIT_LIST:
N = len(object)
array = [0] * ((N // 8) + 1)
for i, bit in enumerate(object):
array[i // 8] |= bit << (i % 8)
array[N // 8] |= 1 << (N % 8)
return (True, bytes(array))
flag_is_variable = False
if typ in [TypeNumber.CONTAINER, TypeNumber.LIST, TypeNumber.VECTOR]:
fixed_parts = []
variable_parts = []
if typ == TypeNumber.CONTAINER:
items = [getattr(object, field_name) for field_name in object.fields().keys()]
items_count = len(object.fields())
else:
items = list(object)
items_count = len(object)
for item in items:
is_variable, serialized = _serialize(item)
fixed_parts.append(None if is_variable else serialized)
variable_parts.append(serialized if is_variable else b"")
if is_variable:
flag_is_variable = True
fixed_lengths = [len(part) if part != None else BYTES_PER_LENGTH_OFFSET for part in fixed_parts]
variable_lengths = [len(part) for part in variable_parts]
# if typ == TypeNumber.CONTAINER:
# print(f"fixed_part =====> {fixed_parts}")
# print(f"variable_part =====> {variable_parts}")
# print(f"fixed_length =====> {fixed_lengths}")
# print(f"variable_length =====> {variable_lengths}")
variable_offsets = [_serialize(uint32(sum(fixed_lengths + variable_lengths[:i])))[1] for i in range(items_count)]
fixed_parts = [part if part != None else variable_offsets[i] for i, part in enumerate(fixed_parts)]
# print("fixed_parts: ", fixed_parts)
# print("variable_parts: ", variable_parts)
# print(variable_parts)
if typ == TypeNumber.LIST:
return (True, b"".join(fixed_parts) + b"".join(variable_parts))
return (flag_is_variable, b"".join(fixed_parts) + b"".join(variable_parts))
return None
def serialize(object):
return _serialize(object)[1]