bitarray 3.12.2__tar.gz → 3.12.3__tar.gz

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.
Files changed (26) hide show
  1. {bitarray-3.12.2 → bitarray-3.12.3}/PKG-INFO +12 -10
  2. {bitarray-3.12.2 → bitarray-3.12.3}/README.rst +11 -9
  3. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/__init__.pyi +22 -20
  4. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/_bitarray.c +66 -46
  5. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/_util.c +69 -26
  6. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/bitarray.h +32 -7
  7. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/test_bitarray.py +134 -29
  8. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/test_util.py +67 -4
  9. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray.egg-info/PKG-INFO +12 -10
  10. {bitarray-3.12.2 → bitarray-3.12.3}/LICENSE +0 -0
  11. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/__init__.py +0 -0
  12. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/bitfields.py +0 -0
  13. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/py.typed +0 -0
  14. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/pythoncapi_compat.h +0 -0
  15. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/test_281.pickle +0 -0
  16. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/test_bitfields.py +0 -0
  17. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/test_free_threading.py +0 -0
  18. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/util.py +0 -0
  19. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray/util.pyi +0 -0
  20. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray.egg-info/SOURCES.txt +0 -0
  21. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray.egg-info/dependency_links.txt +0 -0
  22. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray.egg-info/not-zip-safe +0 -0
  23. {bitarray-3.12.2 → bitarray-3.12.3}/bitarray.egg-info/top_level.txt +0 -0
  24. {bitarray-3.12.2 → bitarray-3.12.3}/pyproject.toml +0 -0
  25. {bitarray-3.12.2 → bitarray-3.12.3}/setup.cfg +0 -0
  26. {bitarray-3.12.2 → bitarray-3.12.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitarray
3
- Version: 3.12.2
3
+ Version: 3.12.3
4
4
  Summary: efficient arrays of booleans -- C extension
5
5
  Home-page: https://github.com/ilanschnell/bitarray
6
6
  Author: Ilan Schnell
@@ -57,6 +57,7 @@ Key features
57
57
  their in-place versions ``&=``, ``|=``, ``^=``, ``<<=``, ``>>=``).
58
58
  * `Free-threading support <https://github.com/ilanschnell/bitarray/blob/master/doc/free_threading.rst>`__
59
59
  (for GIL-disabled CPython 3.14 and later)
60
+ * Written to `support all platforms supported by CPython and PyPy <https://github.com/ilanschnell/bitarray/blob/master/doc/platforms.rst>`__.
60
61
  * Fast methods for encoding and decoding variable-length
61
62
  `prefix codes <https://github.com/ilanschnell/bitarray/blob/master/doc/prefix_codes.rst>`__.
62
63
  * Bitarray objects support the `buffer protocol <https://github.com/ilanschnell/bitarray/blob/master/doc/buffer.rst>`__
@@ -92,7 +93,7 @@ versions. Which means you can simply:
92
93
 
93
94
  .. code-block:: shell-session
94
95
 
95
- $ pip install bitarray
96
+ $ python -m pip install bitarray
96
97
 
97
98
  Once you have installed the package, you may want to test it:
98
99
 
@@ -101,26 +102,27 @@ Once you have installed the package, you may want to test it:
101
102
  $ python -c 'import bitarray; bitarray.test()'
102
103
  bitarray is installed in: /Users/ilan/bitarray/bitarray
103
104
  sys.prefix: /Users/ilan/miniforge
104
- bitarray version: 3.12.2
105
+ bitarray version: 3.12.3
106
+ platform.machine(): arm64
107
+ platform.platform(): macOS-14.3-arm64-arm-64bit-Mach-O
108
+ sys.platform: darwin
105
109
  sys.version: 3.15.0 (main, Oct 9 2026) [Clang 21.1.8]
106
110
  sys.abiflags: ''
107
111
  sys._is_gil_enabled(): True
108
112
  pointer size: 64 bit
109
- sizeof(size_t): 8
110
- sizeof(bitarrayobject): 80
111
113
  HAVE_BUILTIN_BSWAP64: 1
112
114
  default bit-endianness: big
113
115
  machine byte-order: little
114
116
  Py_GIL_DISABLED: 0
115
117
  Py_DEBUG: 0
116
118
  DEBUG: 0
117
- .....................................s...................................
118
- ..........s.....................................s........................
119
+ .........................................................................
120
+ ................................................s........................
119
121
  ......s.........................................................
120
122
  ----------------------------------------------------------------------
121
- Ran 711 tests in 0.161s
123
+ Ran 723 tests in 0.163s
122
124
 
123
- OK (skipped=4)
125
+ OK (skipped=2)
124
126
 
125
127
  The ``test()`` function is part of the API. It will return
126
128
  a ``unittest.runner.TextTestResult`` object, such that one can verify that
@@ -312,7 +314,7 @@ and can therefore be used as a dictionary key:
312
314
  Reference
313
315
  =========
314
316
 
315
- bitarray version: 3.12.2 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
317
+ bitarray version: 3.12.3 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
316
318
 
317
319
  In the following, ``item`` and ``value`` are usually a single bit -
318
320
  an integer 0 or 1.
@@ -21,6 +21,7 @@ Key features
21
21
  their in-place versions ``&=``, ``|=``, ``^=``, ``<<=``, ``>>=``).
22
22
  * `Free-threading support <https://github.com/ilanschnell/bitarray/blob/master/doc/free_threading.rst>`__
23
23
  (for GIL-disabled CPython 3.14 and later)
24
+ * Written to `support all platforms supported by CPython and PyPy <https://github.com/ilanschnell/bitarray/blob/master/doc/platforms.rst>`__.
24
25
  * Fast methods for encoding and decoding variable-length
25
26
  `prefix codes <https://github.com/ilanschnell/bitarray/blob/master/doc/prefix_codes.rst>`__.
26
27
  * Bitarray objects support the `buffer protocol <https://github.com/ilanschnell/bitarray/blob/master/doc/buffer.rst>`__
@@ -56,7 +57,7 @@ versions. Which means you can simply:
56
57
 
57
58
  .. code-block:: shell-session
58
59
 
59
- $ pip install bitarray
60
+ $ python -m pip install bitarray
60
61
 
61
62
  Once you have installed the package, you may want to test it:
62
63
 
@@ -65,26 +66,27 @@ Once you have installed the package, you may want to test it:
65
66
  $ python -c 'import bitarray; bitarray.test()'
66
67
  bitarray is installed in: /Users/ilan/bitarray/bitarray
67
68
  sys.prefix: /Users/ilan/miniforge
68
- bitarray version: 3.12.2
69
+ bitarray version: 3.12.3
70
+ platform.machine(): arm64
71
+ platform.platform(): macOS-14.3-arm64-arm-64bit-Mach-O
72
+ sys.platform: darwin
69
73
  sys.version: 3.15.0 (main, Oct 9 2026) [Clang 21.1.8]
70
74
  sys.abiflags: ''
71
75
  sys._is_gil_enabled(): True
72
76
  pointer size: 64 bit
73
- sizeof(size_t): 8
74
- sizeof(bitarrayobject): 80
75
77
  HAVE_BUILTIN_BSWAP64: 1
76
78
  default bit-endianness: big
77
79
  machine byte-order: little
78
80
  Py_GIL_DISABLED: 0
79
81
  Py_DEBUG: 0
80
82
  DEBUG: 0
81
- .....................................s...................................
82
- ..........s.....................................s........................
83
+ .........................................................................
84
+ ................................................s........................
83
85
  ......s.........................................................
84
86
  ----------------------------------------------------------------------
85
- Ran 711 tests in 0.161s
87
+ Ran 723 tests in 0.163s
86
88
 
87
- OK (skipped=4)
89
+ OK (skipped=2)
88
90
 
89
91
  The ``test()`` function is part of the API. It will return
90
92
  a ``unittest.runner.TextTestResult`` object, such that one can verify that
@@ -276,7 +278,7 @@ and can therefore be used as a dictionary key:
276
278
  Reference
277
279
  =========
278
280
 
279
- bitarray version: 3.12.2 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
281
+ bitarray version: 3.12.3 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
280
282
 
281
283
  In the following, ``item`` and ``value`` are usually a single bit -
282
284
  an integer 0 or 1.
