bitstruct 8.22.2__cp315-cp315-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bitstruct/__init__.py +700 -0
- bitstruct/bitstream.c +600 -0
- bitstruct/bitstream.h +170 -0
- bitstruct/c.c +2629 -0
- bitstruct/c.cp315-win32.pyd +0 -0
- bitstruct-8.22.2.dist-info/METADATA +252 -0
- bitstruct-8.22.2.dist-info/RECORD +10 -0
- bitstruct-8.22.2.dist-info/WHEEL +5 -0
- bitstruct-8.22.2.dist-info/licenses/LICENSE +22 -0
- bitstruct-8.22.2.dist-info/top_level.txt +1 -0
bitstruct/__init__.py
ADDED
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
__version__ = '8.22.2'
|
|
2
|
+
|
|
3
|
+
import binascii
|
|
4
|
+
import re
|
|
5
|
+
import struct
|
|
6
|
+
from io import BytesIO
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Error(Exception):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _Info:
|
|
14
|
+
|
|
15
|
+
def __init__(self, size, name=None):
|
|
16
|
+
self.size = size
|
|
17
|
+
self.name = name
|
|
18
|
+
self.endianness = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _SignedInteger(_Info):
|
|
22
|
+
|
|
23
|
+
def __init__(self, size, name):
|
|
24
|
+
super().__init__(size, name)
|
|
25
|
+
self.minimum = -2 ** (size - 1)
|
|
26
|
+
self.maximum = -self.minimum - 1
|
|
27
|
+
|
|
28
|
+
def pack(self, arg):
|
|
29
|
+
value = int(arg)
|
|
30
|
+
|
|
31
|
+
if value < self.minimum or value > self.maximum:
|
|
32
|
+
raise Error(
|
|
33
|
+
'"s{}" requires {} <= integer <= {} (got {})'.format(
|
|
34
|
+
self.size,
|
|
35
|
+
self.minimum,
|
|
36
|
+
self.maximum,
|
|
37
|
+
arg))
|
|
38
|
+
|
|
39
|
+
if value < 0:
|
|
40
|
+
value += (1 << self.size)
|
|
41
|
+
|
|
42
|
+
value += (1 << self.size)
|
|
43
|
+
|
|
44
|
+
return bin(value)[3:]
|
|
45
|
+
|
|
46
|
+
def unpack(self, bits):
|
|
47
|
+
value = int(bits, 2)
|
|
48
|
+
|
|
49
|
+
if bits[0] == '1':
|
|
50
|
+
value -= (1 << len(bits))
|
|
51
|
+
|
|
52
|
+
return value
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _UnsignedInteger(_Info):
|
|
56
|
+
|
|
57
|
+
def __init__(self, size, name):
|
|
58
|
+
super().__init__(size, name)
|
|
59
|
+
self.maximum = 2 ** size - 1
|
|
60
|
+
|
|
61
|
+
def pack(self, arg):
|
|
62
|
+
value = int(arg)
|
|
63
|
+
|
|
64
|
+
if value < 0 or value > self.maximum:
|
|
65
|
+
raise Error(
|
|
66
|
+
f'"u{self.size}" requires 0 <= integer <= {self.maximum} (got {arg})')
|
|
67
|
+
|
|
68
|
+
return bin(value + (1 << self.size))[3:]
|
|
69
|
+
|
|
70
|
+
def unpack(self, bits):
|
|
71
|
+
return int(bits, 2)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class _Boolean(_UnsignedInteger):
|
|
75
|
+
|
|
76
|
+
def pack(self, arg):
|
|
77
|
+
return super().pack(int(bool(arg)))
|
|
78
|
+
|
|
79
|
+
def unpack(self, bits):
|
|
80
|
+
return bool(super().unpack(bits))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class _Float(_Info):
|
|
84
|
+
|
|
85
|
+
def pack(self, arg):
|
|
86
|
+
value = float(arg)
|
|
87
|
+
|
|
88
|
+
if self.size == 16:
|
|
89
|
+
value = struct.pack('>e', value)
|
|
90
|
+
elif self.size == 32:
|
|
91
|
+
value = struct.pack('>f', value)
|
|
92
|
+
elif self.size == 64:
|
|
93
|
+
value = struct.pack('>d', value)
|
|
94
|
+
else:
|
|
95
|
+
raise Error(f'expected float size of 16, 32, or 64 bits (got {self.size})')
|
|
96
|
+
|
|
97
|
+
return bin(int(b'01' + binascii.hexlify(value), 16))[3:]
|
|
98
|
+
|
|
99
|
+
def unpack(self, bits):
|
|
100
|
+
packed = _unpack_bytearray(self.size, bits)
|
|
101
|
+
|
|
102
|
+
if self.size == 16:
|
|
103
|
+
value = struct.unpack('>e', packed)[0]
|
|
104
|
+
elif self.size == 32:
|
|
105
|
+
value = struct.unpack('>f', packed)[0]
|
|
106
|
+
elif self.size == 64:
|
|
107
|
+
value = struct.unpack('>d', packed)[0]
|
|
108
|
+
else:
|
|
109
|
+
raise Error(f'expected float size of 16, 32, or 64 bits (got {self.size})')
|
|
110
|
+
|
|
111
|
+
return value
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class _Raw(_Info):
|
|
115
|
+
|
|
116
|
+
def pack(self, arg):
|
|
117
|
+
number_of_padding_bytes = ((self.size - 8 * len(arg)) // 8)
|
|
118
|
+
arg += (number_of_padding_bytes * b'\x00')
|
|
119
|
+
|
|
120
|
+
return bin(int(b'01' + binascii.hexlify(arg), 16))[3:self.size + 3]
|
|
121
|
+
|
|
122
|
+
def unpack(self, bits):
|
|
123
|
+
rest = self.size % 8
|
|
124
|
+
|
|
125
|
+
if rest > 0:
|
|
126
|
+
bits += (8 - rest) * '0'
|
|
127
|
+
|
|
128
|
+
return binascii.unhexlify(hex(int('10000000' + bits, 2))[4:].rstrip('L'))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class _Padding(_Info):
|
|
132
|
+
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class _ZeroPadding(_Padding):
|
|
137
|
+
|
|
138
|
+
def pack(self):
|
|
139
|
+
return self.size * '0'
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class _OnePadding(_Padding):
|
|
143
|
+
|
|
144
|
+
def pack(self):
|
|
145
|
+
return self.size * '1'
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class _Text(_Info):
|
|
149
|
+
|
|
150
|
+
def __init__(self, size, name, encoding, errors):
|
|
151
|
+
super().__init__(size, name)
|
|
152
|
+
self.encoding = encoding
|
|
153
|
+
self.errors = errors
|
|
154
|
+
|
|
155
|
+
def pack(self, arg):
|
|
156
|
+
encoded = arg.encode('utf-8')
|
|
157
|
+
number_of_padding_bytes = ((self.size - 8 * len(encoded)) // 8)
|
|
158
|
+
encoded += (number_of_padding_bytes * b'\x00')
|
|
159
|
+
|
|
160
|
+
return _pack_bytearray(self.size, encoded)
|
|
161
|
+
|
|
162
|
+
def unpack(self, bits):
|
|
163
|
+
return _unpack_bytearray(self.size, bits).decode(self.encoding, self.errors)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _parse_format(fmt, names, text_encoding, text_errors):
|
|
167
|
+
if fmt and fmt[-1] in '><':
|
|
168
|
+
byte_order = fmt[-1]
|
|
169
|
+
fmt = fmt[:-1]
|
|
170
|
+
else:
|
|
171
|
+
byte_order = ''
|
|
172
|
+
|
|
173
|
+
parsed_infos = re.findall(r'([<>]?)([a-zA-Z])(\d+)(\s*)', fmt)
|
|
174
|
+
|
|
175
|
+
if ''.join([''.join(info) for info in parsed_infos]) != fmt:
|
|
176
|
+
raise Error(f"bad format '{fmt + byte_order}'")
|
|
177
|
+
|
|
178
|
+
# Use big endian as default and use the endianness of the previous
|
|
179
|
+
# value if none is given for the current value.
|
|
180
|
+
infos = []
|
|
181
|
+
endianness = ">"
|
|
182
|
+
i = 0
|
|
183
|
+
|
|
184
|
+
for parsed_info in parsed_infos:
|
|
185
|
+
if parsed_info[0] != "":
|
|
186
|
+
endianness = parsed_info[0]
|
|
187
|
+
|
|
188
|
+
type_ = parsed_info[1]
|
|
189
|
+
size = int(parsed_info[2])
|
|
190
|
+
|
|
191
|
+
if size == 0:
|
|
192
|
+
raise Error(f"bad format '{fmt + byte_order}'")
|
|
193
|
+
|
|
194
|
+
if names is None:
|
|
195
|
+
name = i
|
|
196
|
+
elif type_ not in 'pP':
|
|
197
|
+
name = names[i]
|
|
198
|
+
|
|
199
|
+
if type_ == 's':
|
|
200
|
+
info = _SignedInteger(size, name)
|
|
201
|
+
i += 1
|
|
202
|
+
elif type_ == 'u':
|
|
203
|
+
info = _UnsignedInteger(size, name)
|
|
204
|
+
i += 1
|
|
205
|
+
elif type_ == 'f':
|
|
206
|
+
info = _Float(size, name)
|
|
207
|
+
i += 1
|
|
208
|
+
elif type_ == 'b':
|
|
209
|
+
info = _Boolean(size, name)
|
|
210
|
+
i += 1
|
|
211
|
+
elif type_ == 't':
|
|
212
|
+
info = _Text(size, name, text_encoding, text_errors)
|
|
213
|
+
i += 1
|
|
214
|
+
elif type_ == 'r':
|
|
215
|
+
info = _Raw(size, name)
|
|
216
|
+
i += 1
|
|
217
|
+
elif type_ == 'p':
|
|
218
|
+
info = _ZeroPadding(size)
|
|
219
|
+
elif type_ == 'P':
|
|
220
|
+
info = _OnePadding(size)
|
|
221
|
+
else:
|
|
222
|
+
raise Error(f"bad char '{type_}' in format")
|
|
223
|
+
|
|
224
|
+
info.endianness = endianness
|
|
225
|
+
|
|
226
|
+
infos.append(info)
|
|
227
|
+
|
|
228
|
+
return infos, byte_order or '>'
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _pack_bytearray(size, arg):
|
|
232
|
+
return bin(int(b'01' + binascii.hexlify(arg), 16))[3:size + 3]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _unpack_bytearray(size, bits):
|
|
236
|
+
rest = size % 8
|
|
237
|
+
|
|
238
|
+
if rest > 0:
|
|
239
|
+
bits += (8 - rest) * '0'
|
|
240
|
+
|
|
241
|
+
return binascii.unhexlify(hex(int('10000000' + bits, 2))[4:].rstrip('L'))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class _CompiledFormat:
|
|
245
|
+
|
|
246
|
+
def __init__(self,
|
|
247
|
+
fmt,
|
|
248
|
+
names=None,
|
|
249
|
+
text_encoding='utf-8',
|
|
250
|
+
text_errors='strict'):
|
|
251
|
+
infos, byte_order = _parse_format(fmt, names, text_encoding, text_errors)
|
|
252
|
+
self._infos = infos
|
|
253
|
+
self._byte_order = byte_order
|
|
254
|
+
self._number_of_bits_to_unpack = sum([info.size for info in infos])
|
|
255
|
+
|
|
256
|
+
def pack_value(self, info, value, bits):
|
|
257
|
+
value_bits = info.pack(value)
|
|
258
|
+
|
|
259
|
+
# Reverse the bit order in little endian values.
|
|
260
|
+
if info.endianness == "<":
|
|
261
|
+
value_bits = value_bits[::-1]
|
|
262
|
+
|
|
263
|
+
# Reverse bytes order for least significant byte first.
|
|
264
|
+
if self._byte_order == ">" or isinstance(info, (_Raw, _Text)):
|
|
265
|
+
bits += value_bits
|
|
266
|
+
else:
|
|
267
|
+
aligned_offset = len(value_bits) - (8 - (len(bits) % 8))
|
|
268
|
+
|
|
269
|
+
while aligned_offset > 0:
|
|
270
|
+
bits += value_bits[aligned_offset:]
|
|
271
|
+
value_bits = value_bits[:aligned_offset]
|
|
272
|
+
aligned_offset -= 8
|
|
273
|
+
|
|
274
|
+
bits += value_bits
|
|
275
|
+
|
|
276
|
+
return bits
|
|
277
|
+
|
|
278
|
+
def pack_any(self, values):
|
|
279
|
+
bits = ''
|
|
280
|
+
|
|
281
|
+
for info in self._infos:
|
|
282
|
+
if isinstance(info, _Padding):
|
|
283
|
+
bits += info.pack()
|
|
284
|
+
else:
|
|
285
|
+
bits = self.pack_value(info, values[info.name], bits)
|
|
286
|
+
|
|
287
|
+
# Padding of last byte.
|
|
288
|
+
tail = len(bits) % 8
|
|
289
|
+
|
|
290
|
+
if tail != 0:
|
|
291
|
+
bits += (8 - tail) * '0'
|
|
292
|
+
|
|
293
|
+
return bytes(_unpack_bytearray(len(bits), bits))
|
|
294
|
+
|
|
295
|
+
def unpack_from_any(self, data, offset, allow_truncated):
|
|
296
|
+
bits = bin(int(b'01' + binascii.hexlify(data), 16))[3 + offset:]
|
|
297
|
+
|
|
298
|
+
# Sanity check.
|
|
299
|
+
if not allow_truncated and self._number_of_bits_to_unpack > len(bits):
|
|
300
|
+
raise Error(
|
|
301
|
+
"unpack requires at least {} bits to unpack (got {})".format(
|
|
302
|
+
self._number_of_bits_to_unpack,
|
|
303
|
+
len(bits)))
|
|
304
|
+
|
|
305
|
+
offset = 0
|
|
306
|
+
|
|
307
|
+
for info in self._infos:
|
|
308
|
+
if offset + info.size > len(bits):
|
|
309
|
+
# Stop unpacking if we ran out of bytes to
|
|
310
|
+
# unpack. Note that this condition will never trigger
|
|
311
|
+
# if `allow_truncated` is not `True` because of the
|
|
312
|
+
# sanity check above.
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
if isinstance(info, _Padding):
|
|
316
|
+
pass
|
|
317
|
+
else:
|
|
318
|
+
# Reverse bytes order for least significant byte
|
|
319
|
+
# first.
|
|
320
|
+
if self._byte_order == ">" or isinstance(info, (_Raw, _Text)):
|
|
321
|
+
value_bits = bits[offset:offset + info.size]
|
|
322
|
+
else:
|
|
323
|
+
value_bits_tmp = bits[offset:offset + info.size]
|
|
324
|
+
aligned_offset = (info.size - ((offset + info.size) % 8))
|
|
325
|
+
value_bits = ''
|
|
326
|
+
|
|
327
|
+
while aligned_offset > 0:
|
|
328
|
+
value_bits += value_bits_tmp[aligned_offset:aligned_offset + 8]
|
|
329
|
+
value_bits_tmp = value_bits_tmp[:aligned_offset]
|
|
330
|
+
aligned_offset -= 8
|
|
331
|
+
|
|
332
|
+
value_bits += value_bits_tmp
|
|
333
|
+
|
|
334
|
+
# Reverse the bit order in little endian values.
|
|
335
|
+
if info.endianness == "<":
|
|
336
|
+
value_bits = value_bits[::-1]
|
|
337
|
+
|
|
338
|
+
yield info, info.unpack(value_bits)
|
|
339
|
+
|
|
340
|
+
offset += info.size
|
|
341
|
+
|
|
342
|
+
def pack_into_any(self, buf, offset, data, **kwargs):
|
|
343
|
+
fill_padding = kwargs.get('fill_padding', True)
|
|
344
|
+
buf_bits = _pack_bytearray(8 * len(buf), buf)
|
|
345
|
+
bits = buf_bits[0:offset]
|
|
346
|
+
|
|
347
|
+
for info in self._infos:
|
|
348
|
+
if isinstance(info, _Padding):
|
|
349
|
+
if fill_padding:
|
|
350
|
+
bits += info.pack()
|
|
351
|
+
else:
|
|
352
|
+
bits += buf_bits[len(bits):len(bits) + info.size]
|
|
353
|
+
else:
|
|
354
|
+
bits = self.pack_value(info, data[info.name], bits)
|
|
355
|
+
|
|
356
|
+
bits += buf_bits[len(bits):]
|
|
357
|
+
|
|
358
|
+
if len(bits) > len(buf_bits):
|
|
359
|
+
raise Error(
|
|
360
|
+
f'pack_into requires a buffer of at least {len(bits)} bits')
|
|
361
|
+
|
|
362
|
+
buf[:] = _unpack_bytearray(len(bits), bits)
|
|
363
|
+
|
|
364
|
+
def calcsize(self):
|
|
365
|
+
"""Return the number of bits in the compiled format string.
|
|
366
|
+
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
return self._number_of_bits_to_unpack
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class CompiledFormat(_CompiledFormat):
|
|
373
|
+
"""A compiled format string that can be used to pack and/or unpack
|
|
374
|
+
data multiple times.
|
|
375
|
+
|
|
376
|
+
Instances of this class are created by the factory function
|
|
377
|
+
:func:`~bitstruct.compile()`.
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
def __init__(self, fmt, text_encoding='utf-8', text_errors='strict'):
|
|
382
|
+
super().__init__(fmt, None, text_encoding, text_errors)
|
|
383
|
+
self._number_of_arguments = 0
|
|
384
|
+
|
|
385
|
+
for info in self._infos:
|
|
386
|
+
if not isinstance(info, _Padding):
|
|
387
|
+
self._number_of_arguments += 1
|
|
388
|
+
|
|
389
|
+
def pack(self, *args):
|
|
390
|
+
"""See :func:`~bitstruct.pack()`.
|
|
391
|
+
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
# Sanity check of the number of arguments.
|
|
395
|
+
if len(args) < self._number_of_arguments:
|
|
396
|
+
raise Error(
|
|
397
|
+
"pack expected {} item(s) for packing (got {})".format(
|
|
398
|
+
self._number_of_arguments,
|
|
399
|
+
len(args)))
|
|
400
|
+
|
|
401
|
+
return self.pack_any(args)
|
|
402
|
+
|
|
403
|
+
def unpack(self, data, allow_truncated=False):
|
|
404
|
+
"""See :func:`~bitstruct.unpack()`.
|
|
405
|
+
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
return self.unpack_from(data, allow_truncated=allow_truncated)
|
|
409
|
+
|
|
410
|
+
def pack_into(self, buf, offset, *args, **kwargs):
|
|
411
|
+
"""See :func:`~bitstruct.pack_into()`.
|
|
412
|
+
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
# Sanity check of the number of arguments.
|
|
416
|
+
if len(args) < self._number_of_arguments:
|
|
417
|
+
raise Error(
|
|
418
|
+
"pack expected {} item(s) for packing (got {})".format(
|
|
419
|
+
self._number_of_arguments,
|
|
420
|
+
len(args)))
|
|
421
|
+
|
|
422
|
+
self.pack_into_any(buf, offset, args, **kwargs)
|
|
423
|
+
|
|
424
|
+
def unpack_from(self, data, offset=0, allow_truncated=False):
|
|
425
|
+
"""See :func:`~bitstruct.unpack_from()`.
|
|
426
|
+
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
return tuple([v[1] for v in self.unpack_from_any(
|
|
430
|
+
data, offset, allow_truncated=allow_truncated)])
|
|
431
|
+
|
|
432
|
+
class CompiledFormatDict(_CompiledFormat):
|
|
433
|
+
"""See :class:`~bitstruct.CompiledFormat`.
|
|
434
|
+
|
|
435
|
+
"""
|
|
436
|
+
|
|
437
|
+
def pack(self, data):
|
|
438
|
+
"""See :func:`~bitstruct.pack_dict()`.
|
|
439
|
+
|
|
440
|
+
"""
|
|
441
|
+
|
|
442
|
+
try:
|
|
443
|
+
return self.pack_any(data)
|
|
444
|
+
except KeyError as e:
|
|
445
|
+
raise Error(f'{str(e)} not found in data dictionary')
|
|
446
|
+
|
|
447
|
+
def unpack(self, data, allow_truncated=False):
|
|
448
|
+
"""See :func:`~bitstruct.unpack_dict()`.
|
|
449
|
+
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
return self.unpack_from(data, allow_truncated=allow_truncated)
|
|
453
|
+
|
|
454
|
+
def pack_into(self, buf, offset, data, **kwargs):
|
|
455
|
+
"""See :func:`~bitstruct.pack_into_dict()`.
|
|
456
|
+
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
try:
|
|
460
|
+
self.pack_into_any(buf, offset, data, **kwargs)
|
|
461
|
+
except KeyError as e:
|
|
462
|
+
raise Error(f'{str(e)} not found in data dictionary')
|
|
463
|
+
|
|
464
|
+
def unpack_from(self, data, offset=0, allow_truncated=False):
|
|
465
|
+
"""See :func:`~bitstruct.unpack_from_dict()`.
|
|
466
|
+
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
info.name: v for info, v in self.unpack_from_any(
|
|
471
|
+
data, offset, allow_truncated=allow_truncated)}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def pack(fmt, *args):
|
|
475
|
+
"""Return a bytes object containing the values v1, v2, ... packed
|
|
476
|
+
according to given format string `fmt`. If the total number of
|
|
477
|
+
bits are not a multiple of 8, padding will be added at the end of
|
|
478
|
+
the last byte.
|
|
479
|
+
|
|
480
|
+
`fmt` is a string of bit order-type-length groups, and optionally
|
|
481
|
+
a byte order identifier after the groups. Bit Order and byte order
|
|
482
|
+
may be omitted.
|
|
483
|
+
|
|
484
|
+
Bit Order is either ``>`` or ``<``, where ``>`` means MSB first
|
|
485
|
+
and ``<`` means LSB first. If bit order is omitted, the previous
|
|
486
|
+
values' bit order is used for the current value. For example, in
|
|
487
|
+
the format string ``'u1<u2u3'``, ``u1`` is MSB first and both
|
|
488
|
+
``u2`` and ``u3`` are LSB first.
|
|
489
|
+
|
|
490
|
+
Byte Order is either ``>`` or ``<``, where ``>`` means most
|
|
491
|
+
significant byte first and ``<`` means least significant byte
|
|
492
|
+
first. If byte order is omitted, most significant byte first is
|
|
493
|
+
used.
|
|
494
|
+
|
|
495
|
+
There are eight types; ``u``, ``s``, ``f``, ``b``, ``t``, ``r``,
|
|
496
|
+
``p`` and ``P``.
|
|
497
|
+
|
|
498
|
+
- ``u`` -- unsigned integer
|
|
499
|
+
- ``s`` -- signed integer
|
|
500
|
+
- ``f`` -- floating point number of 16, 32, or 64 bits
|
|
501
|
+
- ``b`` -- boolean
|
|
502
|
+
- ``t`` -- text (ascii or utf-8)
|
|
503
|
+
- ``r`` -- raw, bytes
|
|
504
|
+
- ``p`` -- padding with zeros, ignore
|
|
505
|
+
- ``P`` -- padding with ones, ignore
|
|
506
|
+
|
|
507
|
+
Length is the number of bits to pack the value into.
|
|
508
|
+
|
|
509
|
+
Example format string with default bit and byte ordering:
|
|
510
|
+
``'u1u3p7s16'``
|
|
511
|
+
|
|
512
|
+
Same format string, but with least significant byte first:
|
|
513
|
+
``'u1u3p7s16<'``
|
|
514
|
+
|
|
515
|
+
Same format string, but with LSB first (``<`` prefix) and least
|
|
516
|
+
significant byte first (``<`` suffix): ``'<u1u3p7s16<'``
|
|
517
|
+
|
|
518
|
+
It is allowed to separate groups with a single space for better
|
|
519
|
+
readability.
|
|
520
|
+
|
|
521
|
+
"""
|
|
522
|
+
|
|
523
|
+
return CompiledFormat(fmt).pack(*args)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def unpack(fmt,
|
|
527
|
+
data,
|
|
528
|
+
allow_truncated=False,
|
|
529
|
+
text_encoding='utf-8',
|
|
530
|
+
text_errors='strict'):
|
|
531
|
+
"""Unpack `data` (bytes or bytearray) according to given format string
|
|
532
|
+
`fmt`.
|
|
533
|
+
|
|
534
|
+
If `allow_truncated` is `True`, `data` may be shorter than the
|
|
535
|
+
number of items specified by `fmt`; in this case, only the
|
|
536
|
+
complete items will be unpacked. The result is a tuple even if it
|
|
537
|
+
contains exactly one item.
|
|
538
|
+
|
|
539
|
+
Text fields are decoded with given encoding `text_encoding` and
|
|
540
|
+
error handling as given by `text_errors` (both passed to
|
|
541
|
+
`bytes.decode()`).
|
|
542
|
+
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
return CompiledFormat(fmt, text_encoding, text_errors).unpack(
|
|
546
|
+
data, allow_truncated=allow_truncated)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def pack_into(fmt, buf, offset, *args, **kwargs):
|
|
550
|
+
"""Pack given values v1, v2, ... into given bytearray `buf`, starting
|
|
551
|
+
at given bit offset `offset`. Pack according to given format
|
|
552
|
+
string `fmt`. Give `fill_padding` as ``False`` to leave padding
|
|
553
|
+
bits in `buf` unmodified.
|
|
554
|
+
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
return CompiledFormat(fmt).pack_into(buf,
|
|
558
|
+
offset,
|
|
559
|
+
*args,
|
|
560
|
+
**kwargs)
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def unpack_from(fmt,
|
|
564
|
+
data,
|
|
565
|
+
offset=0,
|
|
566
|
+
allow_truncated=False,
|
|
567
|
+
text_encoding='utf-8',
|
|
568
|
+
text_errors='strict'):
|
|
569
|
+
"""Unpack `data` (bytes or bytearray) according to given format string
|
|
570
|
+
`fmt`, starting at given bit offset `offset`. If `allow_truncated`
|
|
571
|
+
is `True`, `data` may be shorter than the number of items
|
|
572
|
+
specified by `fmt`; in this case, only the complete items will be
|
|
573
|
+
unpacked. The result is a tuple even if it contains exactly one
|
|
574
|
+
item.
|
|
575
|
+
|
|
576
|
+
"""
|
|
577
|
+
|
|
578
|
+
return CompiledFormat(fmt, text_encoding, text_errors).unpack_from(
|
|
579
|
+
data, offset, allow_truncated=allow_truncated)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def pack_dict(fmt, names, data):
|
|
583
|
+
"""Same as :func:`~bitstruct.pack()`, but data is read from a
|
|
584
|
+
dictionary.
|
|
585
|
+
|
|
586
|
+
The names list `names` contains the format group names, used as
|
|
587
|
+
keys in the dictionary.
|
|
588
|
+
|
|
589
|
+
>>> pack_dict('u4u4', ['foo', 'bar'], {'foo': 1, 'bar': 2})
|
|
590
|
+
b'\\x12'
|
|
591
|
+
|
|
592
|
+
"""
|
|
593
|
+
|
|
594
|
+
return CompiledFormatDict(fmt, names).pack(data)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def unpack_dict(fmt,
|
|
598
|
+
names,
|
|
599
|
+
data,
|
|
600
|
+
allow_truncated=False,
|
|
601
|
+
text_encoding='utf-8',
|
|
602
|
+
text_errors='strict'):
|
|
603
|
+
"""Same as :func:`~bitstruct.unpack()`, but returns a dictionary.
|
|
604
|
+
|
|
605
|
+
See :func:`~bitstruct.pack_dict()` for details on `names`.
|
|
606
|
+
|
|
607
|
+
>>> unpack_dict('u4u4', ['foo', 'bar'], b'\\x12')
|
|
608
|
+
{'foo': 1, 'bar': 2}
|
|
609
|
+
|
|
610
|
+
"""
|
|
611
|
+
|
|
612
|
+
return CompiledFormatDict(fmt, names, text_encoding, text_errors).unpack(
|
|
613
|
+
data, allow_truncated=allow_truncated)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def pack_into_dict(fmt, names, buf, offset, data, **kwargs):
|
|
617
|
+
"""Same as :func:`~bitstruct.pack_into()`, but data is read from a
|
|
618
|
+
dictionary.
|
|
619
|
+
|
|
620
|
+
See :func:`~bitstruct.pack_dict()` for details on `names`.
|
|
621
|
+
|
|
622
|
+
"""
|
|
623
|
+
|
|
624
|
+
return CompiledFormatDict(fmt, names).pack_into(buf,
|
|
625
|
+
offset,
|
|
626
|
+
data,
|
|
627
|
+
**kwargs)
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def unpack_from_dict(fmt,
|
|
631
|
+
names,
|
|
632
|
+
data,
|
|
633
|
+
offset=0,
|
|
634
|
+
allow_truncated=False,
|
|
635
|
+
text_encoding='utf-8',
|
|
636
|
+
text_errors='strict'):
|
|
637
|
+
"""Same as :func:`~bitstruct.unpack_from()`, but returns a
|
|
638
|
+
dictionary.
|
|
639
|
+
|
|
640
|
+
See :func:`~bitstruct.pack_dict()` for details on `names`.
|
|
641
|
+
|
|
642
|
+
"""
|
|
643
|
+
|
|
644
|
+
return CompiledFormatDict(fmt, names, text_encoding, text_errors).unpack_from(
|
|
645
|
+
data, offset, allow_truncated=allow_truncated)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def calcsize(fmt):
|
|
649
|
+
"""Return the number of bits in given format string `fmt`.
|
|
650
|
+
|
|
651
|
+
>>> calcsize('u1s3p4')
|
|
652
|
+
8
|
|
653
|
+
|
|
654
|
+
"""
|
|
655
|
+
|
|
656
|
+
return CompiledFormat(fmt).calcsize()
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def byteswap(fmt, data, offset=0):
|
|
660
|
+
"""Swap bytes in `data` according to `fmt`, starting at byte `offset`
|
|
661
|
+
and return the result. `fmt` must be an iterable, iterating over
|
|
662
|
+
number of bytes to swap. For example, the format string ``'24'``
|
|
663
|
+
applied to the bytes ``b'\\x00\\x11\\x22\\x33\\x44\\x55'`` will
|
|
664
|
+
produce the result ``b'\\x11\\x00\\x55\\x44\\x33\\x22'``.
|
|
665
|
+
|
|
666
|
+
"""
|
|
667
|
+
|
|
668
|
+
data = BytesIO(data)
|
|
669
|
+
data.seek(offset)
|
|
670
|
+
data_swapped = BytesIO()
|
|
671
|
+
|
|
672
|
+
for f in fmt:
|
|
673
|
+
swapped = data.read(int(f))[::-1]
|
|
674
|
+
data_swapped.write(swapped)
|
|
675
|
+
|
|
676
|
+
return data_swapped.getvalue()
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def compile(fmt,
|
|
680
|
+
names=None,
|
|
681
|
+
text_encoding='utf-8',
|
|
682
|
+
text_errors='strict'):
|
|
683
|
+
"""Compile given format string `fmt` and return a compiled format
|
|
684
|
+
object that can be used to pack and/or unpack data multiple times.
|
|
685
|
+
|
|
686
|
+
Returns a :class:`~bitstruct.CompiledFormat` object if `names` is
|
|
687
|
+
``None``, and otherwise a :class:`~bitstruct.CompiledFormatDict`
|
|
688
|
+
object.
|
|
689
|
+
|
|
690
|
+
See :func:`~bitstruct.pack_dict()` for details on `names`.
|
|
691
|
+
|
|
692
|
+
See :func:`~bitstruct.unpack()` for details on `text_encoding` and
|
|
693
|
+
`text_errors`.
|
|
694
|
+
|
|
695
|
+
"""
|
|
696
|
+
|
|
697
|
+
if names is None:
|
|
698
|
+
return CompiledFormat(fmt, text_encoding, text_errors)
|
|
699
|
+
else:
|
|
700
|
+
return CompiledFormatDict(fmt, names, text_encoding, text_errors)
|