-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbasic_datatypes.py
More file actions
193 lines (149 loc) · 4.13 KB
/
basic_datatypes.py
File metadata and controls
193 lines (149 loc) · 4.13 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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import struct
from collections import OrderedDict
class Data(object):
repeats = 1
_fields = {"value": None}
def __init__(this, value=None, **kwargs):
if value is not None:
setattr(this, 'value', value)
for k, v in kwargs.items():
setattr(this, k, v)
def parse(this, p):
tmp, r = this._parse(p)
assert r == b'', "Could not parse all data: %s" % repr((p, r))
return tmp
def _parse(this, data):
klist = []
vlist = []
if hasattr(this, '_identifier'):
identifier, data = Byte()._parse(data)
assert identifier == this._identifier
for name, datatype in this._fields.items():
val, data = datatype._parse(data)
klist.append(name)
vlist.append(val)
obj = this.__class__(**dict(zip(klist, vlist)))
return obj, data
def pack(this):
retval = b''
if hasattr(this, '_identifier'):
retval += Byte(this._identifier).pack()
for name in this._fields:
retval += getattr(this, name).pack()
return retval
def __repr__(this):
data = [n+'='+repr(getattr(this, n)) for n in this._fields if hasattr(this, n)]
return this.__class__.__name__ + '(' + ', '.join(data) + ')'
class IntData(Data):
def __eq__(this, other):
ot = other.__class__
if this.value is bytes:
if ot is bytes:
return this.value == other
elif len(this.value) == 1:
return this.value[0] == other
elif ot in [Data, Uint32, Uint64, Mpint]:
return this.value == other.value
elif ot is int:
return this.value == other
else:
raise TypeError(repr((this, other)))
class Byte(IntData):
_fields = OrderedDict(value=bytes)
size = 1
value = b''
def __init__(this, value=b''):
if value.__class__ is int:
value = bytes([value])
this.value = value
def _parse(this, p):
assert len(p) >= this.size, "Not enough data " + \
"to parse %u bytes: %s" % (this.size, len(p))
return Byte(p[:this.size]), p[this.size:]
def __mul__(this, val):
this.size *= val
return this
def pack(this):
return this.value
def __eq__(this, other):
ot = other.__class__
if ot is int and len(this.value) == 1:
return other == this.value[0]
elif ot is Byte:
return this.value == other.value
elif ot is bytes:
return this.value == other
else:
raise TypeError(repr((this, other)))
class Uint32(IntData):
_fields=OrderedDict(value=Data())
def _parse(this, data):
assert len(data) >= 4
i, = struct.unpack('>I', data[:4])
return Uint32(i), data[4:]
def __repr__(this):
return 'Uint32(%s)' % repr(this.value)
def pack(this):
return struct.pack('>I', this.value)
class Uint64(IntData):
def _parse(this, data):
assert len(data) >= 8
i, = struct.unpack('>L', data[:8])
return Uint32(i), data[8:]
def __repr__(this):
return 'Uint64(%s)' % repr(this.value)
def pack(this):
return struct.pack('>Q', this.value)
class Bool(Data):
def _parse(this, data):
tmp, data = Byte()._parse(data)
return (Bool(tmp.value != b'\x00'), data)
def pack(this):
if this.value:
return b'\x01'
else:
return b'\x00'
class String(Data):
def _parse(this, data):
length, data = Uint32()._parse(data)
tmp, data = (Byte()*(length.value))._parse(data)
return String(tmp.value), data
def pack(this):
return Uint32(len(this.value)).pack() + this.value
class Mpint(IntData):
def pack(this):
def bit_length(x):
x = abs(x)
i = 0
while (1 << i) <= x:
i += 1
return i
if this.value == 0:
return b'\x00'*4
elif this.value > 0:
bytelen = (bit_length(this.value) // 8) + 1
val = this.value
else:
pval = -this.value-1
bytelen = (bit_length(pval) // 8) + 1
val = (1<<(8*bytelen)) - (1+pval)
return String(val.to_bytes(bytelen, 'big')).pack()
def _parse(this, data):
content, data = String()._parse(data)
content = content.value
if len(content) == 0:
return Mpint(0), data
isPositive = (content[0] & 0x80) == 0
val = int.from_bytes(content, 'big')
if not isPositive:
val -= (1<<(8*len(content)))
return Mpint(val), data
class NameList(Data):
def _parse(this, data):
s, data = String()._parse(data)
if len(s.value) == 0:
return NameList([]), data
else:
return NameList(s.value.split(b',')), data
def pack(this):
return String(b','.join(this.value)).pack()