@@ -6,9 +6,11 @@ from abc import ABCMeta
6
6
  from collections.abc import Iterable, Iterator, Sequence
7
7
  from unittest.runner import TextTestResult
8
8
 
9
- from typing import Any, BinaryIO, Dict, Union, overload, NamedTuple
9
+ from typing import Any, BinaryIO, Dict, Union, overload, NamedTuple, TypeVar
10
10
 
11
11
 
12
+ _BitarrayT = TypeVar("_BitarrayT", bound="bitarray")
13
+
12
14
  CodeDict = Dict[Any, bitarray]
13
15
  # Python 3.12 has abc.Buffer which should be used instead
14
16
  BytesLike = Union[bytes, bytearray]
@@ -52,7 +54,7 @@ class bitarray:
52
54
  stop: int = ...) -> None: ...
53
55
 
54
56
  def clear(self) -> None: ...
55
- def copy(self) -> bitarray: ...
57
+ def copy(self: _BitarrayT) -> _BitarrayT: ...
56
58
  def count(self,
57
59
  sub_bitarray: Union[bitarray, int] = ...,
58
60
  start: int = ...,
@@ -111,8 +113,8 @@ class bitarray:
111
113
  @overload
112
114
  def __getitem__(self, i: int) -> int: ...
113
115
  @overload
114
- def __getitem__(self,
115
- s: Union[slice, bitarray, Sequence]) -> bitarray: ...
116
+ def __getitem__(self: _BitarrayT,
117
+ s: Union[slice, bitarray, Sequence]) -> _BitarrayT: ...
116
118
  @overload
117
119
  def __setitem__(self,
118
120
  i: Union[int, slice, Sequence],
@@ -127,28 +129,28 @@ class bitarray:
127
129
  def __buffer__(self, flags: int, /) -> memoryview: ...
128
130
  def __release_buffer__(self, buffer: memoryview, /) -> None: ...
129
131
 
130
- def __add__(self, other: bitarray) -> bitarray: ...
131
- def __iadd__(self, other: bitarray) -> bitarray: ...
132
- def __mul__(self, n: int) -> bitarray: ...
133
- def __imul__(self, n: int) -> bitarray: ...
134
- def __rmul__(self, n: int) -> bitarray: ...
132
+ def __add__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
133
+ def __iadd__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
134
+ def __mul__(self: _BitarrayT, n: int) -> _BitarrayT: ...
135
+ def __imul__(self: _BitarrayT, n: int) -> _BitarrayT: ...
136
+ def __rmul__(self: _BitarrayT, n: int) -> _BitarrayT: ...
135
137
 
136
138
  def __ge__(self, other: bitarray) -> bool: ...
137
139
  def __gt__(self, other: bitarray) -> bool: ...
138
140
  def __le__(self, other: bitarray) -> bool: ...
139
141
  def __lt__(self, other: bitarray) -> bool: ...
140
142
 
141
- def __and__(self, other: bitarray) -> bitarray: ...
142
- def __or__(self, other: bitarray) -> bitarray: ...
143
- def __xor__(self, other: bitarray) -> bitarray: ...
144
- def __iand__(self, other: bitarray) -> bitarray: ...
145
- def __ior__(self, other: bitarray) -> bitarray: ...
146
- def __ixor__(self, other: bitarray) -> bitarray: ...
147
- def __invert__(self) -> bitarray: ...
148
- def __lshift__(self, n: int) -> bitarray: ...
149
- def __rshift__(self, n: int) -> bitarray: ...
150
- def __ilshift__(self, n: int) -> bitarray: ...
151
- def __irshift__(self, n: int) -> bitarray: ...
143
+ def __and__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
144
+ def __or__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
145
+ def __xor__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
146
+ def __iand__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
147
+ def __ior__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
148
+ def __ixor__(self: _BitarrayT, other: bitarray) -> _BitarrayT: ...
149
+ def __invert__(self: _BitarrayT) -> _BitarrayT: ...
150
+ def __lshift__(self: _BitarrayT, n: int) -> _BitarrayT: ...
151
+ def __rshift__(self: _BitarrayT, n: int) -> _BitarrayT: ...
152
+ def __ilshift__(self: _BitarrayT, n: int) -> _BitarrayT: ...
153
+ def __irshift__(self: _BitarrayT, n: int) -> _BitarrayT: ...
152
154
 
153
155
  # data descriptors
154
156
  @property
@@ -224,16 +224,16 @@ bytereverse(char *p, Py_ssize_t n)
224
224
  static void
225
225
  shift_r8le(unsigned char *buff, Py_ssize_t n, int k)
226
226
  {
227
- Py_ssize_t w = 0;
227
+ Py_ssize_t w = 0, r = n;
228
228
 
229
229
  #if HAVE_BUILTIN_BSWAP64 || PY_LITTLE_ENDIAN /* use shift word */
230
230
  w = n / 8; /* number of words used for shifting */
231
- n %= 8; /* number of remaining bytes */
231
+ r = n % 8; /* number of remaining bytes */
232
232
  #endif
233
- while (n--) { /* shift in byte-range(8 * w, n) */
234
- Py_ssize_t i = n + 8 * w;
233
+ while (r--) { /* shift in byte-range(8 * w, n) */
234
+ Py_ssize_t i = r + 8 * w;
235
235
  buff[i] <<= k; /* shift byte */
236
- if (n || w) /* add shifted next lower byte */
236
+ if (r || w) /* add shifted next lower byte */
237
237
  buff[i] |= buff[i - 1] >> (8 - k);
238
238
  }
239
239
  assert(w == 0 || ((uintptr_t) buff) % 8 == 0);
@@ -254,16 +254,16 @@ shift_r8le(unsigned char *buff, Py_ssize_t n, int k)
254
254
  static void
255
255
  shift_r8be(unsigned char *buff, Py_ssize_t n, int k)
256
256
  {
257
- Py_ssize_t w = 0;
257
+ Py_ssize_t w = 0, r = n;
258
258
 
259
259
  #if HAVE_BUILTIN_BSWAP64 || PY_BIG_ENDIAN /* use shift word */
260
260
  w = n / 8; /* number of words used for shifting */
261
- n %= 8; /* number of remaining bytes */
261
+ r = n % 8; /* number of remaining bytes */
262
262
  #endif
263
- while (n--) { /* shift in byte-range(8 * w, n) */
264
- Py_ssize_t i = n + 8 * w;
263
+ while (r--) { /* shift in byte-range(8 * w, n) */
264
+ Py_ssize_t i = r + 8 * w;
265
265
  buff[i] >>= k; /* shift byte */
266
- if (n || w) /* add shifted next lower byte */
266
+ if (r || w) /* add shifted next lower byte */
267
267
  buff[i] |= buff[i - 1] << (8 - k);
268
268
  }
269
269
  assert(w == 0 || ((uintptr_t) buff) % 8 == 0);
@@ -285,7 +285,7 @@ shift_r8be(unsigned char *buff, Py_ssize_t n, int k)
285
285
  static void
286
286
  shift_r8(bitarrayobject *self, Py_ssize_t a, Py_ssize_t b, int k)
287
287
  {
288
- unsigned char *buff = (unsigned char *) self->ob_item + a;
288
+ unsigned char *buff;
289
289
  Py_ssize_t n = b - a; /* number of bytes to be shifted */
290
290
  Py_ssize_t s = 0; /* distance to next aligned pointer */
291
291
 
@@ -296,8 +296,11 @@ shift_r8(bitarrayobject *self, Py_ssize_t a, Py_ssize_t b, int k)
296
296
  if (k == 0 || n <= 0)
297
297
  return;
298
298
 
299
+ assert(Py_SIZE(self));
300
+ buff = (unsigned char *) self->ob_item + a;
301
+
299
302
  if (n >= 8) {
300
- s = to_aligned((void *) buff);
303
+ s = to_aligned(buff);
301
304
  buff += s; /* align pointer for casting to (uint64_t *) */
302
305
  n -= s;
303
306
  }
@@ -464,14 +467,18 @@ invert_span(bitarrayobject *self, Py_ssize_t a, Py_ssize_t b)
464
467
  assert(self->readonly == 0);
465
468
 
466
469
  if (n >= 64) {
467
- const Py_ssize_t wa = (a + 63) / 64; /* word-range(wa, wb) */
468
- const Py_ssize_t wb = b / 64;
469
- uint64_t *wbuff = WBUFF(self);
470
+ uint64_t *words;
471
+ Py_ssize_t p1, p2, w;
472
+
473
+ w = aligned_words(self->ob_item, BYTES(a), b / 8, &p1, &p2);
474
+
475
+ invert_span(self, a, 8 * p1);
476
+
477
+ words = w ? (uint64_t *) (self->ob_item + p1) : NULL;
478
+ for (i = 0; i < w; i++)
479
+ words[i] = ~words[i];
470
480
 
471
- invert_span(self, a, 64 * wa);
472
- for (i = wa; i < wb; i++)
473
- wbuff[i] = ~wbuff[i];
474
- invert_span(self, 64 * wb, b);
481
+ invert_span(self, 8 * p2, b);
475
482
  }
476
483
  else if (n >= 8) {
477
484
  const Py_ssize_t ca = BYTES(a); /* char-range(ca, cb) */
@@ -574,15 +581,14 @@ count_span(bitarrayobject *self, Py_ssize_t a, Py_ssize_t b)
574
581
  assert(0 <= b && b <= self->nbits);
575
582
 
576
583
  if (n >= 64) {
577
- Py_ssize_t p = BYTES(a), w; /* first full byte */
578
- p += to_aligned((void *) (self->ob_item + p)); /* align pointer */
579
- w = (b / 8 - p) / 8; /* number of (full) words to count */
584
+ Py_ssize_t p1, p2, w;
580
585
 
581
- assert(8 * p - a < 64 && b - (8 * (p + 8 * w)) < 64 && w >= 0);
586
+ w = aligned_words(self->ob_item, BYTES(a), b / 8, &p1, &p2);
582
587
 
583
- cnt += count_span(self, a, 8 * p);
584
- cnt += popcnt_words((uint64_t *) (self->ob_item + p), w);
585
- cnt += count_span(self, 8 * (p + 8 * w), b);
588
+ cnt += count_span(self, a, 8 * p1);
589
+ if (w)
590
+ cnt += popcnt_words((uint64_t *) (self->ob_item + p1), w);
591
+ cnt += count_span(self, 8 * p2, b);
586
592
  }
587
593
  else if (n >= 8) {
588
594
  const Py_ssize_t ca = BYTES(a); /* char-range(ca, cb) */
@@ -636,31 +642,36 @@ static Py_ssize_t
636
642
  find_bit_words(bitarrayobject *self, int vi,
637
643
  Py_ssize_t a, Py_ssize_t b, int right)
638
644
  {
639
- const Py_ssize_t wa = (a + 63) / 64; /* word-range(wa, wb) */
640
- const Py_ssize_t wb = b / 64;
641
- const uint64_t *wbuff = WBUFF(self);
642
- const uint64_t w = vi ? 0 : ~0;
643
- Py_ssize_t res, i;
645
+ const uint64_t word = vi ? 0 : ~0;
646
+ const uint64_t *words;
647
+ Py_ssize_t p1, p2, n, q, res, i;
648
+
649
+ n = aligned_words(self->ob_item, BYTES(a), b / 8, &p1, &p2);
650
+ words = n ? (uint64_t *) (self->ob_item + p1) : NULL;
644
651
 
645
652
  if (right) {
646
- if ((res = find_bit(self, vi, 64 * wb, b, 1)) >= 0)
653
+ if ((res = find_bit(self, vi, 8 * p2, b, 1)) >= 0)
647
654
  return res;
648
655
 
649
- for (i = wb - 1; i >= wa; i--) { /* skip uint64 words */
650
- if (w ^ wbuff[i])
651
- return find_bit(self, vi, 64 * i, 64 * i + 64, 1);
656
+ for (i = n - 1; i >= 0; i--) { /* skip uint64 words */
657
+ if (word ^ words[i]) {
658
+ q = 8 * p1 + 64 * i;
659
+ return find_bit(self, vi, q, q + 64, 1);
660
+ }
652
661
  }
653
- return find_bit(self, vi, a, 64 * wa, 1);
662
+ return find_bit(self, vi, a, 8 * p1, 1);
654
663
  }
655
664
  else {
656
- if ((res = find_bit(self, vi, a, 64 * wa, 0)) >= 0)
665
+ if ((res = find_bit(self, vi, a, 8 * p1, 0)) >= 0)
657
666
  return res;
658
667
 
659
- for (i = wa; i < wb; i++) { /* skip uint64 words */
660
- if (w ^ wbuff[i])
661
- return find_bit(self, vi, 64 * i, 64 * i + 64, 0);
668
+ for (i = 0; i < n; i++) { /* skip uint64 words */
669
+ if (word ^ words[i]) {
670
+ q = 8 * p1 + 64 * i;
671
+ return find_bit(self, vi, q, q + 64, 0);
672
+ }
662
673
  }
663
- return find_bit(self, vi, 64 * wb, b, 0);
674
+ return find_bit(self, vi, 8 * p2, b, 0);
664
675
  }
665
676
  }
666
677
 
@@ -3428,16 +3439,25 @@ bitwise_lock_held(bitarrayobject *self,
3428
3439
  bitarrayobject *other, const char oper)
3429
3440
  {
3430
3441
  const Py_ssize_t nbytes = Py_SIZE(self);
3431
- const Py_ssize_t cwords = nbytes / 8; /* complete 64-bit words */
3432
- Py_ssize_t i;
3442
+ Py_ssize_t cwords, i;
3433
3443
  char *buff_s = self->ob_item;
3434
3444
  char *buff_o = other->ob_item;
3435
- uint64_t *wbuff_s = WBUFF(self);
3436
- uint64_t *wbuff_o = WBUFF(other);
3445
+ uint64_t *wbuff_s, *wbuff_o;
3437
3446
 
3438
3447
  assert(self->nbits == other->nbits);
3439
3448
  assert(self->endian == other->endian);
3440
3449
  assert(self->readonly == 0);
3450
+
3451
+ if (((uintptr_t) buff_s) % 8 == 0 &&
3452
+ ((uintptr_t) buff_o) % 8 == 0) {
3453
+ cwords = nbytes / 8; /* complete aligned 64-bit words */
3454
+ wbuff_s = WBUFF(self);
3455
+ wbuff_o = WBUFF(other);
3456
+ }
3457
+ else {
3458
+ cwords = 0;
3459
+ }
3460
+
3441
3461
  switch (oper) {
3442
3462
  case '&':
3443
3463
  for (i = 0; i < cwords; i++)
@@ -5120,7 +5140,7 @@ static PyTypeObject Bitarray_Type = {
5120
5140
  &bitarray_as_number, /* tp_as_number */
5121
5141
  &bitarray_as_sequence, /* tp_as_sequence */
5122
5142
  &bitarray_as_mapping, /* tp_as_mapping */
5123
- PyObject_HashNotImplemented, /* tp_hash */
5143
+ 0, /* tp_hash */
5124
5144
  0, /* tp_call */
5125
5145
  0, /* tp_str */
5126
5146
  PyObject_GenericGetAttr, /* tp_getattro */
@@ -205,6 +205,21 @@ count_n_lock_held(bitarrayobject *a, Py_ssize_t n, int vi)
205
205
  return i;
206
206
  }
207
207
 
208
+ static inline int
209
+ check_aligned(bitarrayobject *a)
210
+ {
211
+ #ifdef __sparc__
212
+ if (((uintptr_t) a->ob_item) % 8) {
213
+ PyErr_SetString(PyExc_NotImplementedError, "unaligned buffers "
214
+ "not supported on SPARC for this function");
215
+ return -1;
216
+ }
217
+ #else
218
+ (void) a; /* silence potential compiler warning about unused a */
219
+ #endif
220
+ return 0;
221
+ }
222
+
208
223
  static PyObject *
209
224
  count_n(PyObject *module, PyObject *args)
210
225
  {
@@ -220,6 +235,8 @@ count_n(PyObject *module, PyObject *args)
220
235
  PyErr_SetString(PyExc_ValueError, "non-negative integer expected");
221
236
  return NULL;
222
237
  }
238
+ if (check_aligned(a) < 0)
239
+ return NULL;
223
240
 
224
241
  Py_BEGIN_CRITICAL_SECTION(a);
225
242
  nbits = a->nbits;
@@ -250,19 +267,22 @@ static PyObject *
250
267
  parity(PyObject *module, PyObject *obj)
251
268
  {
252
269
  bitarrayobject *a;
253
- uint64_t x, *wbuff;
254
- Py_ssize_t i;
270
+ uint64_t x;
271
+ Py_ssize_t i, p1, p2;
255
272
 
256
273
  if (ensure_bitarray(obj) < 0)
257
274
  return NULL;
258
275
 
259
276
  a = (bitarrayobject *) obj;
260
277
  Py_BEGIN_CRITICAL_SECTION(a);
261
- wbuff = WBUFF(a);
262
- x = zlw(a);
263
- i = a->nbits / 64;
264
- while (i--)
265
- x ^= *wbuff++;
278
+ x = (unsigned char) zlc(a);
279
+ aligned_words(a->ob_item, 0, a->nbits / 8, &p1, &p2);
280
+ for (i = 0; i < p1; i++)
281
+ x ^= (unsigned char) a->ob_item[i];
282
+ for (i = p1; i < p2; i += 8)
283
+ x ^= *((uint64_t *) (a->ob_item + i));
284
+ for (i = p2; i < a->nbits / 8; i++)
285
+ x ^= (unsigned char) a->ob_item[i];
266
286
  Py_END_CRITICAL_SECTION();
267
287
  return PyLong_FromLong(parity_64(x));
268
288
  }
@@ -460,6 +480,8 @@ binary_function(PyObject *args, const char *format, const char oper)
460
480
  bitarray_type, (PyObject *) &a,
461
481
  bitarray_type, (PyObject *) &b))
462
482
  return NULL;
483
+ if (check_aligned(a) < 0 || check_aligned(b) < 0)
484
+ return NULL;
463
485
 
464
486
  Py_BEGIN_CRITICAL_SECTION2(a, b);
465
487
  if (ensure_eq_size_endian(a, b) == 0)
@@ -562,6 +584,8 @@ correspond_all(PyObject *module, PyObject *args)
562
584
  bitarray_type, (PyObject *) &a,
563
585
  bitarray_type, (PyObject *) &b))
564
586
  return NULL;
587
+ if (check_aligned(a) < 0 || check_aligned(b) < 0)
588
+ return NULL;
565
589
 
566
590
  Py_BEGIN_CRITICAL_SECTION2(a, b);
567
591
  if (ensure_eq_size_endian(a, b) == 0)
@@ -582,21 +606,22 @@ byteswap_lock_held(Py_buffer view, Py_ssize_t n)
582
606
  {
583
607
  char *buff = view.buf;
584
608
  Py_ssize_t m = view.len / n, k;
609
+ int is_aligned = ((uintptr_t) buff) % 8 == 0;
585
610
 
586
611
  assert(n >= 1 && n * m == view.len);
587
612
 
588
- if (n == 8 && HAVE_BUILTIN_BSWAP64) {
613
+ if (is_aligned && n == 8 && HAVE_BUILTIN_BSWAP64) {
589
614
  uint64_t *w = (uint64_t *) buff;
590
615
  for (k = 0; k < m; k++)
591
616
  w[k] = builtin_bswap64(w[k]);
592
617
  }
593
618
  #if (defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5)))
594
- else if (n == 4) {
619
+ else if (is_aligned && n == 4) {
595
620
  uint32_t *w = (uint32_t *) buff;
596
621
  for (k = 0; k < m; k++)
597
622
  w[k] = __builtin_bswap32(w[k]);
598
623
  }
599
- else if (n == 2) {
624
+ else if (is_aligned && n == 2) {
600
625
  uint16_t *w = (uint16_t *) buff;
601
626
  for (k = 0; k < m; k++)
602
627
  w[k] = __builtin_bswap16(w[k]);
@@ -1523,7 +1548,7 @@ sc_last_pop_seg(const Py_ssize_t *rts, Py_ssize_t nseg)
1523
1548
  /* expose sc_rts() to Python during debug mode for testing */
1524
1549
  #ifndef NDEBUG
1525
1550
  static PyObject *
1526
- module_sc_rts(PyObject *module, PyObject *obj)
1551
+ mod_sc_rts(PyObject *module, PyObject *obj)
1527
1552
  {
1528
1553
  PyObject *list;
1529
1554
  bitarrayobject *a;
@@ -1857,6 +1882,8 @@ sc_encode(PyObject *module, PyObject *obj)
1857
1882
 
1858
1883
  if (ensure_bitarray(obj) < 0)
1859
1884
  return NULL;
1885
+ if (check_aligned((bitarrayobject *) obj) < 0)
1886
+ return NULL;
1860
1887
 
1861
1888
  out = PyBytes_FromStringAndSize(NULL, ALLOC_SIZE);
1862
1889
  if (out == NULL)
@@ -2627,7 +2654,7 @@ static PyTypeObject CHDI_Type = {
2627
2654
 
2628
2655
  #ifndef NDEBUG
2629
2656
  static PyObject *
2630
- module_setup_table(PyObject *module, PyObject *obj)
2657
+ mod_setup_table(PyObject *module, PyObject *obj)
2631
2658
  {
2632
2659
  char table[256];
2633
2660
 
@@ -2641,7 +2668,7 @@ module_setup_table(PyObject *module, PyObject *obj)
2641
2668
  This makes testing easier, because the int result would depend
2642
2669
  on the machine byteorder. */
2643
2670
  static PyObject *
2644
- module_zlw(PyObject *module, PyObject *obj)
2671
+ mod_zlw(PyObject *module, PyObject *obj)
2645
2672
  {
2646
2673
  bitarrayobject *a, *res;
2647
2674
  uint64_t w;
@@ -2663,7 +2690,22 @@ module_zlw(PyObject *module, PyObject *obj)
2663
2690
  }
2664
2691
 
2665
2692
  static PyObject *
2666
- module_adjust_slice(PyObject *module, PyObject *args)
2693
+ mod_aligned_words(PyObject *module, PyObject *args)
2694
+ {
2695
+ Py_ssize_t start, stop, p1, p2, n;
2696
+ bitarrayobject *a;
2697
+
2698
+ if (!PyArg_ParseTuple(args, "O!nn", bitarray_type, (PyObject *) &a,
2699
+ &start, &stop))
2700
+ return NULL;
2701
+ Py_BEGIN_CRITICAL_SECTION(a);
2702
+ n = aligned_words(a->ob_item, start, stop, &p1, &p2);
2703
+ Py_END_CRITICAL_SECTION();
2704
+ return Py_BuildValue("nnn", n, p1, p2);
2705
+ }
2706
+
2707
+ static PyObject *
2708
+ mod_adjust_slice(PyObject *module, PyObject *args)
2667
2709
  {
2668
2710
  Py_ssize_t length, slicelength, start, stop, step;
2669
2711
 
@@ -2674,7 +2716,7 @@ module_adjust_slice(PyObject *module, PyObject *args)
2674
2716
  }
2675
2717
 
2676
2718
  static PyObject *
2677
- module_cfw(PyObject *module, PyObject *args) /* count_from_word() */
2719
+ mod_cfw(PyObject *module, PyObject *args) /* count_from_word() */
2678
2720
  {
2679
2721
  bitarrayobject *a;
2680
2722
  Py_ssize_t res, i;
@@ -2690,7 +2732,7 @@ module_cfw(PyObject *module, PyObject *args) /* count_from_word() */
2690
2732
  }
2691
2733
 
2692
2734
  static PyObject *
2693
- module_d2i(PyObject *module, PyObject *args)
2735
+ mod_d2i(PyObject *module, PyObject *args)
2694
2736
  {
2695
2737
  int m;
2696
2738
  char c;
@@ -2701,7 +2743,7 @@ module_d2i(PyObject *module, PyObject *args)
2701
2743
  }
2702
2744
 
2703
2745
  static PyObject *
2704
- module_read_n(PyObject *module, PyObject *args)
2746
+ mod_read_n(PyObject *module, PyObject *args)
2705
2747
  {
2706
2748
  PyObject *iter;
2707
2749
  Py_ssize_t i;
@@ -2715,7 +2757,7 @@ module_read_n(PyObject *module, PyObject *args)
2715
2757
  }
2716
2758
 
2717
2759
  static PyObject *
2718
- module_write_n(PyObject *module, PyObject *args)
2760
+ mod_write_n(PyObject *module, PyObject *args)
2719
2761
  {
2720
2762
  PyObject *res;
2721
2763
  char *str;
@@ -2803,14 +2845,15 @@ static PyMethodDef module_functions[] = {
2803
2845
 
2804
2846
  #ifndef NDEBUG
2805
2847
  /* functions exposed in debug mode for testing */
2806
- {"_setup_table", (PyCFunction) module_setup_table, METH_O, 0},
2807
- {"_zlw", (PyCFunction) module_zlw, METH_O, 0},
2808
- {"_adjust_slice", (PyCFunction) module_adjust_slice, METH_VARARGS, 0},
2809
- {"_cfw", (PyCFunction) module_cfw, METH_VARARGS, 0},
2810
- {"_d2i", (PyCFunction) module_d2i, METH_VARARGS, 0},
2811
- {"_read_n", (PyCFunction) module_read_n, METH_VARARGS, 0},
2812
- {"_write_n", (PyCFunction) module_write_n, METH_VARARGS, 0},
2813
- {"_sc_rts", (PyCFunction) module_sc_rts, METH_O, 0},
2848
+ {"_setup_table", (PyCFunction) mod_setup_table, METH_O, 0},
2849
+ {"_zlw", (PyCFunction) mod_zlw, METH_O, 0},
2850
+ {"_aligned_words", (PyCFunction) mod_aligned_words, METH_VARARGS, 0},
2851
+ {"_adjust_slice", (PyCFunction) mod_adjust_slice, METH_VARARGS, 0},
2852
+ {"_cfw", (PyCFunction) mod_cfw, METH_VARARGS, 0},
2853
+ {"_d2i", (PyCFunction) mod_d2i, METH_VARARGS, 0},
2854
+ {"_read_n", (PyCFunction) mod_read_n, METH_VARARGS, 0},
2855
+ {"_write_n", (PyCFunction) mod_write_n, METH_VARARGS, 0},
2856
+ {"_sc_rts", (PyCFunction) mod_sc_rts, METH_O, 0},
2814
2857
  #endif
2815
2858
 
2816
2859
  {NULL} /* sentinel */
@@ -4,7 +4,7 @@
4
4
 
5
5
  Author: Ilan Schnell
6
6
  */
7
- #define BITARRAY_VERSION "3.12.2"
7
+ #define BITARRAY_VERSION "3.12.3"
8
8
 
9
9
  #ifdef STDC_HEADERS
10
10
  # include <stddef.h>
@@ -124,19 +124,22 @@ zlc(bitarrayobject *self) /* zlc = zeroed last char */
124
124
  /* Return a uint64_t word representing the last (up to 63) remaining bits
125
125
  of the buffer. All missing bytes (to complete the word) and padbits are
126
126
  treated as zeros.
127
+ The remaining bytes are copied, so the buffer need not be word-aligned.
128
+ The result does not, in general, correspond to an aligned 64-bit
129
+ word in the buffer.
127
130
  If the length of the bitarray is a multiple of 64 (which also includes
128
131
  an empty bitarray), 0 is returned. */
129
132
  static inline uint64_t
130
133
  zlw(bitarrayobject *self) /* zlw = zeroed last word */
131
134
  {
132
135
  const size_t nbits = self->nbits;
133
- const size_t nw = (nbits / 64) * 8; /* bytes in complete words */
134
- const size_t nr = (nbits % 64) / 8; /* complete remaining bytes */
136
+ const size_t offset = (nbits / 64) * 8; /* bytes in complete words */
137
+ const size_t nr = (nbits % 64) / 8; /* complete remaining bytes */
135
138
  uint64_t res = 0;
136
139
 
137
- assert(nw + nr == nbits / 8 && 8 * (nw + nr) + nbits % 8 == nbits);
140
+ assert(offset + nr == nbits / 8);
138
141
  if (nr)
139
- memcpy((char *) &res, self->ob_item + nw, nr);
142
+ memcpy((char *) &res, self->ob_item + offset, nr);
140
143
  if (nbits % 8)
141
144
  *(((char *) &res) + nr) = zlc(self);
142
145
 
@@ -252,12 +255,35 @@ setup_table(char *table, char kop)
252
255
  cause problems on older ones. Moreover, it may lead to slowdown (even
253
256
  on modern compilers). */
254
257
  static inline int
255
- to_aligned(void *p)
258
+ to_aligned(const void *p)
256
259
  {
257
260
  int r = ((uintptr_t) p) % 8;
258
261
  return (8 - r) % 8;
259
262
  }
260
263
 
264
+ /* Return number of complete aligned words in byte-range(start, stop).
265
+ Set byte offsets *p1 and *p2 such that the aligned words are in
266
+ byte-range(*p1, *p2). */
267
+ static inline Py_ssize_t
268
+ aligned_words(const char *buff, Py_ssize_t start, Py_ssize_t stop,
269
+ Py_ssize_t *p1, Py_ssize_t *p2)
270
+ {
271
+ Py_ssize_t n = 0;
272
+
273
+ assert(0 <= start && start <= stop);
274
+
275
+ if (start < stop) {
276
+ /* non-empty buffer cannot be NULL */
277
+ start += to_aligned(buff + start);
278
+ if (start > stop)
279
+ start = stop;
280
+ n = (stop - start) / 8;
281
+ }
282
+ *p1 = start;
283
+ *p2 = start + 8 * n;
284
+ return n;
285
+ }
286
+
261
287
  /* population count of n words starting at uint64_t pointer w */
262
288
  static inline Py_ssize_t
263
289
  popcnt_words(uint64_t *w, Py_ssize_t n)
@@ -265,7 +291,6 @@ popcnt_words(uint64_t *w, Py_ssize_t n)
265
291
  Py_ssize_t cnt = 0;
266
292
 
267
293
  assert(n >= 0);
268
- assert(n == 0 || ((uintptr_t) w) % 8 == 0);
269
294
  while (n--)
270
295
  cnt += popcnt_64(*w++);
271
296
  return cnt;
@@ -33,6 +33,7 @@ import weakref
33
33
 
34
34
  is_emscripten = platform.machine() in ('wasm32', 'wasm64')
35
35
  is_pypy = platform.python_implementation() == 'PyPy'
36
+ is_sparc = platform.machine().lower().startswith("sparc")
36
37
 
37
38
 
38
39
  from bitarray import (bitarray, frozenbitarray, bits2bytes, decodetree,
@@ -73,6 +74,26 @@ class Util:
73
74
  for a in self.randombitarrays():
74
75
  yield a.tolist()
75
76
 
77
+ def unaligned_buffers(self, size=32, endian=None):
78
+ for remainder in range(8):
79
+ owner = bytearray(size + 8)
80
+ base = bitarray(buffer=owner).buffer_info().address
81
+ offset = (remainder - base) % 8
82
+ buffer = memoryview(owner)[offset:offset + size]
83
+ a = bitarray(buffer=buffer, endian=endian)
84
+
85
+ self.assertEqual(len(a), 8 * size)
86
+ self.assertEqual(a.buffer_info().address % 8, remainder)
87
+ yield a
88
+
89
+ self.assertEqual(owner[:offset], bytearray(offset))
90
+ self.assertEqual(owner[offset + size:], bytearray(8 - offset))
91
+
92
+ @staticmethod
93
+ def is_unaligned(a):
94
+ info = a.buffer_info()
95
+ return info.address % 8 != 0
96
+
76
97
  @staticmethod
77
98
  def opposite_endian(endian):
78
99
  t = {'little': 'big',
@@ -1735,6 +1756,20 @@ class MiscTests(unittest.TestCase, Util):
1735
1756
  for i in range(len(a)):
1736
1757
  self.assertEqual(a[i], b[i + 1234])
1737
1758
 
1759
+ def test_subclass_inplace(self) -> None:
1760
+ class SubBitarray(bitarray):
1761
+ pass
1762
+
1763
+ a: SubBitarray = SubBitarray('101')
1764
+ a += bitarray()
1765
+ a *= 1
1766
+ a &= bitarray('110')
1767
+ a |= bitarray('001')
1768
+ a ^= bitarray('010')
1769
+ a <<= 1
1770
+ a >>= 1
1771
+ self.assertIs(type(a), SubBitarray)
1772
+
1738
1773
  @unittest.skipIf(is_pypy, "skip test on PyPy")
1739
1774
  def test_overflow(self):
1740
1775
  a = bitarray(1)
@@ -1773,8 +1808,7 @@ class MiscTests(unittest.TestCase, Util):
1773
1808
  self.assertIsInstance(a, abc.MutableSequence)
1774
1809
  if sys.version_info[:2] >= (3, 12):
1775
1810
  self.assertIsInstance(a, abc.Buffer)
1776
- if sys.platform != "win32":
1777
- self.assertFalse(isinstance(a, abc.Hashable))
1811
+ self.assertFalse(isinstance(a, abc.Hashable))
1778
1812
 
1779
1813
  # ---------------------------------------------------------------------------
1780
1814
 
@@ -2080,14 +2114,16 @@ class SpecialMethodTests(unittest.TestCase, Util):
2080
2114
 
2081
2115
  # -------------------------- Sequence methods tests -------------------------
2082
2116
 
2083
- class SequenceTests(unittest.TestCase, Util):
2117
+ class SequenceTests(unittest.TestCase):
2084
2118
 
2085
2119
  def test_len(self):
2086
2120
  for n in range(100):
2087
2121
  a = bitarray(n)
2088
2122
  self.assertEqual(len(a), n)
2089
2123
 
2090
- def test_concat(self):
2124
+ class ConcatTests(unittest.TestCase, Util):
2125
+
2126
+ def test_simple(self):
2091
2127
  a = bitarray('001')
2092
2128
  b = a + bitarray('110')
2093
2129
  self.assertEQUAL(b, bitarray('001110'))
@@ -2103,7 +2139,7 @@ class SequenceTests(unittest.TestCase, Util):
2103
2139
  self.assertRaises(TypeError, a.__add__, 42)
2104
2140
  self.assertRaises(ValueError, a.__add__, b'1101')
2105
2141
 
2106
- def test_concat_random(self):
2142
+ def test_random(self):
2107
2143
  for a in self.randombitarrays():
2108
2144
  aa = a.copy()
2109
2145
  for b in self.randombitarrays():
@@ -2116,7 +2152,7 @@ class SequenceTests(unittest.TestCase, Util):
2116
2152
  self.assertEQUAL(a, aa)
2117
2153
  self.assertEQUAL(b, bb)
2118
2154
 
2119
- def test_concat_frozenbitarray(self):
2155
+ def test_frozenbitarray(self):
2120
2156
  a = frozenbitarray('001')
2121
2157
  b = a + bitarray('011')
2122
2158
  self.assertIs(type(b), frozenbitarray)
@@ -2124,7 +2160,7 @@ class SequenceTests(unittest.TestCase, Util):
2124
2160
  self.assertTrue(b.readonly)
2125
2161
  self.check_obj(b)
2126
2162
 
2127
- def test_inplace_concat(self):
2163
+ def test_inplace(self):
2128
2164
  a = bitarray('001')
2129
2165
  a += bitarray('110')
2130
2166
  self.assertEqual(a, bitarray('001110'))
@@ -2151,7 +2187,9 @@ class SequenceTests(unittest.TestCase, Util):
2151
2187
  self.assertIs(c, d)
2152
2188
  self.check_obj(d)
2153
2189
 
2154
- def test_repeat_explicit(self):
2190
+ class RepeatTests(unittest.TestCase, Util):
2191
+
2192
+ def test_explicit(self):
2155
2193
  for m, s, r in [
2156
2194
  ( 0, '', ''),
2157
2195
  ( 0, '1001111', ''),
@@ -2168,14 +2206,14 @@ class SequenceTests(unittest.TestCase, Util):
2168
2206
  c *= m
2169
2207
  self.assertEqual(c, bitarray(r))
2170
2208
 
2171
- def test_repeat_wrong_args(self):
2209
+ def test_wrong_args(self):
2172
2210
  a = bitarray()
2173
2211
  self.assertRaises(TypeError, a.__mul__, None)
2174
2212
  self.assertRaises(TypeError, a.__mul__, 2.0)
2175
2213
  self.assertRaises(TypeError, a.__imul__, None)
2176
2214
  self.assertRaises(TypeError, a.__imul__, 3.0)
2177
2215
 
2178
- def test_repeat_frozenbitarray(self):
2216
+ def test_frozenbitarray(self):
2179
2217
  a = frozenbitarray('001')
2180
2218
  b = 3 * a
2181
2219
  self.assertIs(type(b), frozenbitarray)
@@ -2183,7 +2221,7 @@ class SequenceTests(unittest.TestCase, Util):
2183
2221
  self.assertTrue(b.readonly)
2184
2222
  self.check_obj(b)
2185
2223
 
2186
- def test_repeat_random(self):
2224
+ def test_random(self):
2187
2225
  for a in self.randombitarrays():
2188
2226
  b = a.copy()
2189
2227
  for m in list(range(-3, 5)) + [randint(5, 200)]:
@@ -2200,7 +2238,9 @@ class SequenceTests(unittest.TestCase, Util):
2200
2238
 
2201
2239
  self.assertEQUAL(a, b)
2202
2240
 
2203
- def test_contains_simple(self):
2241
+ class ContainsTests(unittest.TestCase):
2242
+
2243
+ def test_simple(self):
2204
2244
  a = bitarray()
2205
2245
  self.assertFalse(0 in a)
2206
2246
  self.assertFalse(1 in a)
@@ -2215,7 +2255,7 @@ class SequenceTests(unittest.TestCase, Util):
2215
2255
  self.assertTrue(0 in a)
2216
2256
  self.assertTrue(1 in a)
2217
2257
 
2218
- def test_contains_errors(self):
2258
+ def test_errors(self):
2219
2259
  a = bitarray()
2220
2260
  self.assertFalse(a.__contains__(1))
2221
2261
  a.append(1)
@@ -2227,7 +2267,7 @@ class SequenceTests(unittest.TestCase, Util):
2227
2267
  self.assertRaises(ValueError, a.__contains__, 2)
2228
2268
  self.assertRaises(ValueError, a.__contains__, -1)
2229
2269
 
2230
- def test_contains_range(self):
2270
+ def test_range(self):
2231
2271
  for n in range(2, 50):
2232
2272
  a = zeros(n)
2233
2273
  self.assertTrue(0 in a)
@@ -2242,7 +2282,7 @@ class SequenceTests(unittest.TestCase, Util):
2242
2282
  self.assertTrue(True in a)
2243
2283
  self.assertTrue(False in a)
2244
2284
 
2245
- def test_contains_explicit(self):
2285
+ def test_explicit(self):
2246
2286
  a = bitarray('011010000001')
2247
2287
  for s, r in [('', True), # every bitarray contains an empty one
2248
2288
  ('1', True), ('11', True), ('111', False),
@@ -2431,6 +2471,18 @@ class NumberTests(unittest.TestCase, Util):
2431
2471
  b[16:] &= bitarray("1111 0000 1111 0000", 'big')
2432
2472
  self.assertEqual(a, bytearray([0xfc, 0x88, 0x10, 0xe0]))
2433
2473
 
2474
+ def test_unaligned_buffers(self):
2475
+ for a in self.unaligned_buffers():
2476
+ for b in self.unaligned_buffers():
2477
+ a.setall(0)
2478
+ a[187] = a[193] = b[193] = b[194] = 1
2479
+ a &= b
2480
+ self.assertEqual(list(a.search(1)), [193])
2481
+ a ^= b
2482
+ self.assertEqual(list(a.search(1)), [194])
2483
+ a |= b
2484
+ self.assertEqual(list(a.search(1)), [193, 194])
2485
+
2434
2486
 
2435
2487
  class ShiftTests(unittest.TestCase, Util):
2436
2488
 
@@ -2593,6 +2645,18 @@ class ShiftTests(unittest.TestCase, Util):
2593
2645
  b >>= 8
2594
2646
  self.assertEqual(a, bytearray([0x00, 0xfc, 0x88, 0x10]))
2595
2647
 
2648
+ def test_unaligned_buffers(self):
2649
+ for endian in ENDIANS:
2650
+ for a in self.unaligned_buffers(endian=endian):
2651
+ a[0] = a[193] = a[-1] = 1
2652
+ expected = self.shift(a, 3, 'left')
2653
+ a <<= 3
2654
+ self.assertEQUAL(a, expected)
2655
+
2656
+ expected = self.shift(a, 5, 'right')
2657
+ a >>= 5
2658
+ self.assertEQUAL(a, expected)
2659
+
2596
2660
  # -------------------------------- .extend() ----------------------------
2597
2661
 
2598
2662
  class ExtendTests(unittest.TestCase, Util):
@@ -3115,6 +3179,22 @@ class InvertTests(unittest.TestCase, Util):
3115
3179
  b.invert()
3116
3180
  self.assertEqual(a, bytearray([255, 254, 253, 252, 223, 0]))
3117
3181
 
3182
+ def test_unaligned_buffers(self):
3183
+ for n in range(20):
3184
+ for endian in ENDIANS:
3185
+ for a in self.unaligned_buffers(size=n, endian=endian):
3186
+ a.invert()
3187
+ self.assertEqual(a, ones(len(a)))
3188
+ if n == 0:
3189
+ continue
3190
+ i = randrange(len(a))
3191
+ a.invert(i)
3192
+ self.assertEqual(a.find(0), i)
3193
+ self.assertEqual(a.endian, endian)
3194
+ a.invert(slice(0, i))
3195
+ a.invert(slice(i + 1, None))
3196
+ self.assertEqual(a, zeros(8 * n))
3197
+
3118
3198
  class SortTests(unittest.TestCase, Util):
3119
3199
 
3120
3200
  def test_simple(self):
@@ -3813,13 +3893,15 @@ class CountTests(unittest.TestCase, Util):
3813
3893
  self.assertEqual(a.count(v, s.start, s.stop, s.step),
3814
3894
  a[s].count(v))
3815
3895
 
3816
- def test_offset_buffer(self):
3817
- # this tests if words are aligned in popcnt_words()
3818
- N = 1 << 16
3819
- for i in range(20):
3820
- a = urandom_2(N, 'little')
3821
- b = bitarray(buffer=memoryview(a)[i:], endian='little')
3822
- self.assertEqual(b.count(), a.count(1, 8 * i))
3896
+ def test_unaligned_buffers(self):
3897
+ for n in range(20):
3898
+ for endian in ENDIANS:
3899
+ for a in self.unaligned_buffers(size=n, endian=endian):
3900
+ b = urandom_2(8 * n)
3901
+ expected = b.count()
3902
+ a[:] = b
3903
+ self.assertEqual(a.count(), expected)
3904
+ self.assertEqual(a.endian, endian)
3823
3905
 
3824
3906
  # -------------------------- .find() and .index() ---------------------------
3825
3907
 
@@ -3993,6 +4075,23 @@ class IndexTests(unittest.TestCase, Util):
3993
4075
  self.assertEqual(a.find(v, i, j, 0), ref_l)
3994
4076
  self.assertEqual(a.find(v, i, j, 1), ref_r)
3995
4077
 
4078
+ def test_unaligned_buffers(self):
4079
+ for n in range(20):
4080
+ for endian in ENDIANS:
4081
+ for a in self.unaligned_buffers(size=n, endian=endian):
4082
+ self.assertEqual(a.find(1), -1)
4083
+ if n == 0:
4084
+ continue
4085
+ i = randrange(len(a))
4086
+ a[i] = 1
4087
+ self.assertEqual(a.find(1), i)
4088
+ self.assertEqual(a.find(1, right=1), i)
4089
+ a.setall(1)
4090
+ self.assertEqual(a.find(0), -1)
4091
+ a[i] = 0
4092
+ self.assertEqual(a.find(0), i)
4093
+ self.assertEqual(a.find(0, right=1), i)
4094
+
3996
4095
  # ----------------------------- .search() -----------------------------------
3997
4096
 
3998
4097
  class SearchTests(unittest.TestCase, Util):
@@ -5623,12 +5722,17 @@ class FrozenbitarrayTests(unittest.TestCase, Util):
5623
5722
 
5624
5723
  self.check_obj(a)
5625
5724
 
5626
- def test_copy(self):
5725
+ def test_copy(self) -> None:
5627
5726
  a = frozenbitarray('101')
5628
- # not only .copy() creates new frozenbitarray which are read-only
5629
- for b in [a, a.copy(), 3 * a, 5 * a, a & bitarray('110'),
5630
- a >> 2, ~a, a + bitarray(8*'1'),
5631
- a[:], a[::2], a[[0, 1]], a[bitarray('011')]]:
5727
+ # Check the stub return types as well as the runtime types.
5728
+ copies: list[frozenbitarray] = [
5729
+ a.copy(), 3 * a, a * 5, a & bitarray('110'),
5730
+ a | bitarray('110'), a ^ bitarray('110'),
5731
+ a & frozenbitarray('110'), a | frozenbitarray('110'),
5732
+ a ^ frozenbitarray('110'), a >> 2, a << 1, ~a,
5733
+ a + bitarray(8*'1'), a[:], a[::2], a[[0, 1]], a[bitarray('011')],
5734
+ ]
5735
+ for b in copies:
5632
5736
  self.assertIs(type(b), frozenbitarray)
5633
5737
  self.assertTrue(b.readonly)
5634
5738
  self.check_obj(b)
@@ -5748,14 +5852,15 @@ def show_info(verbosity=1):
5748
5852
 
5749
5853
  if verbosity >= 2:
5750
5854
  print('bitarray version: %s' % __version__)
5855
+ print('platform.machine(): %s' % platform.machine())
5856
+ print('platform.platform(): %s' % platform.platform())
5857
+ print('sys.platform: %s' % sys.platform)
5751
5858
  print('sys.version: %s' % sys.version)
5752
5859
  if os.name == "posix":
5753
5860
  print('sys.abiflags: %r' % sys.abiflags)
5754
5861
  if sys.version_info[:2] >= (3, 14):
5755
5862
  print('sys._is_gil_enabled(): %s' % sys._is_gil_enabled())
5756
5863
  print('pointer size: %d bit' % (8 * PTRSIZE))
5757
- print('sizeof(size_t): %d' % sysinfo("size_t"))
5758
- print('sizeof(bitarrayobject): %d' % sysinfo("bitarrayobject"))
5759
5864
  print('HAVE_BUILTIN_BSWAP64: %d' % sysinfo("HAVE_BUILTIN_BSWAP64"))
5760
5865
  print('default bit-endianness: %s' % get_default_endian())
5761
5866
  print('machine byte-order: %s' % sys.byteorder)
@@ -24,7 +24,8 @@ from collections import Counter
24
24
 
25
25
  from bitarray import (bitarray, frozenbitarray, decodetree, bits2bytes,
26
26
  get_default_endian)
27
- from bitarray.test_bitarray import Util, is_pypy, urandom_2, ENDIANS, PTRSIZE
27
+ from bitarray.test_bitarray import (Util, is_pypy, is_sparc, urandom_2,
28
+ ENDIANS, PTRSIZE)
28
29
 
29
30
  from bitarray.util import (
30
31
  zeros, ones, urandom, random_k, random_p, pprint, strip, count_n,
@@ -687,6 +688,15 @@ class CountN_Tests(unittest.TestCase, Util):
687
688
  i = count_n(a, n, v)
688
689
  self.check_result(a, n, i, v)
689
690
 
691
+ def test_unaligned_buffers(self):
692
+ # size=1000 means that 8000 bits > BLOCK_BITS 4096
693
+ for a in self.unaligned_buffers(size=1000):
694
+ a[-1] = 1
695
+ if is_sparc and self.is_unaligned(a):
696
+ self.assertRaises(NotImplementedError, count_n, a, 1)
697
+ else:
698
+ self.assertEqual(count_n(a, 1), len(a))
699
+
690
700
  # ---------------------------------------------------------------------------
691
701
 
692
702
  class BitwiseCountTests(unittest.TestCase, Util):
@@ -771,6 +781,16 @@ class BitwiseCountTests(unittest.TestCase, Util):
771
781
  self.assertEqual(all(a ^ b), count_xor(a, b) == n)
772
782
  self.assertEqual(all(a ^ b), a == ~b)
773
783
 
784
+ def test_unaligned_buffers(self):
785
+ for f in count_and, count_or, count_xor, any_and, subset:
786
+ for a in self.unaligned_buffers():
787
+ for b in self.unaligned_buffers():
788
+ if is_sparc and (self.is_unaligned(a) or
789
+ self.is_unaligned(b)):
790
+ self.assertRaises(NotImplementedError, f, a, b)
791
+ else:
792
+ self.assertEqual(f(a, b), f == subset)
793
+
774
794
  # --------------------------- any_and() -----------------------------------
775
795
 
776
796
  class BitwiseAnyTests(unittest.TestCase, Util):
@@ -905,7 +925,7 @@ class SubsetTests(unittest.TestCase, Util):
905
925
 
906
926
  # ------------------------- correspond_all() ------------------------------
907
927
 
908
- class CorrespondAllTests(unittest.TestCase):
928
+ class CorrespondAllTests(unittest.TestCase, Util):
909
929
 
910
930
  def test_basic(self):
911
931
  a = frozenbitarray('0101')
@@ -941,10 +961,20 @@ class CorrespondAllTests(unittest.TestCase):
941
961
  self.assertEqual(res[1] + res[2], count_xor(a, b))
942
962
  self.assertEqual(sum(res), n)
943
963
 
964
+ def test_unaligned_buffers(self):
965
+ for a in self.unaligned_buffers():
966
+ for b in self.unaligned_buffers():
967
+ if is_sparc and (self.is_unaligned(a) or
968
+ self.is_unaligned(b)):
969
+ self.assertRaises(NotImplementedError,
970
+ correspond_all, a, b)
971
+ else:
972
+ self.assertEqual(correspond_all(a, b), (len(a), 0, 0, 0))
973
+
944
974
  # ----------------------------- byteswap() --------------------------------
945
975
 
946
976
  @unittest.skipIf(is_pypy, "skip test on PyPy")
947
- class ByteSwapTests(unittest.TestCase):
977
+ class ByteSwapTests(unittest.TestCase, Util):
948
978
 
949
979
  def test_basic_bytearray(self):
950
980
  a = bytearray(b"ABCD")
@@ -1039,9 +1069,15 @@ class ByteSwapTests(unittest.TestCase):
1039
1069
  a.bytereverse()
1040
1070
  self.assertEqual(a, b[::-1])
1041
1071
 
1072
+ def test_unaligned_buffers(self):
1073
+ for n in 2, 4, 8, 16:
1074
+ for a in self.unaligned_buffers():
1075
+ byteswap(a, n)
1076
+ self.assertEqual(a, zeros(256))
1077
+
1042
1078
  # ------------------------------ parity() ---------------------------------
1043
1079
 
1044
- class ParityTests(unittest.TestCase):
1080
+ class ParityTests(unittest.TestCase, Util):
1045
1081
 
1046
1082
  def test_explicit(self):
1047
1083
  for s, res in [('', 0), ('1', 1), ('0010011', 1), ('10100110', 0)]:
@@ -1072,6 +1108,27 @@ class ParityTests(unittest.TestCase):
1072
1108
  self.assertRaises(TypeError, parity)
1073
1109
  self.assertRaises(TypeError, parity, bitarray("110"), 1)
1074
1110
 
1111
+ def test_unaligned_buffers(self):
1112
+ for a in self.unaligned_buffers():
1113
+ self.assertEqual(parity(a), 0)
1114
+ a[0] = 1
1115
+ self.assertEqual(parity(a), 1)
1116
+ a[193] = 1
1117
+ self.assertEqual(parity(a), 0)
1118
+ a[-1] = 1
1119
+ self.assertEqual(parity(a), 1)
1120
+
1121
+ def test_unaligned_buffers_random(self):
1122
+ for n in range(20):
1123
+ for endian in ENDIANS:
1124
+ for a in self.unaligned_buffers(size=n, endian=endian):
1125
+ b = urandom(8 * n)
1126
+ expected = b.count() % 2
1127
+ a[:] = b
1128
+ self.assertEqual(len(a), 8 * n)
1129
+ self.assertEqual(a.endian, endian)
1130
+ self.assertEqual(parity(a), expected)
1131
+
1075
1132
  # ---------------------------- sum_indices() ------------------------------
1076
1133
 
1077
1134
  class SumIndicesTests(unittest.TestCase):
@@ -2143,6 +2200,12 @@ class SC_Tests(unittest.TestCase, Util, SC_Util):
2143
2200
  a &= urandom(n, endian)
2144
2201
  self.round_trip(a)
2145
2202
 
2203
+ def test_unaligned_buffers(self):
2204
+ for a in self.unaligned_buffers(size=1000):
2205
+ if is_sparc and self.is_unaligned(a):
2206
+ self.assertRaises(NotImplementedError, sc_encode, a)
2207
+ else:
2208
+ self.assertEqual(sc_decode(sc_encode(a)), a)
2146
2209
 
2147
2210
  # ------------------------ variable length codec ------------------------
2148
2211
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitarray
3
- Version: 3.12.2
3
+ Version: 3.12.3
4
4
  Summary: efficient arrays of booleans -- C extension
5
5
  Home-page: https://github.com/ilanschnell/bitarray
6
6
  Author: Ilan Schnell
@@ -57,6 +57,7 @@ Key features
57
57
  their in-place versions ``&=``, ``|=``, ``^=``, ``<<=``, ``>>=``).
58
58
  * `Free-threading support <https://github.com/ilanschnell/bitarray/blob/master/doc/free_threading.rst>`__
59
59
  (for GIL-disabled CPython 3.14 and later)
60
+ * Written to `support all platforms supported by CPython and PyPy <https://github.com/ilanschnell/bitarray/blob/master/doc/platforms.rst>`__.
60
61
  * Fast methods for encoding and decoding variable-length
61
62
  `prefix codes <https://github.com/ilanschnell/bitarray/blob/master/doc/prefix_codes.rst>`__.
62
63
  * Bitarray objects support the `buffer protocol <https://github.com/ilanschnell/bitarray/blob/master/doc/buffer.rst>`__
@@ -92,7 +93,7 @@ versions. Which means you can simply:
92
93
 
93
94
  .. code-block:: shell-session
94
95
 
95
- $ pip install bitarray
96
+ $ python -m pip install bitarray
96
97
 
97
98
  Once you have installed the package, you may want to test it:
98
99
 
@@ -101,26 +102,27 @@ Once you have installed the package, you may want to test it:
101
102
  $ python -c 'import bitarray; bitarray.test()'
102
103
  bitarray is installed in: /Users/ilan/bitarray/bitarray
103
104
  sys.prefix: /Users/ilan/miniforge
104
- bitarray version: 3.12.2
105
+ bitarray version: 3.12.3
106
+ platform.machine(): arm64
107
+ platform.platform(): macOS-14.3-arm64-arm-64bit-Mach-O
108
+ sys.platform: darwin
105
109
  sys.version: 3.15.0 (main, Oct 9 2026) [Clang 21.1.8]
106
110
  sys.abiflags: ''
107
111
  sys._is_gil_enabled(): True
108
112
  pointer size: 64 bit
109
- sizeof(size_t): 8
110
- sizeof(bitarrayobject): 80
111
113
  HAVE_BUILTIN_BSWAP64: 1
112
114
  default bit-endianness: big
113
115
  machine byte-order: little
114
116
  Py_GIL_DISABLED: 0
115
117
  Py_DEBUG: 0
116
118
  DEBUG: 0
117
- .....................................s...................................
118
- ..........s.....................................s........................
119
+ .........................................................................
120
+ ................................................s........................
119
121
  ......s.........................................................
120
122
  ----------------------------------------------------------------------
121
- Ran 711 tests in 0.161s
123
+ Ran 723 tests in 0.163s
122
124
 
123
- OK (skipped=4)
125
+ OK (skipped=2)
124
126
 
125
127
  The ``test()`` function is part of the API. It will return
126
128
  a ``unittest.runner.TextTestResult`` object, such that one can verify that
@@ -312,7 +314,7 @@ and can therefore be used as a dictionary key:
312
314
  Reference
313
315
  =========
314
316
 
315
- bitarray version: 3.12.2 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
317
+ bitarray version: 3.12.3 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
316
318
 
317
319
  In the following, ``item`` and ``value`` are usually a single bit -
318
320
  an integer 0 or 1.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes