Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/bitstring/bitarray_.py: 17%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import copy
4import numbers
5import re
6from collections import abc
7from typing import Union, List, Iterable, Any, Optional
8from bitstring import utils
9from bitstring.exceptions import CreationError, Error
10from bitstring.bits import Bits, BitsType, TBits
12import bitstring.dtypes
13common_helpers = bitstring.bitstore_common_helpers
15MutableBitStore = bitstring.bitstore.MutableBitStore
18class BitArray(Bits):
19 """A container holding a mutable sequence of bits.
21 Subclass of the immutable Bits class. Inherits all of its
22 methods (except __hash__) and adds mutating methods.
24 Mutating methods:
26 append() -- Append a bitstring.
27 byteswap() -- Change byte endianness in-place.
28 clear() -- Remove all bits from the bitstring.
29 insert() -- Insert a bitstring.
30 invert() -- Flip bit(s) between one and zero.
31 overwrite() -- Overwrite a section with a new bitstring.
32 prepend() -- Prepend a bitstring.
33 replace() -- Replace occurrences of one bitstring with another.
34 reverse() -- Reverse bits in-place.
35 rol() -- Rotate bits to the left.
36 ror() -- Rotate bits to the right.
37 set() -- Set bit(s) to 1 or 0.
39 Methods inherited from Bits:
41 all() -- Check if all specified bits are set to 1 or 0.
42 any() -- Check if any of specified bits are set to 1 or 0.
43 copy() -- Return a copy of the bitstring.
44 count() -- Count the number of bits set to 1 or 0.
45 cut() -- Create generator of constant sized chunks.
46 endswith() -- Return whether the bitstring ends with a sub-string.
47 find() -- Find a sub-bitstring in the current bitstring.
48 findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
49 fromstring() -- Create a bitstring from a formatted string.
50 join() -- Join bitstrings together using current bitstring.
51 pp() -- Pretty print the bitstring.
52 rfind() -- Seek backwards to find a sub-bitstring.
53 split() -- Create generator of chunks split by a delimiter.
54 startswith() -- Return whether the bitstring starts with a sub-bitstring.
55 tobitarray() -- Return bitstring as a bitarray from the bitarray package.
56 tobytes() -- Return bitstring as bytes, padding if needed.
57 tofile() -- Write bitstring to file, padding if needed.
58 unpack() -- Interpret bits using format string.
60 Special methods:
62 Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^=
63 in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^.
65 Properties:
67 [GENERATED_PROPERTY_DESCRIPTIONS]
69 len -- Length of the bitstring in bits.
71 """
73 __slots__ = ()
75 # As BitArray objects are mutable, we shouldn't allow them to be hashed.
76 __hash__: None = None
78 def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None,
79 offset: Optional[int] = None, **kwargs) -> None:
80 """Either specify an 'auto' initialiser:
81 A string of comma separated tokens, an integer, a file object,
82 a bytearray, a boolean iterable or another bitstring.
84 Or initialise via **kwargs with one (and only one) of:
85 bin -- binary string representation, e.g. '0b001010'.
86 hex -- hexadecimal string representation, e.g. '0x2ef'
87 oct -- octal string representation, e.g. '0o777'.
88 bytes -- raw data as a bytes object, for example read from a binary file.
89 int -- a signed integer.
90 uint -- an unsigned integer.
91 float / floatbe -- a big-endian floating point number.
92 bool -- a boolean (True or False).
93 se -- a signed exponential-Golomb code.
94 ue -- an unsigned exponential-Golomb code.
95 sie -- a signed interleaved exponential-Golomb code.
96 uie -- an unsigned interleaved exponential-Golomb code.
97 floatle -- a little-endian floating point number.
98 floatne -- a native-endian floating point number.
99 bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number.
100 bfloatle -- a little-endian bfloat format 16-bit floating point number.
101 bfloatne -- a native-endian bfloat format 16-bit floating point number.
102 intbe -- a signed big-endian whole byte integer.
103 intle -- a signed little-endian whole byte integer.
104 intne -- a signed native-endian whole byte integer.
105 uintbe -- an unsigned big-endian whole byte integer.
106 uintle -- an unsigned little-endian whole byte integer.
107 uintne -- an unsigned native-endian whole byte integer.
108 filename -- the path of a file which will be opened in binary read-only mode.
110 Other keyword arguments:
111 length -- length of the bitstring in bits, if needed and appropriate.
112 It must be supplied for all integer and float initialisers.
113 offset -- bit offset to the data. These offset bits are
114 ignored and this is intended for use when
115 initialising using 'bytes' or 'filename'.
117 """
118 pass
120 def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None,
121 offset: Optional[int] = None, **kwargs) -> TBits:
122 x = super(Bits, cls).__new__(cls)
123 if auto is None and not kwargs:
124 # No initialiser so fill with zero bits up to length
125 if length is not None:
126 x._bitstore = MutableBitStore.from_zeros(length)
127 else:
128 x._bitstore = MutableBitStore()
129 return x
130 x._initialise(auto, length, offset, immutable=False, **kwargs)
131 return x
133 @classmethod
134 def fromstring(cls: TBits, s: str, /) -> TBits:
135 """Create a new bitstring from a formatted string."""
136 x = super().__new__(cls)
137 b = common_helpers.str_to_bitstore(s)
138 x._bitstore = b._mutable_copy()
139 return x
141 def copy(self: TBits) -> TBits:
142 """Return a copy of the bitstring."""
143 return self.__copy__()
145 def _invert_all(self) -> None:
146 """Invert every bit."""
147 self._bitstore.invert()
149 def __setattr__(self, attribute, value) -> None:
150 try:
151 # First try the ordinary attribute setter
152 super().__setattr__(attribute, value)
153 except AttributeError:
154 dtype = bitstring.dtypes.Dtype(attribute)
155 x = object.__new__(Bits)
156 if (set_fn := dtype.set_fn) is None:
157 raise AttributeError(f"Cannot set attribute '{attribute}' as it does not have a set_fn.")
158 set_fn(x, value)
159 if len(x) != dtype.bitlength:
160 raise CreationError(f"Can't initialise with value of length {len(x)} bits, "
161 f"as attribute has length of {dtype.bitlength} bits.")
162 self._bitstore = x._bitstore
163 return
165 def __iadd__(self, bs: BitsType) -> BitArray:
166 """Append bs to current bitstring. Return self.
168 bs -- the bitstring to append.
170 """
171 self._append(bs)
172 return self
174 def __copy__(self) -> BitArray:
175 """Return a new copy of the BitArray."""
176 s_copy = BitArray()
177 s_copy._bitstore = self._bitstore._mutable_copy()
178 return s_copy
180 def _setitem_int(self, key: int, value: Union[BitsType, int]) -> None:
181 if isinstance(value, numbers.Integral):
182 if value == 0:
183 self._bitstore[key] = 0
184 return
185 if value in (1, -1):
186 self._bitstore[key] = 1
187 return
188 raise ValueError(f"Cannot set a single bit with integer {value}.")
189 try:
190 value = self._create_from_bitstype(value)
191 except TypeError:
192 raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.")
193 positive_key = key + len(self) if key < 0 else key
194 if positive_key < 0 or positive_key >= len(self._bitstore):
195 raise IndexError(f"Bit position {key} out of range.")
196 self._bitstore[positive_key: positive_key + 1] = value._bitstore
198 def _setitem_slice(self, key: slice, value: BitsType) -> None:
199 if isinstance(value, numbers.Integral):
200 value = int(value)
201 if key.step not in [None, -1, 1]:
202 if value in [0, 1]:
203 self.set(value, range(*key.indices(len(self))))
204 return
205 else:
206 raise ValueError("Can't assign an integer except 0 or 1 to a slice with a step value.")
207 # To find the length we first get the slice
208 s = self._bitstore.getslice(key.start, key.stop)
209 length = len(s)
210 # Now create an int of the correct length
211 if value >= 0:
212 value = self.__class__(uint=value, length=length)
213 else:
214 value = self.__class__(int=value, length=length)
215 else:
216 try:
217 value = self._create_from_bitstype(value)
218 except TypeError:
219 raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.")
220 self._bitstore.__setitem__(key, value._bitstore)
222 def __setitem__(self, key: Union[slice, int], value: BitsType) -> None:
223 if isinstance(key, numbers.Integral):
224 self._setitem_int(int(key), value)
225 else:
226 self._setitem_slice(key, value)
228 def __delitem__(self, key: Union[slice, int]) -> None:
229 """Delete item or range.
231 >>> a = BitArray('0x001122')
232 >>> del a[8:16]
233 >>> print a
234 0x0022
236 """
237 self._bitstore.__delitem__(key)
238 return
240 def __ilshift__(self: TBits, n: int) -> TBits:
241 """Shift bits by n to the left in place. Return self.
243 n -- the number of bits to shift. Must be >= 0.
245 """
246 if n < 0:
247 raise ValueError("Cannot shift by a negative amount.")
248 if not len(self):
249 raise ValueError("Cannot shift an empty bitstring.")
250 if not n:
251 return self
252 n = min(n, len(self))
253 return self._ilshift(n)
255 def __irshift__(self: TBits, n: int) -> TBits:
256 """Shift bits by n to the right in place. Return self.
258 n -- the number of bits to shift. Must be >= 0.
260 """
261 if n < 0:
262 raise ValueError("Cannot shift by a negative amount.")
263 if not len(self):
264 raise ValueError("Cannot shift an empty bitstring.")
265 if not n:
266 return self
267 n = min(n, len(self))
268 return self._irshift(n)
270 def __imul__(self: TBits, n: int) -> TBits:
271 """Concatenate n copies of self in place. Return self.
273 Called for expressions of the form 'a *= 3'.
274 n -- The number of concatenations. Must be >= 0.
276 """
277 if n < 0:
278 raise ValueError("Cannot multiply by a negative integer.")
279 return self._imul(n)
281 def __ior__(self: TBits, bs: BitsType) -> TBits:
282 bs = self._create_from_bitstype(bs)
283 self._bitstore |= bs._bitstore
284 return self
286 def __iand__(self: TBits, bs: BitsType) -> TBits:
287 bs = self._create_from_bitstype(bs)
288 self._bitstore &= bs._bitstore
289 return self
291 def __ixor__(self: TBits, bs: BitsType) -> TBits:
292 bs = self._create_from_bitstype(bs)
293 self._bitstore ^= bs._bitstore
294 return self
296 def _replace(self, old: Bits, new: Bits, start: int, end: int, count: int, bytealigned: Optional[bool]) -> int:
297 if bytealigned is None:
298 bytealigned = bitstring.options.bytealigned
299 # First find all the places where we want to do the replacements
300 starting_points: List[int] = []
301 for x in self.findall(old, start, end, bytealigned=bytealigned):
302 if not starting_points:
303 starting_points.append(x)
304 elif x >= starting_points[-1] + len(old):
305 # Can only replace here if it hasn't already been replaced!
306 starting_points.append(x)
307 if count != 0 and len(starting_points) == count:
308 break
309 if not starting_points:
310 return 0
311 replacement_list = [self._bitstore.getslice(0, starting_points[0])]
312 for i in range(len(starting_points) - 1):
313 replacement_list.append(new._bitstore)
314 replacement_list.append(
315 self._bitstore.getslice(starting_points[i] + len(old), starting_points[i + 1]))
316 # Final replacement
317 replacement_list.append(new._bitstore)
318 replacement_list.append(self._bitstore.getslice(starting_points[-1] + len(old), None))
319 if bitstring.options.lsb0:
320 # Addition of bitarray is always on the right, so assemble from other end
321 replacement_list.reverse()
322 self._bitstore.clear()
323 for r in replacement_list:
324 self._bitstore += r
325 return len(starting_points)
327 def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None,
328 count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int:
329 """Replace all occurrences of old with new in place.
331 Returns number of replacements made.
333 old -- The bitstring to replace.
334 new -- The replacement bitstring.
335 start -- Any occurrences that start before this will not be replaced.
336 Defaults to 0.
337 end -- Any occurrences that finish after this will not be replaced.
338 Defaults to len(self).
339 count -- The maximum number of replacements to make. Defaults to
340 replace all occurrences.
341 bytealigned -- If True replacements will only be made on byte
342 boundaries.
344 Raises ValueError if old is empty or if start or end are
345 out of range.
347 """
348 if count == 0:
349 return 0
350 old = self._create_from_bitstype(old)
351 new = self._create_from_bitstype(new)
352 if len(old) == 0:
353 raise ValueError("Empty bitstring cannot be replaced.")
354 start, end = self._validate_slice(start, end)
356 if new is self:
357 # Prevent self assignment woes
358 new = copy.copy(self)
359 return self._replace(old, new, start, end, 0 if count is None else count, bytealigned)
361 def insert(self, bs: BitsType, pos: int) -> None:
362 """Insert bs at bit position pos.
364 bs -- The bitstring to insert.
365 pos -- The bit position to insert at.
367 Raises ValueError if pos < 0 or pos > len(self).
369 """
370 bs = self._create_from_bitstype(bs)
371 if len(bs) == 0:
372 return
373 if bs is self:
374 bs = self._copy()
375 if pos < 0:
376 pos += len(self)
377 if not 0 <= pos <= len(self):
378 raise ValueError("Invalid insert position.")
379 self._insert(bs, pos)
381 def overwrite(self, bs: BitsType, pos: int) -> None:
382 """Overwrite with bs at bit position pos.
384 bs -- The bitstring to overwrite with.
385 pos -- The bit position to begin overwriting from.
387 Raises ValueError if pos < 0 or pos > len(self).
389 """
390 bs = self._create_from_bitstype(bs)
391 if len(bs) == 0:
392 return
393 if pos < 0:
394 pos += len(self)
395 if pos < 0 or pos > len(self):
396 raise ValueError("Overwrite starts outside boundary of bitstring.")
397 self._overwrite(bs, pos)
399 def append(self, bs: BitsType) -> None:
400 """Append a bitstring to the current bitstring.
402 bs -- The bitstring to append.
404 """
405 self._append(bs)
407 def prepend(self, bs: BitsType) -> None:
408 """Prepend a bitstring to the current bitstring.
410 bs -- The bitstring to prepend.
412 """
413 self._prepend(bs)
415 def _append_msb0(self, bs: BitsType) -> None:
416 self._addright(self._create_from_bitstype(bs))
418 def _append_lsb0(self, bs: BitsType) -> None:
419 bs = self._create_from_bitstype(bs)
420 self._addleft(bs)
422 def reverse(self, start: Optional[int] = None, end: Optional[int] = None) -> None:
423 """Reverse bits in-place.
425 start -- Position of first bit to reverse. Defaults to 0.
426 end -- One past the position of the last bit to reverse.
427 Defaults to len(self).
429 Using on an empty bitstring will have no effect.
431 Raises ValueError if start < 0, end > len(self) or end < start.
433 """
434 start, end = self._validate_slice(start, end)
435 if start == 0 and end == len(self):
436 self._bitstore.reverse()
437 return
438 s = self._slice(start, end)
439 s._bitstore.reverse()
440 self[start:end] = s
442 def set(self, value: Any, pos: Optional[Union[int, Iterable[int]]] = None) -> None:
443 """Set one or many bits to 1 or 0.
445 value -- If bool(value) is True bits are set to 1, otherwise they are set to 0.
446 pos -- Either a single bit position or an iterable of bit positions.
447 Negative numbers are treated in the same way as slice indices.
448 Defaults to the entire bitstring.
450 Raises IndexError if pos < -len(self) or pos >= len(self).
452 """
453 if pos is None:
454 # Set all bits to either 1 or 0
455 self._bitstore.__setitem__(slice(None), bool(value))
456 return
457 if not isinstance(pos, abc.Iterable):
458 pos = (pos,)
459 v = 1 if value else 0
460 if isinstance(pos, range):
461 self._bitstore.__setitem__(slice(pos.start, pos.stop, pos.step), v)
462 return
463 for p in pos:
464 self._bitstore[p] = v
466 def invert(self, pos: Optional[Union[Iterable[int], int]] = None) -> None:
467 """Invert one or many bits from 0 to 1 or vice versa.
469 pos -- Either a single bit position or an iterable of bit positions.
470 Negative numbers are treated in the same way as slice indices.
472 Raises IndexError if pos < -len(self) or pos >= len(self).
474 """
475 if pos is None:
476 self._invert_all()
477 return
478 if not isinstance(pos, abc.Iterable):
479 pos = (pos,)
480 length = len(self)
482 for p in pos:
483 if p < 0:
484 p += length
485 if not 0 <= p < length:
486 raise IndexError(f"Bit position {p} out of range.")
487 self._invert(p)
489 def ror(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None:
490 """Rotate bits to the right in-place.
492 bits -- The number of bits to rotate by.
493 start -- Start of slice to rotate. Defaults to 0.
494 end -- End of slice to rotate. Defaults to len(self).
496 Raises ValueError if bits < 0.
498 """
499 if not len(self):
500 raise Error("Cannot rotate an empty bitstring.")
501 if bits < 0:
502 raise ValueError("Cannot rotate by negative amount.")
503 self._ror(bits, start, end)
505 def _ror_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None:
506 start, end = self._validate_slice(start, end) # the _slice deals with msb0/lsb0
507 bits %= (end - start)
508 if not bits:
509 return
510 rhs = self._slice(end - bits, end)
511 self._delete(bits, end - bits)
512 self._insert(rhs, start)
514 def rol(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None:
515 """Rotate bits to the left in-place.
517 bits -- The number of bits to rotate by.
518 start -- Start of slice to rotate. Defaults to 0.
519 end -- End of slice to rotate. Defaults to len(self).
521 Raises ValueError if bits < 0.
523 """
524 if not len(self):
525 raise Error("Cannot rotate an empty bitstring.")
526 if bits < 0:
527 raise ValueError("Cannot rotate by negative amount.")
528 self._rol(bits, start, end)
530 def _rol_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None):
531 start, end = self._validate_slice(start, end)
532 bits %= (end - start)
533 if bits == 0:
534 return
535 lhs = self._slice(start, start + bits)
536 self._delete(bits, start)
537 self._insert(lhs, end - bits)
539 def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]] = None, start: Optional[int] = None,
540 end: Optional[int] = None, repeat: bool = True) -> int:
541 """Change the endianness in-place. Return number of repeats of fmt done.
543 fmt -- A compact structure string, an integer number of bytes or
544 an iterable of integers. Defaults to 0, which byte reverses the
545 whole bitstring.
546 start -- Start bit position, defaults to 0.
547 end -- End bit position, defaults to len(self).
548 repeat -- If True (the default) the byte swapping pattern is repeated
549 as much as possible.
551 """
552 start_v, end_v = self._validate_slice(start, end)
553 if fmt is None or fmt == 0:
554 # reverse all of the whole bytes.
555 bytesizes = [(end_v - start_v) // 8]
556 elif isinstance(fmt, numbers.Integral):
557 if fmt < 0:
558 raise ValueError(f"Improper byte length {fmt}.")
559 bytesizes = [fmt]
560 elif isinstance(fmt, str):
561 if not (m := utils.BYTESWAP_STRUCT_PACK_RE.match(fmt)):
562 raise ValueError(f"Cannot parse format string {fmt}.")
563 # Split the format string into a list of 'q', '4h' etc.
564 formatlist = re.findall(utils.STRUCT_SPLIT_RE, m.group('fmt'))
565 # Now deal with multiplicative factors, 4h -> hhhh etc.
566 bytesizes = []
567 for f in formatlist:
568 if len(f) == 1:
569 bytesizes.append(utils.PACK_CODE_SIZE[f])
570 else:
571 bytesizes.extend([utils.PACK_CODE_SIZE[f[-1]]] * int(f[:-1]))
572 elif isinstance(fmt, abc.Iterable):
573 bytesizes = fmt
574 for bytesize in bytesizes:
575 if not isinstance(bytesize, numbers.Integral) or bytesize < 0:
576 raise ValueError(f"Improper byte length {bytesize}.")
577 else:
578 raise TypeError("Format must be an integer, string or iterable.")
580 repeats = 0
581 totalbitsize: int = 8 * sum(bytesizes)
582 if not totalbitsize:
583 return 0
584 if repeat:
585 # Try to repeat up to the end of the bitstring.
586 finalbit = end_v
587 else:
588 # Just try one (set of) byteswap(s).
589 finalbit = start_v + totalbitsize
590 for patternend in range(start_v + totalbitsize, finalbit + 1, totalbitsize):
591 bytestart = patternend - totalbitsize
592 for bytesize in bytesizes:
593 byteend = bytestart + bytesize * 8
594 self._reversebytes(bytestart, byteend)
595 bytestart += bytesize * 8
596 repeats += 1
597 return repeats
599 def clear(self) -> None:
600 """Remove all bits, reset to zero length."""
601 self._clear()