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.
- bitarray/__init__.py +66 -0
- bitarray/__init__.pyi +176 -0
- bitarray/_bitarray.cpython-313-wasm32-emscripten.so +0 -0
- bitarray/_util.cpython-313-wasm32-emscripten.so +0 -0
- bitarray/bitarray.h +357 -0
- bitarray/bitfields.py +447 -0
- bitarray/py.typed +0 -0
- bitarray/pythoncapi_compat.h +3138 -0
- bitarray/test_281.pickle +0 -0
- bitarray/test_bitarray.py +5788 -0
- bitarray/test_bitfields.py +721 -0
- bitarray/test_free_threading.py +840 -0
- bitarray/test_util.py +3166 -0
- bitarray/util.py +694 -0
- bitarray/util.pyi +86 -0
- bitarray-3.12.2.dist-info/METADATA +1134 -0
- bitarray-3.12.2.dist-info/RECORD +20 -0
- bitarray-3.12.2.dist-info/WHEEL +5 -0
- bitarray-3.12.2.dist-info/licenses/LICENSE +46 -0
- bitarray-3.12.2.dist-info/top_level.txt +1 -0
|
@@ -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()
|