bitarray 3.12.2__cp313-cp313-pyemscripten_2025_0_wasm32.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.
@@ -0,0 +1,721 @@
1
+ # Copyright (c) 2026, Ilan Schnell; All Rights Reserved
2
+ # bitarray is published under the PSF license.
3
+ #
4
+ # Author: Ilan Schnell
5
+ """
6
+ Tests for bitarray.bitfields module
7
+ """
8
+ import os
9
+ import math
10
+ import pickle
11
+ import struct
12
+ import unittest
13
+ import dataclasses
14
+
15
+ from bitarray import bitarray, frozenbitarray
16
+ from bitarray.util import urandom
17
+ from bitarray.bitfields import (Struct, compile, pack, pack_into,
18
+ unpack, unpack_from, calcsize, PaddingError,
19
+ DEFAULT_ENDIAN, _ENDIAN_FROM_PREFIX)
20
+ from bitarray.test_bitarray import Util, ENDIANS
21
+
22
+
23
+ class Example:
24
+ format = ">u2 s7 x{000111} <u h b5 p{11111} ? B16 f16"
25
+ canonical_format = ">u2 >s7 >x6{000111} <u1 <h4 <b5 <P5 <? <B16 <f16"
26
+ width = 63
27
+ values = 2, -8, 1, "e", bitarray("01110"), False, b"A\xff", -29.0
28
+ bits = bitarray("10 1111000 000111 1 0111 01110 11111 0 "
29
+ "10000010_11111111 0000001011110011")
30
+
31
+
32
+ class PackUnpackTests(unittest.TestCase, Util):
33
+
34
+ def test_example(self):
35
+ cf = compile(Example.format)
36
+ self.assertIsInstance(cf, Struct)
37
+ self.assertEqual(cf.width, Example.width)
38
+ self.assertEqual(cf.values, len(Example.values))
39
+ self.assertEqual(cf.format(), Example.canonical_format)
40
+ a = cf.pack(*Example.values)
41
+ self.assertEqual(len(a), Example.width)
42
+ self.assertEqual(a.endian, "big")
43
+ self.assertEqual(a, Example.bits)
44
+ a[9:15] = 1 # the 'x' pad-bits are not validated
45
+ for b in a, frozenbitarray(a):
46
+ self.assertEqual(cf.unpack(b), Example.values)
47
+
48
+ def test_offset(self):
49
+ cf = compile(Example.format)
50
+ w = cf.width
51
+ n = 1000
52
+ for offset in 0, 357, n - w, -n, -611, -w:
53
+ a = urandom(n)
54
+ b = a.copy()
55
+ cf.pack_into(a, offset, *Example.values)
56
+ self.assertEqual(cf.unpack_from(a, offset), Example.values)
57
+ start = offset if offset >= 0 else n + offset
58
+ self.assertEqual(a[:start], b[:start])
59
+ self.assertEqual(a[start:start + w], Example.bits)
60
+ self.assertEqual(a[start + w:], b[start + w:])
61
+
62
+ def test_pack_value_count(self):
63
+ cf = compile("u8 s8")
64
+ self.assertEqual(cf.values, 2)
65
+ self.assertRaisesMessage(ValueError,
66
+ "expected 2 values to pack, got 1",
67
+ cf.pack, 1)
68
+ self.assertRaises(ValueError, cf.pack, 1, 2, 3)
69
+ self.assertRaises(ValueError, compile("x").pack, 1)
70
+
71
+ def test_pack_value_size_mismatch(self):
72
+ for fmt, value, msg in [
73
+ ("h12", "ff", "expected hex string of length 3, got 2"),
74
+ ("b3", bitarray(2), "expected bitarray of length 3, got 2"),
75
+ ("B16", b"\xa5", "expected bytes of length 2, got 1"),
76
+ ]:
77
+ cf = compile(fmt)
78
+ self.assertRaisesMessage(ValueError, msg, cf.pack, value)
79
+
80
+ def test_pack_into(self):
81
+ cf = compile(Example.format)
82
+ w = cf.width
83
+ for endian in ENDIANS:
84
+ offset = 357
85
+ a = urandom(512, endian)
86
+ b = a.copy()
87
+ self.assertIsNone(cf.pack_into(a, offset, *Example.values))
88
+ self.assertEqual(len(a), 512) # pack_into does not resize
89
+ self.assertEqual(a.endian, endian)
90
+ self.assertEqual(a[:offset], b[:offset])
91
+ self.assertEqual(a[offset:offset + w], Example.bits)
92
+ self.assertEqual(a[offset + w:], b[offset + w:])
93
+
94
+ def test_pack_into_errors(self):
95
+ cf = compile("u8")
96
+ lst = 8 * [0]
97
+ self.assertRaisesMessage(TypeError, "bitarray expected, got 'list'",
98
+ cf.pack_into, lst, 0, 123)
99
+ for a in frozenbitarray(12), bitarray(buffer=b"AA"):
100
+ self.assertRaisesMessage(TypeError,
101
+ "cannot pack into read-only bitarray",
102
+ cf.pack_into, a, 0, 123)
103
+ a = bitarray(12)
104
+ self.assertRaises(TypeError, cf.pack_into, a, 0.0, 123)
105
+ for offset in 5, -7, -13, 13:
106
+ msg = ("offset %d out of range for bitarray size 12" % offset
107
+ if offset in (-13, 13) else
108
+ "bitarray of at least 13 bits required for packing 8 bits "
109
+ "at offset 5 (actual bitarray size is 12)")
110
+ self.assertRaisesMessage(ValueError, msg,
111
+ cf.pack_into, a, offset, 123)
112
+
113
+ def test_unpack_names(self):
114
+ cf = compile("x5 u6{red} u6{green} u6{blue} x4")
115
+ a = cf.pack(1, 2, 3)
116
+ cf2 = compile("u6{red} u6{green} u6{blue}")
117
+ for values in cf.unpack(a), cf2.unpack_from(a, 5):
118
+ self.assertIsInstance(values, tuple)
119
+ self.assertEqual(type(values).__name__, "Unpacked")
120
+ self.assertEqual(values.red, 1)
121
+ self.assertEqual(values.green, 2)
122
+ self.assertEqual(values.blue, 3)
123
+ self.assertEqual(values._fields, ("red", "green", "blue"))
124
+
125
+ def test_unpack_errors(self):
126
+ cf = compile(">u8")
127
+ self.assertRaisesMessage(ValueError, "expected bitarray of length 8 "
128
+ "to unpack, got 7", cf.unpack, bitarray(7))
129
+ self.assertRaises(ValueError, cf.unpack, bitarray(9))
130
+ lst = 8 * [0]
131
+ self.assertRaises(TypeError, cf.unpack, lst)
132
+ cf = compile("p{0010}")
133
+ self.assertRaisesMessage(PaddingError,
134
+ "expected pad-bits 0010, got 0110",
135
+ cf.unpack, bitarray("0110"))
136
+
137
+ def test_unpack_from(self):
138
+ cf = compile(Example.format)
139
+ for offset in 0, 1, 357:
140
+ a = urandom(512)
141
+ a[offset:offset + cf.width] = Example.bits
142
+ a = frozenbitarray(a)
143
+ if offset == 0:
144
+ self.assertEqual(cf.unpack_from(a), Example.values)
145
+ self.assertEqual(cf.unpack_from(a, offset), Example.values)
146
+
147
+ def test_unpack_from_errors(self):
148
+ cf = compile("u8")
149
+ self.assertRaisesMessage(TypeError, "bitarray expected, got 'list'",
150
+ cf.unpack_from, 8 * [0])
151
+ a = cf.pack(123)
152
+ self.assertEqual(len(a), 8)
153
+ self.assertRaises(TypeError, cf.unpack_from, a, 0.0)
154
+ for offset in 1, -7, -9, 9:
155
+ msg = ("offset %d out of range for bitarray size 8" % offset
156
+ if offset in (-9, 9) else
157
+ "bitarray of at least 9 bits required for unpacking 8 bits "
158
+ "at offset 1 (actual bitarray size is 8)")
159
+ self.assertRaisesMessage(ValueError, msg,
160
+ cf.unpack_from, a, offset)
161
+
162
+ def test_overlapping_view(self):
163
+ a = bitarray("00000000 11111111")
164
+ x = bitarray(buffer=memoryview(a)[1:2], endian=a.endian)
165
+ y = bitarray(buffer=memoryview(a)[0:1], endian=a.endian)
166
+ compile("b8 b8").pack_into(a, 0, x, y)
167
+ self.assertEqual(a, bitarray("11111111 00000000"))
168
+
169
+ def test_empty_format(self):
170
+ cf = compile("")
171
+ self.assertEqual(cf.pack(), bitarray())
172
+ a = bitarray()
173
+ cf.pack_into(a, 0)
174
+ self.assertEqual(len(a), 0)
175
+ self.assertEqual(cf.unpack(a), ())
176
+ self.assertEqual(cf.unpack_from(a), ())
177
+ self.assertEqual(cf.unpack_from(a, 0), ())
178
+ for f in cf.pack_into, cf.unpack_from:
179
+ msg = "offset 1 out of range for bitarray size 0"
180
+ self.assertRaisesMessage(ValueError, msg, f, a, 1)
181
+ a = bitarray("0")
182
+ cf.pack_into(a, 1)
183
+ self.assertEqual(a.to01(), "0")
184
+ for offset in 0, 1:
185
+ self.assertEqual(cf.unpack_from(a, offset), ())
186
+ for f in cf.pack_into, cf.unpack_from:
187
+ msg = "offset 2 out of range for bitarray size 1"
188
+ self.assertRaisesMessage(ValueError, msg, f, a, 2)
189
+
190
+
191
+ class StructTests(unittest.TestCase, Util):
192
+
193
+ all_codes = "us?fhbBpPxX"
194
+
195
+ def test_cached(self):
196
+ self.assertIs(compile("u8"), compile("u8"))
197
+ self.assertIsNot(Struct("u8"), compile("u8"))
198
+ self.assertEqual(Struct("u8"), compile("u8"))
199
+
200
+ def test_struct_read_only(self):
201
+ cf = compile("u2 x s7 x X3 u b5")
202
+ self.assertEqual(len(cf._fields), 7)
203
+ self.assertEqual(cf.width, 20)
204
+ self.assertEqual(cf.values, 4)
205
+ for name in "_fields", "width", "values", "_names":
206
+ self.assertRaises(dataclasses.FrozenInstanceError,
207
+ setattr, cf, name, 0)
208
+
209
+ def test_zero_width(self):
210
+ for c in self.all_codes:
211
+ msg = "field width cannot be zero: '%s0'" % c
212
+ self.assertRaisesMessage(ValueError, msg, compile, c + "0")
213
+
214
+ def test_default_width(self):
215
+ for c in self.all_codes:
216
+ if c == "f":
217
+ msg = "float field width must be 16, 32 or 64, got 1"
218
+ self.assertRaisesMessage(ValueError, msg, compile, c)
219
+ continue
220
+ cf = compile(c)
221
+ width = {"h": 4, "B": 8}.get(c, 1)
222
+ self.assertEqual(cf.width, width)
223
+ if c == "?":
224
+ self.assertEqual(cf.format(), "<?")
225
+ continue
226
+ self.assertEqual(cf.format(), "<%s%d" % (c, width))
227
+
228
+ def test_pickle(self):
229
+ fmt = ">x2 <u3{red} >s5{green} <u4{blue} <x3"
230
+ cf = compile(fmt)
231
+ self.assertEqual(pickle.loads(pickle.dumps(cf)), cf)
232
+
233
+ def test_format_ascii(self):
234
+ # Arabic-Indic digit two
235
+ self.assertRaises(ValueError, compile, "s\u0662")
236
+ # Non-breaking space
237
+ self.assertRaises(ValueError, compile, "s8\u00a0s8")
238
+
239
+ def test_format_canonical(self):
240
+ for fmt in [">u7", "<f16{value}", ">x2 <u3{foo}", "<s1{a} >s7{b}",
241
+ ">?{switch} >B80{raw} <b15{array}",
242
+ "<p5{11011} >x4{1010}"]:
243
+ self.assertEqual(compile(fmt).format(), fmt)
244
+
245
+ def test_format_equivalence(self):
246
+ for fmts in [ # canonical format first
247
+ ("<u1", "u", "<u", "u1"),
248
+ ("<?", "?", "?1", "<?1"),
249
+ ("<u1 <?", "u?", "\tu\v?\n", " u\r?1"),
250
+ ("<u3 <s5 >? >x2", "u3 s5 >?1 x{00}"),
251
+ ("<u1{a} <P3 <p2 <u2{A}", "u{a}P3p2u2{A}"),
252
+ ("<u9 <u9", "u9u9"),
253
+ ("<u22 <u1", "u22u"),
254
+ ("<u1 <p3{101} <p2", "up3{101}p2"),
255
+ ("<p1", "p1{0}", "p1{_0}", "P{0__}"),
256
+ ("<X1", "x1{1}", "x1{1_}", "X{_1_}"),
257
+ ("<p2", "p{00}", "P{00}", "P2{00}", "P{0_0}", "P2{_00_}"),
258
+ ("<P3", "p{11_1}", "P{111}", "p3{111}", "P3{111_}"),
259
+ ("<p2{10}", "p{10}", "P2{10}", "p{1_0}", "P{_10_}"),
260
+ ("<x3{110}", "x{110}", "X3{110}", "X{11_0}", "x{_110_}"),
261
+ ]:
262
+ canon_format = fmts[0]
263
+ cf = compile(canon_format)
264
+ for fmt in fmts:
265
+ self.assertEqual(compile(fmt).format(), canon_format)
266
+ self.assertEqual(compile(fmt), cf)
267
+
268
+ def test_format_comments(self):
269
+ # Note that unlike the format string itself, comments may contain
270
+ # non-ASCII characters.
271
+ cf = compile("""
272
+ # IPv4 header format
273
+ >u4 # version (\u0662)
274
+ u4 # header length
275
+ u6 u2 # DSCP, ECN
276
+ """)
277
+ self.assertEqual(cf.format(), ">u4 >u4 >u6 >u2")
278
+
279
+ def test_format_names(self):
280
+ fmt = ">x2 <u3{red} >s5{green} <u4{blue} <x3"
281
+ cf = compile(fmt)
282
+ self.assertEqual(len(cf._names), cf.values)
283
+ self.assertEqual(cf._names, ("red", "green", "blue"))
284
+ self.assertEqual(cf.format(), fmt)
285
+ self.assertNotEqual(compile("u{a}"), compile("u{b}"))
286
+
287
+ def test_format_invalid(self):
288
+ for format in "<", "<<u8", "3x", "u8junk", "!", "q8", ">0z":
289
+ self.assertRaises(ValueError, compile, format)
290
+
291
+ def test_format_errors(self):
292
+ for fmt, msg in [
293
+ ("=u8", "invalid format: '=u8'"),
294
+ ("u 9u9", "invalid format: '9u9'"),
295
+ ("1", "invalid format: '1'"),
296
+ ("?A", "invalid code: 'A'"),
297
+ ("u8j", "invalid code: 'j'"),
298
+ ("u{}", "invalid format: '{}'"),
299
+ ("?2", "bool field width must be 1, got 2"),
300
+ # padding literal errors
301
+ ("x{102}", "expected '0' or '1' (or whitespace or "
302
+ "underscore), got '2' (0x32)"),
303
+ ("p2{0}", "pad-bits width 2 does not match pattern length 1"),
304
+ ("p0{1}", "field width cannot be zero: 'p0{1}'"),
305
+ ("x{_}", "field width cannot be zero: 'x{_}'"),
306
+ # name field errors
307
+ ("u{a} s{a}", "Encountered duplicate field name: 'a'"),
308
+ ("u{_a}", "Field names cannot start with an underscore: '_a'"),
309
+ ("u{01}", "Type names and field names must be valid "
310
+ "identifiers: '01'"),
311
+ ("u{if}", "Type names and field names cannot be a "
312
+ "keyword: 'if'"),
313
+ ("u{a} s", "Some but not all fields have a name"),
314
+ ]:
315
+ self.assertRaisesMessage(ValueError, msg, compile, fmt)
316
+
317
+ def test_format_empty(self):
318
+ self.assertRaises(TypeError, Struct)
319
+ for fmt in "", " ", " ", "\n\r\t\v", "# comment", "#":
320
+ cf = compile(fmt)
321
+ self.assertEqual(cf, Struct(""))
322
+ self.assertEqual(cf.width, 0)
323
+ self.assertEqual(cf.values, 0)
324
+ self.assertEqual(cf.format(), "")
325
+ a = cf.pack()
326
+ self.assertEqual(len(a), 0)
327
+ self.assertEqual(a.endian, DEFAULT_ENDIAN)
328
+
329
+ def test_repr(self):
330
+ self.assertEqual(repr(compile("")), "Struct('')")
331
+ self.assertEqual(repr(compile("u2 >s7 x")), "Struct('<u2 >s7 >x1')")
332
+
333
+ def test_endian(self):
334
+ for fmt, endian in [("<u4", "little"),
335
+ (">u4", "big"),
336
+ ("u4", DEFAULT_ENDIAN)]:
337
+ cf = compile(fmt)
338
+ a = cf.pack(11)
339
+ self.assertEqual(len(a), 4)
340
+ self.assertEqual(a.endian, endian)
341
+ self.assertEqual(compile(cf.format()), cf)
342
+ value = 11
343
+ self.assertEqual(cf.unpack(cf.pack(value)), (value,))
344
+
345
+
346
+ class ModuleFunctionTests(unittest.TestCase, Util):
347
+
348
+ def test_pack(self):
349
+ a = pack(Example.format, *Example.values)
350
+ self.assertIs(type(a), bitarray)
351
+ self.assertEqual(a, Example.bits)
352
+
353
+ def test_pack_into(self):
354
+ a = urandom(1024)
355
+ b = a.copy()
356
+ offset = 123
357
+ pack_into(Example.format, a, offset, *Example.values)
358
+ width = Example.width
359
+ self.assertEqual(a[:offset], b[:offset])
360
+ self.assertEqual(a[offset:offset + width], Example.bits)
361
+ self.assertEqual(a[offset + width:], b[offset + width:])
362
+
363
+ def test_unpack(self):
364
+ values = unpack(Example.format, Example.bits)
365
+ self.assertIs(type(values), tuple)
366
+ self.assertEqual(values, Example.values)
367
+
368
+ def test_unpack_from(self):
369
+ a = urandom(1024)
370
+ offset = 129
371
+ a[offset:offset + Example.width] = Example.bits
372
+ values = unpack_from(Example.format, a, offset)
373
+ self.assertIs(type(values), tuple)
374
+ self.assertEqual(values, Example.values)
375
+
376
+ def test_unpack_from_default_offset(self):
377
+ a = bitarray(Example.bits, "little")
378
+ a.fill(256)
379
+ values = unpack_from(Example.format, a)
380
+ self.assertIs(type(values), tuple)
381
+ self.assertEqual(values, Example.values)
382
+
383
+ def test_calcsize(self):
384
+ size = calcsize(Example.format)
385
+ self.assertIs(type(size), int)
386
+ self.assertEqual(size, Example.width)
387
+ self.assertEqual(calcsize(""), 0)
388
+
389
+
390
+ class FieldTests(unittest.TestCase):
391
+
392
+ # format value type bitarray
393
+ data = [
394
+ ("<u11", 5, int, "10100000000"),
395
+ (">u12", 6, int, "000000000110"),
396
+ ("<s11", -2, int, "01111111111"),
397
+ (">s12", -4, int, "111111111100"),
398
+ ("<?", True, bool, "1"),
399
+ ("<f16", -1.5, float, "0000000001 11110 1"),
400
+ (">f16", 1.875, float, "0 01111 1110000000"),
401
+ ("<h12", "af1", str, "0101 1111 1000"),
402
+ (">h12", "2c3", str, "0010 1100 0011"),
403
+ ("<b3", bitarray("110"), bitarray, "110"),
404
+ ("<B16", b"AC", bytes, "10000010 11000010"),
405
+ (">B16", b"A ", bytes, "01000001 00100000"),
406
+ ("<p3", None, None, "000"),
407
+ (">P5", None, None, "11111"),
408
+ (">x4", None, None, "0000"),
409
+ ("<X2", None, None, "11"),
410
+ ("<p2{10}", None, None, "10"),
411
+ ]
412
+
413
+ def test_compile(self):
414
+ for fmt, value, tp, s in self.data:
415
+ for optname in "", "{Foo}", "{bar_}":
416
+ if optname and value is None:
417
+ continue
418
+ cf = compile(fmt + optname)
419
+ self.assertEqual(cf.width, len(bitarray(s)))
420
+ self.assertEqual(cf.values, 0 if value is None else 1)
421
+ self.assertEqual(cf.format(), fmt + optname)
422
+
423
+ def test_pack(self):
424
+ for fmt, value, tp, s in self.data:
425
+ values = []
426
+ if value is not None:
427
+ self.assertIs(type(value), tp)
428
+ values.append(value)
429
+ a = pack(fmt, *values)
430
+ self.assertIs(type(a), bitarray)
431
+ self.assertEqual(a.endian, _ENDIAN_FROM_PREFIX[fmt[0]])
432
+ self.assertEqual(a, bitarray(s))
433
+
434
+ def test_unpack(self):
435
+ for fmt, value, tp, s in self.data:
436
+ for endian in "little", "big":
437
+ for a in bitarray(s, endian), frozenbitarray(s, endian):
438
+ b = unpack(fmt, a)
439
+ self.assertIs(type(b), tuple)
440
+ if value is None:
441
+ self.assertEqual(len(b), 0)
442
+ continue
443
+ self.assertEqual(len(b), 1)
444
+ self.assertIs(type(b[0]), tp)
445
+ self.assertEqual(b[0], value)
446
+
447
+ def test_unpack_named(self):
448
+ for fmt, value, tp, s in self.data:
449
+ if value is None:
450
+ continue
451
+ a = bitarray(s)
452
+ b = unpack(fmt + "{Foo}", a)
453
+ self.assertIsInstance(b, tuple)
454
+ self.assertEqual(len(b), 1)
455
+ self.assertEqual(b._fields, ("Foo",))
456
+ self.assertIs(b[0], b.Foo)
457
+ self.assertIs(type(b.Foo), tp)
458
+ self.assertEqual(b.Foo, value)
459
+
460
+ def test_roundtrip(self):
461
+ for fmt, value, tp, s in self.data:
462
+ for pre1, pre2 in "<<", "<>", "><", ">>":
463
+ mixed_fmt = "%su3 %s %su3" % (pre1, fmt, pre2)
464
+ values = [1, 3]
465
+ if value is not None:
466
+ values.insert(1, value)
467
+ a = pack(mixed_fmt, *values)
468
+ self.assertEqual(a.endian, _ENDIAN_FROM_PREFIX[pre1])
469
+ self.assertEqual(a[3:-3], bitarray(s))
470
+ self.assertEqual(unpack(mixed_fmt, a), tuple(values))
471
+
472
+ def test_mixed(self):
473
+ for c, v in [("u8", 1),
474
+ ("s8", -10),
475
+ ("?", True),
476
+ ("f16", -2.0),
477
+ ("h4", "c"),
478
+ ("B8", b"A")]:
479
+ cf = compile("<%s>%s" % (c, c))
480
+ a = cf.pack(v, v)
481
+ self.assertEqual(cf.unpack(a), (v, v))
482
+ # Little and big-endian encodings are reverses of each other.
483
+ self.assertEqual(a[::-1], a)
484
+
485
+ cf = compile("<b4>b4")
486
+ a = bitarray("0110", "big")
487
+ b = bitarray("1100", "little")
488
+ packed = cf.pack(a, b)
489
+ self.assertEqual(packed.endian, "little")
490
+ self.assertEqual(packed, a + b)
491
+ x, y = cf.unpack(packed)
492
+ self.assertEqual((x, y), (a, b))
493
+ self.assertEqual((x.endian, y.endian), ("little", "big"))
494
+
495
+ def test_unsigned_int(self):
496
+ cf = compile("<u9")
497
+ self.assertRaises(TypeError, cf.pack, 1.0)
498
+ for nbits in range(1, 20):
499
+ for pre, endian in _ENDIAN_FROM_PREFIX.items():
500
+ cf = compile("%su%d" % (pre, nbits))
501
+ self.assertEqual(cf.width, nbits)
502
+ self.assertEqual(cf.values, 1)
503
+ limit = 1 << nbits
504
+ for v, s in [(0, nbits * "0"),
505
+ (1, (nbits - 1) * "0" + "1"),
506
+ (limit // 2, "1" + (nbits - 1) * "0"),
507
+ (limit - 1, nbits * "1")]:
508
+ a = cf.pack(v)
509
+ self.assertEqual(a.endian, endian)
510
+ self.assertEqual(a.to01(), s[::-1] if pre == "<" else s)
511
+ self.assertEqual(cf.unpack(a), (v,))
512
+ self.assertRaises(OverflowError, cf.pack, -1)
513
+ self.assertRaises(OverflowError, cf.pack, limit)
514
+
515
+ def test_signed_int(self):
516
+ cf = compile("<s10")
517
+ self.assertRaises(TypeError, cf.pack, -2.0)
518
+ for nbits in range(1, 20):
519
+ for pre, endian in _ENDIAN_FROM_PREFIX.items():
520
+ cf = compile("%ss%d" % (pre, nbits))
521
+ self.assertEqual(cf.width, nbits)
522
+ self.assertEqual(cf.values, 1)
523
+ limit = 1 << (nbits - 1)
524
+ for v, s in [(0, nbits * "0"),
525
+ (1, (nbits - 1) * "0" + "1"),
526
+ (-1, nbits * "1"),
527
+ (limit - 1, "0" + (nbits - 1) * "1"),
528
+ (-limit, "1" + (nbits - 1) * "0")]:
529
+ if nbits == 1 and v == 1:
530
+ continue
531
+ a = cf.pack(v)
532
+ self.assertEqual(a.endian, endian)
533
+ self.assertEqual(a.to01(), s[::-1] if pre == "<" else s)
534
+ self.assertEqual(cf.unpack(a), (v,))
535
+ self.assertRaises(OverflowError, cf.pack, -limit - 1)
536
+ self.assertRaises(OverflowError, cf.pack, limit)
537
+
538
+ def test_bool(self):
539
+ cf = compile("?")
540
+ self.assertEqual(cf.width, 1)
541
+ self.assertEqual(cf.values, 1)
542
+ for value in False, True, 0, 1, 2, "", "ya":
543
+ a = cf.pack(value)
544
+ self.assertEqual(a.endian, "little")
545
+ self.assertEqual(len(a), 1)
546
+ self.assertEqual(a[0], bool(value))
547
+ v, = cf.unpack(a)
548
+ self.assertEqual(type(v), bool)
549
+ self.assertIs(v, bool(value))
550
+
551
+ # list of (nbits, exponent bits)
552
+ float_sizes = [(16, 5), (32, 8), (64, 11)]
553
+
554
+ def test_float_1_5(self):
555
+ for nbits, exp_bits in self.float_sizes:
556
+ for pre, endian in _ENDIAN_FROM_PREFIX.items():
557
+ cf = compile("%sf%d" % (pre, nbits))
558
+ self.assertEqual(cf.width, nbits)
559
+ self.assertEqual(cf.values, 1)
560
+ a = cf.pack(1.5)
561
+ self.assertEqual(len(a), nbits)
562
+ self.assertEqual(a.endian, endian)
563
+ s = "0 0%s 1%s" % ((exp_bits - 1) * "1",
564
+ (nbits - exp_bits - 2) * "0")
565
+ if endian == "little":
566
+ s = s[::-1]
567
+ self.assertEqual(a, bitarray(s))
568
+ self.assertEqual(cf.unpack(a), (1.5,))
569
+
570
+ def test_float_special(self):
571
+ for nbits, exp_bits in self.float_sizes:
572
+ cf = compile(">f%d" % nbits)
573
+ # -0.0
574
+ a = cf.pack(-0.0)
575
+ self.assertEqual(len(a), nbits)
576
+ s = "1 %s" % ((nbits - 1) * "0")
577
+ self.assertEqual(a, bitarray(s))
578
+ x, = cf.unpack(a)
579
+ self.assertEqual(x, 0.0)
580
+ self.assertEqual(math.copysign(1.0, x), -1.0)
581
+ # infinity
582
+ a = cf.pack(float("inf"))
583
+ self.assertEqual(len(a), nbits)
584
+ s = "0 %s %s" % (exp_bits * "1", (nbits - exp_bits - 1) * "0")
585
+ self.assertEqual(a, bitarray(s))
586
+ self.assertEqual(cf.unpack(a), (float('inf'),))
587
+ # nan
588
+ a = cf.pack(float("nan"))
589
+ self.assertEqual(len(a), nbits)
590
+ s = "0 %s 1%s" % (exp_bits * "1", (nbits - exp_bits - 2) * "0")
591
+ self.assertEqual(a, bitarray(s))
592
+ self.assertTrue(math.isnan(cf.unpack(a)[0]))
593
+
594
+ def test_float_endianness(self):
595
+ for nbits in 16, 32, 64:
596
+ # Symmetric padding tests unaligned fields while preserving
597
+ # the palindrome.
598
+ cf = compile("p{100} <f%d p{1001} >f%d p{001}" % (nbits, nbits))
599
+ for x in 0.0, -0.0, -985.5, 1.875, float("inf"), float("-inf"):
600
+ a = cf.pack(x, x)
601
+ for v in cf.unpack(a):
602
+ self.assertEqual(v, x)
603
+ self.assertEqual(math.copysign(1.0, v),
604
+ math.copysign(1.0, x))
605
+ # Little and big-endian encodings are reverses of each other.
606
+ self.assertEqual(a[::-1], a)
607
+ a = cf.pack(float("nan"), float("nan"))
608
+ for v in cf.unpack(a):
609
+ self.assertTrue(math.isnan(v))
610
+ self.assertEqual(a[::-1], a)
611
+
612
+ def test_struct_bytes(self):
613
+ for nbits, struct_code in (16, "e"), (32, "f"), (64, "d"):
614
+ for pre, endian in _ENDIAN_FROM_PREFIX.items():
615
+ bf = "%sf%d" % (pre, nbits) # bitfields format
616
+ sf = pre + struct_code # struct format
617
+ for x in 1.875, 2.0, 7, -13, True, -0.0, float("inf"):
618
+ b = struct.pack(sf, x)
619
+ self.assertEqual(bytes(pack(bf, x)), b)
620
+ self.assertEqual(unpack(bf, bitarray(b, endian)), (x,))
621
+ self.assertEqual(struct.unpack(sf, b), (x,))
622
+
623
+ def test_float_errors(self):
624
+ cf = compile("f16")
625
+ self.assertRaises(struct.error, cf.pack, b"AB")
626
+ self.assertRaises(ValueError, compile, "f8")
627
+
628
+ def test_hex(self):
629
+ self.assertRaises(ValueError, compile, "h7")
630
+ cf = compile("<h20")
631
+ self.assertEqual(cf.width, 20)
632
+ self.assertEqual(cf.values, 1)
633
+ a = cf.pack("1fA73")
634
+ self.assertEqual(cf.pack("1f a73"), a) # whitespace is ignored
635
+ self.assertEqual(a.endian, "little")
636
+ self.assertEqual(a, bitarray("1000 1111 0101 1110 1100"))
637
+ self.assertEqual(cf.unpack(a), ("1fa73",))
638
+ self.assertRaises(TypeError, cf.pack, b"1fa73")
639
+ self.assertRaises(ValueError, cf.pack, "1fa7")
640
+ self.assertRaises(ValueError, cf.unpack, bitarray(19))
641
+ self.assertEqual(compile("h").format(), "<h4")
642
+
643
+ def test_bitarray(self):
644
+ for n in range(1, 20):
645
+ cf = compile("<b%d" % n)
646
+ self.assertEqual(cf.width, n)
647
+ self.assertEqual(cf.values, 1)
648
+ a = urandom(n, "big")
649
+ b = cf.pack(a)
650
+ self.assertEqual(b.endian, "little")
651
+ self.assertEqual(b, a)
652
+ self.assertEqual(cf.unpack(b), (a,))
653
+ cf = compile("b11")
654
+ self.assertRaises(TypeError, cf.pack, 12)
655
+ self.assertRaises(ValueError, cf.pack, bitarray(10))
656
+
657
+ def test_bytes(self):
658
+ self.assertRaises(ValueError, compile, "B7")
659
+ for n in range(1, 10):
660
+ cf = compile("<B%d" % (8 * n))
661
+ self.assertEqual(cf.width, 8 * n)
662
+ self.assertEqual(cf.values, 1)
663
+ b = os.urandom(n)
664
+ a = cf.pack(b)
665
+ self.assertEqual(a.endian, "little")
666
+ self.assertEqual(bytes(a), b)
667
+ c, = cf.unpack(a)
668
+ self.assertIs(type(c), bytes)
669
+ self.assertEqual(c, b)
670
+ cf = compile(">B24")
671
+ self.assertRaises(TypeError, cf.pack, 12)
672
+ self.assertRaises(ValueError, cf.pack, b"AB")
673
+ self.assertEqual(cf.pack(bytearray(b"XYZ")), bitarray(b"XYZ", "big"))
674
+ self.assertEqual(compile("B").format(), "<B8")
675
+
676
+ def test_padding(self):
677
+ cf = compile("p3 P x4 X2")
678
+ self.assertEqual(cf.width, 10)
679
+ self.assertEqual(cf.values, 0)
680
+ self.assertEqual(cf.format(), "<p3 <P1 <x4 <X2")
681
+ a = cf.pack()
682
+ self.assertEqual(a, bitarray("000 1 0000 11"))
683
+ res = cf.unpack(a)
684
+ self.assertEqual(res, tuple())
685
+ # p / P padding bits are validated
686
+ self.assertRaises(ValueError, cf.unpack, bitarray("010 1 0000 11"))
687
+ self.assertRaises(ValueError, cf.unpack, bitarray("000 0 0000 11"))
688
+ # x / X padding bits are ignored rather than validated
689
+ cf.unpack(bitarray("000 1 0000 10"))
690
+ cf.unpack(bitarray("000 1 0010 11"))
691
+ # wrong length
692
+ self.assertRaises(ValueError, cf.unpack, bitarray("000 1 0000 111"))
693
+
694
+ def test_padding_pattern(self):
695
+ for code in "pPxX":
696
+ cf = compile(code + "{11011}")
697
+ self.assertEqual(cf.width, 5)
698
+ self.assertEqual(cf.format(), "<%s5{11011}" % code.lower())
699
+ self.assertEqual(compile(cf.format()), cf)
700
+ self.assertEqual(cf.pack(), bitarray("11011"))
701
+ self.assertEqual(cf.unpack(bitarray("11011")), ())
702
+ if code in "pP":
703
+ self.assertRaises(ValueError, cf.unpack, bitarray("11001"))
704
+ self.assertRaises(PaddingError, cf.unpack, bitarray("00000"))
705
+ else:
706
+ self.assertEqual(cf.unpack(bitarray("00100")), ())
707
+
708
+ def test_padding_pattern_endianness(self):
709
+ for pre, endian in _ENDIAN_FROM_PREFIX.items():
710
+ cf = compile("%sP{1000_0111}" % pre)
711
+ self.assertEqual(cf.format(), "%sp8{10000111}" % pre)
712
+ a = bitarray("1000_0111")
713
+ b = cf.pack()
714
+ # Equality is independent of the bitarrays' endianness.
715
+ self.assertEqual(b, a)
716
+ self.assertEqual(b.endian, endian)
717
+ self.assertEqual(cf.unpack(a), ())
718
+
719
+
720
+ if __name__ == '__main__':
721
+ unittest.main()