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 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)