diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65692a3e..b1217a3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,100 +160,6 @@ jobs: ls -la - name: Run pytest - env: - BITSTRING_USE_RUST_CORE: ${{ matrix.rust.env_var }} - run: | - python -m pytest tests/ --benchmark-disable - - test-rust: - name: Test ${{ matrix.os.name }} ${{ matrix.python.name }} (Rust core) - if: always() - needs: - - test-standard - runs-on: ${{ matrix.os.runs-on }} - strategy: - fail-fast: false - matrix: - os: - - name: 🐧 - runs-on: ubuntu-latest - - name: 🍎 - runs-on: macos-15 - - name: 🪟 - runs-on: windows-latest - python: - - name: CPython 3.8 - major_dot_minor: '3.8' - action: '3.8' - - name: CPython 3.9 - major_dot_minor: '3.9' - action: '3.9' - - name: CPython 3.10 - major_dot_minor: '3.10' - action: '3.10' - - name: CPython 3.11 - major_dot_minor: '3.11' - action: '3.11' - - name: CPython 3.12 - major_dot_minor: '3.12' - action: '3.12' - - name: CPython 3.13 - major_dot_minor: '3.13' - action: '3.13' - - name: CPython 3.14 - major_dot_minor: '3.14' - action: '3.14' - exclude: - - os: - name: "🍎" - runs-on: macos-15 - python: - name: "CPython 3.8" - major_dot_minor: '3.8' - action: '3.8' - - os: - name: "🍎" - runs-on: macos-15 - python: - name: "CPython 3.9" - major_dot_minor: '3.9' - action: '3.9' - - os: - name: "🍎" - runs-on: macos-15 - python: - name: "CPython 3.10" - major_dot_minor: '3.10' - action: '3.10' - steps: - - uses: actions/checkout@v4 - with: - path: repo - - - name: Download package files - uses: actions/download-artifact@v4 - with: - name: packages - path: dist - - - uses: actions/setup-python@v5 - with: - python-version: ${{ fromJSON(format('["{0}", "{1}"]', format('{0}.0-alpha - {0}.X', matrix.python.action), matrix.python.action))[startsWith(matrix.python.action, 'pypy')] }} - architecture: x64 - - - name: Setup environment - run: | - python --version --version - # make sure we test the installed code - cp -R repo/tests/ tests/ - python -m pip install -r tests/requirements.txt - python -m pip install ./dist/*.whl - # show the directory contents for diagnostics - ls -la - - - name: Run pytest - env: - BITSTRING_USE_RUST_CORE: '1' run: | python -m pytest tests/ --benchmark-disable @@ -268,7 +174,6 @@ jobs: # a merge. - build - test-standard - - test-rust steps: - name: Require all successes uses: re-actors/alls-green@v1.2.2 diff --git a/bitstring/__init__.py b/bitstring/__init__.py index 438539b0..8fd5b92d 100644 --- a/bitstring/__init__.py +++ b/bitstring/__init__.py @@ -55,26 +55,13 @@ THE SOFTWARE. """ -__version__ = "4.4.0" +__version__ = "5.0.0" __author__ = "Scott Griffiths" import sys -import os -import importlib - -# New ability to use tibs for core operations instead of bitarray. -# Tibs is written in Rust and is still in beta. Use the environment variable -# BITSTRING_USE_RUST_CORE=1 before importing the module to turn it on. -_env_core = os.getenv('BITSTRING_USE_RUST_CORE', '').strip().lower() -_USE_RUST_CORE = _env_core in ('1', 'true', 'yes', 'on') -if _USE_RUST_CORE: - bitstore = importlib.import_module('bitstring.bitstore_tibs') - bitstore_helpers = importlib.import_module('bitstring.bitstore_tibs_helpers') -else: - bitstore = importlib.import_module('bitstring.bitstore_bitarray') - bitstore_helpers = importlib.import_module('bitstring.bitstore_bitarray_helpers') -bitstore_common_helpers = importlib.import_module('bitstring.bitstore_common_helpers') +import bitstring.bitstore_tibs as bitstore +# import bitstring.bitstore_helpers as bitstore_helpers from .bits import Bits from .bitstring_options import Options diff --git a/bitstring/bitarray_.py b/bitstring/bitarray_.py index a372b447..ff6d6a18 100644 --- a/bitstring/bitarray_.py +++ b/bitstring/bitarray_.py @@ -10,7 +10,7 @@ from bitstring.bits import Bits, BitsType, TBits import bitstring.dtypes -common_helpers = bitstring.bitstore_common_helpers +import bitstring.bitstore_helpers as helpers MutableBitStore = bitstring.bitstore.MutableBitStore @@ -52,7 +52,6 @@ class BitArray(Bits): rfind() -- Seek backwards to find a sub-bitstring. split() -- Create generator of chunks split by a delimiter. startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobitarray() -- Return bitstring as a bitarray from the bitarray package. tobytes() -- Return bitstring as bytes, padding if needed. tofile() -- Write bitstring to file, padding if needed. unpack() -- Interpret bits using format string. @@ -122,10 +121,7 @@ def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, le x = super(Bits, cls).__new__(cls) if auto is None and not kwargs: # No initialiser so fill with zero bits up to length - if length is not None: - x._bitstore = MutableBitStore.from_zeros(length) - else: - x._bitstore = MutableBitStore() + x._bitstore = MutableBitStore.from_zeros(length if length is not None else 0) return x x._initialise(auto, length, offset, immutable=False, **kwargs) return x @@ -134,7 +130,7 @@ def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, le def fromstring(cls: TBits, s: str, /) -> TBits: """Create a new bitstring from a formatted string.""" x = super().__new__(cls) - b = common_helpers.str_to_bitstore(s) + b = helpers.str_to_bitstore(s) x._bitstore = b._mutable_copy() return x @@ -280,17 +276,17 @@ def __imul__(self: TBits, n: int) -> TBits: def __ior__(self: TBits, bs: BitsType) -> TBits: bs = self._create_from_bitstype(bs) - self._bitstore |= bs._bitstore + self._bitstore.tibs |= bs._bitstore.tibs return self def __iand__(self: TBits, bs: BitsType) -> TBits: bs = self._create_from_bitstype(bs) - self._bitstore &= bs._bitstore + self._bitstore.tibs &= bs._bitstore.tibs return self def __ixor__(self: TBits, bs: BitsType) -> TBits: bs = self._create_from_bitstype(bs) - self._bitstore ^= bs._bitstore + self._bitstore.tibs ^= bs._bitstore.tibs return self def _replace(self, old: Bits, new: Bits, start: int, end: int, count: int, bytealigned: Optional[bool]) -> int: diff --git a/bitstring/bits.py b/bitstring/bits.py index 5ef096a1..5daf3fa2 100644 --- a/bitstring/bits.py +++ b/bitstring/bits.py @@ -10,7 +10,6 @@ from collections import abc import functools from typing import Tuple, Union, List, Iterable, Any, Optional, BinaryIO, TextIO, overload, Iterator, Type, TypeVar -import bitarray import bitstring from bitstring import utils from bitstring.dtypes import Dtype, dtype_register @@ -18,14 +17,15 @@ from bitstring.mxfp import e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, e5m2mxfp_saturate_fmt from bitstring.bitstring_options import Colour +import bitstring.bitstore_helpers as helpers + ConstBitStore = bitstring.bitstore.ConstBitStore MutableBitStore = bitstring.bitstore.MutableBitStore -helpers = bitstring.bitstore_helpers -common_helpers = bitstring.bitstore_common_helpers + # Things that can be converted to Bits when a Bits type is needed -BitsType = Union['Bits', str, Iterable[Any], bool, BinaryIO, bytearray, bytes, memoryview, bitarray.bitarray] +BitsType = Union['Bits', str, Iterable[Any], bool, BinaryIO, bytearray, bytes, memoryview] TBits = TypeVar("TBits", bound='Bits') @@ -54,7 +54,6 @@ class Bits: rfind() -- Seek backwards to find a sub-bitstring. split() -- Create generator of chunks split by a delimiter. startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobitarray() -- Return bitstring as a bitarray from the bitarray package. tobytes() -- Return bitstring as bytes, padding if needed. tofile() -- Write bitstring to file, padding if needed. unpack() -- Interpret bits using format string. @@ -119,10 +118,7 @@ def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, le x = super().__new__(cls) if auto is None and not kwargs: # No initialiser so fill with zero bits up to length - if length is not None: - x._bitstore = ConstBitStore.from_zeros(length) - else: - x._bitstore = ConstBitStore() + x._bitstore = ConstBitStore.from_zeros(length if length is not None else 0) return x x._initialise(auto, length, offset, immutable=True, **kwargs) return x @@ -154,8 +150,6 @@ def _initialise(self, auto: Any, /, length: Optional[int], offset: Optional[int] self._setbytes_with_truncation(v, length, offset) elif k == 'filename': self._setfile(v, length, offset) - elif k == 'bitarray': - self._setbitarray(v, length, offset) elif k == 'auto': raise bitstring.CreationError( f"The 'auto' parameter should not be given explicitly - just use the first positional argument. " @@ -381,7 +375,7 @@ def __mul__(self: TBits, n: int, /) -> TBits: def _imul(self: TBits, n: int, /) -> TBits: """Concatenate n copies of self in place. Return self.""" - self._bitstore.__imul__(n) + self._bitstore.tibs *= n return self def __rmul__(self: TBits, n: int, /) -> TBits: @@ -500,7 +494,7 @@ def _clear(self) -> None: def _setauto_no_length_or_offset(self, s: BitsType, /) -> None: """Set bitstring from a bitstring, file, bool, array, iterable or string.""" if isinstance(s, str): - self._bitstore = common_helpers.str_to_bitstore(s) + self._bitstore = helpers.str_to_bitstore(s) elif isinstance(s, Bits): self._bitstore = s._bitstore.copy() elif isinstance(s, (bytes, bytearray, memoryview)): @@ -509,8 +503,6 @@ def _setauto_no_length_or_offset(self, s: BitsType, /) -> None: self._bitstore = ConstBitStore.from_bytes(s.getvalue()) elif isinstance(s, io.BufferedReader): self._setfile(s.name) - elif isinstance(s, bitarray.bitarray): - self._bitstore = ConstBitStore(s) elif isinstance(s, array.array): self._bitstore = ConstBitStore.from_bytes(s.tobytes()) elif isinstance(s, abc.Iterable): @@ -549,7 +541,7 @@ def _setauto(self, s: BitsType, length: Optional[int], offset: Optional[int], /) return if isinstance(s, (str, Bits, bytes, bytearray, memoryview, io.BytesIO, io.BufferedReader, - bitarray.bitarray, array.array, abc.Iterable)): + array.array, abc.Iterable)): raise bitstring.CreationError(f"Cannot initialise bitstring from type '{type(s)}' when using explicit lengths or offsets.") raise TypeError(f"Cannot initialise bitstring from type '{type(s)}'.") @@ -574,49 +566,36 @@ def _setfile(self, filename: str, length: Optional[int] = None, offset: Optional if len(self) != length: raise bitstring.CreationError(f"Can't use a length of {length} bits and an offset of {offset} bits as file length is only {len(temp)} bits.") - def _setbitarray(self, ba: bitarray.bitarray, length: Optional[int], offset: Optional[int]) -> None: - if offset is None: - offset = 0 - if offset > len(ba): - raise bitstring.CreationError(f"Offset of {offset} too large for bitarray of length {len(ba)}.") - if length is None: - self._bitstore = ConstBitStore(ba[offset:]) - else: - if offset + length > len(ba): - raise bitstring.CreationError( - f"Offset of {offset} and length of {length} too large for bitarray of length {len(ba)}.") - self._bitstore = ConstBitStore(ba[offset: offset + length]) - def _setbits(self, bs: BitsType, length: None = None) -> None: bs = Bits._create_from_bitstype(bs) self._bitstore = bs._bitstore def _setp3binary(self, f: float) -> None: - self._bitstore = common_helpers.p3binary2bitstore(f) + self._bitstore = helpers.p3binary2bitstore(f) def _setp4binary(self, f: float) -> None: - self._bitstore = common_helpers.p4binary2bitstore(f) + self._bitstore = helpers.p4binary2bitstore(f) def _sete4m3mxfp(self, f: float) -> None: - self._bitstore = common_helpers.e4m3mxfp2bitstore(f) + self._bitstore = helpers.e4m3mxfp2bitstore(f) def _sete5m2mxfp(self, f: float) -> None: - self._bitstore = common_helpers.e5m2mxfp2bitstore(f) + self._bitstore = helpers.e5m2mxfp2bitstore(f) def _sete3m2mxfp(self, f: float) -> None: - self._bitstore = common_helpers.e3m2mxfp2bitstore(f) + self._bitstore = helpers.e3m2mxfp2bitstore(f) def _sete2m3mxfp(self, f: float) -> None: - self._bitstore = common_helpers.e2m3mxfp2bitstore(f) + self._bitstore = helpers.e2m3mxfp2bitstore(f) def _sete2m1mxfp(self, f: float) -> None: - self._bitstore = common_helpers.e2m1mxfp2bitstore(f) + self._bitstore = helpers.e2m1mxfp2bitstore(f) def _sete8m0mxfp(self, f: float) -> None: - self._bitstore = common_helpers.e8m0mxfp2bitstore(f) + self._bitstore = helpers.e8m0mxfp2bitstore(f) def _setmxint(self, f: float) -> None: - self._bitstore = common_helpers.mxint2bitstore(f) + self._bitstore = helpers.mxint2bitstore(f) def _setbytes(self, data: Union[bytearray, bytes, List], length:None = None) -> None: """Set the data from a bytes or bytearray object.""" @@ -635,7 +614,7 @@ def _setbytes_with_truncation(self, data: Union[bytearray, bytes], length: Optio else: if length + offset > len(data) * 8: raise bitstring.CreationError(f"Not enough data present. Need {length + offset} bits, have {len(data) * 8}.") - self._bitstore = ConstBitStore.from_bytes(data).getslice_msb0(offset, offset + length) + self._bitstore = MutableBitStore.from_bytes(data).getslice_msb0(offset, offset + length) def _getbytes(self) -> bytes: """Return the data as an ordinary bytes object.""" @@ -807,7 +786,7 @@ def _getbfloatbe(self) -> float: def _setbfloatbe(self, f: Union[float, str], length: Optional[int] = None) -> None: if length is not None and length != 16: raise bitstring.CreationError(f"bfloats must be length 16, received a length of {length} bits.") - self._bitstore = common_helpers.bfloat2bitstore(f, True) + self._bitstore = helpers.bfloat2bitstore(f, True) def _getbfloatle(self) -> float: zero_padded = Bits(16) + self @@ -816,7 +795,7 @@ def _getbfloatle(self) -> float: def _setbfloatle(self, f: Union[float, str], length: Optional[int] = None) -> None: if length is not None and length != 16: raise bitstring.CreationError(f"bfloats must be length 16, received a length of {length} bits.") - self._bitstore = common_helpers.bfloat2bitstore(f, False) + self._bitstore = helpers.bfloat2bitstore(f, False) def _setue(self, i: int) -> None: """Initialise bitstring with unsigned exponential-Golomb code for integer i. @@ -826,7 +805,7 @@ def _setue(self, i: int) -> None: """ if bitstring.options.lsb0: raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") - self._bitstore = common_helpers.ue2bitstore(i) + self._bitstore = helpers.ue2bitstore(i) def _readue(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as unsigned exponential-Golomb code. @@ -883,7 +862,7 @@ def _setse(self, i: int) -> None: """Initialise bitstring with signed exponential-Golomb code for integer i.""" if bitstring.options.lsb0: raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") - self._bitstore = common_helpers.se2bitstore(i) + self._bitstore = helpers.se2bitstore(i) def _readse(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as a signed exponential-Golomb code. @@ -906,7 +885,7 @@ def _setuie(self, i: int) -> None: """ if bitstring.options.lsb0: raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") - self._bitstore = common_helpers.uie2bitstore(i) + self._bitstore = helpers.uie2bitstore(i) def _readuie(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as unsigned interleaved exponential-Golomb code. @@ -933,7 +912,7 @@ def _setsie(self, i: int, ) -> None: """Initialise bitstring with signed interleaved exponential-Golomb code for integer i.""" if bitstring.options.lsb0: raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") - self._bitstore = common_helpers.sie2bitstore(i) + self._bitstore = helpers.sie2bitstore(i) def _readsie(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as a signed interleaved exponential-Golomb code. @@ -1044,7 +1023,7 @@ def _readtoken(self, name: str, pos: int, length: Optional[int]) -> Tuple[Union[ def _addright(self, bs: Bits, /) -> None: """Add a bitstring to the RHS of the current bitstring.""" - self._bitstore += bs._bitstore + self._bitstore.tibs += bs._bitstore.tibs def _addleft(self, bs: Bits, /) -> None: """Prepend a bitstring to the current bitstring.""" @@ -1424,7 +1403,7 @@ def join(self: TBits, sequence: Iterable[Any]) -> TBits: sequence -- A sequence of bitstrings. """ - bs = MutableBitStore() + bs = MutableBitStore.from_zeros(0) if len(self) == 0: # Optimised version that doesn't need to add self between every item for item in sequence: @@ -1451,10 +1430,6 @@ def tobytes(self) -> bytes: """ return self._bitstore.to_bytes() - def tobitarray(self) -> bitarray.bitarray: - """Convert the bitstring to a bitarray object.""" - return self._bitstore.tobitarray() - def tofile(self, f: BinaryIO) -> None: """Write the bitstring to a file object, padding with zero bits if needed. @@ -1534,9 +1509,7 @@ def count(self, value: Any) -> int: 7 """ - # count the number of 1s (from which it's easy to work out the 0s). - count = self._bitstore.count(1) - return count if value else len(self) - count + return self._bitstore.tibs.count(bool(value)) @staticmethod def _format_bits(bits: Bits, bits_per_group: int, sep: str, dtype: Dtype, @@ -1734,7 +1707,7 @@ def copy(self: TBits) -> TBits: def fromstring(cls: TBits, s: str, /) -> TBits: """Create a new bitstring from a formatted string.""" x = super().__new__(cls) - x._bitstore = common_helpers.str_to_bitstore(s) + x._bitstore = helpers.str_to_bitstore(s) return x len = length = property(_getlength, doc="The length of the bitstring in bits. Read only.") diff --git a/bitstring/bitstore_bitarray.py b/bitstring/bitstore_bitarray.py deleted file mode 100644 index 80e9d1a4..00000000 --- a/bitstring/bitstore_bitarray.py +++ /dev/null @@ -1,328 +0,0 @@ -from __future__ import annotations - -import bitarray -import bitarray.util -from bitstring.exceptions import CreationError -from typing import Union, Iterable, Optional, overload, Iterator, Any -from bitstring.helpers import offset_slice_indices_lsb0 - -if bitarray.__version__.startswith("2."): - raise ImportError(f"bitstring version 4.3 requires bitarray version 3 or higher. Found version {bitarray.__version__}.") - - -class _BitStore: - """A light wrapper around bitarray that does the LSB0 stuff""" - - __slots__ = ('_bitarray', 'modified_length', 'immutable') - - def __init__(self, initializer: Union[bitarray.bitarray, None] = None, - immutable: bool = False) -> None: - if isinstance(initializer, str): - assert False - self._bitarray = bitarray.bitarray(initializer) - self.immutable = immutable - self.modified_length = None - - @classmethod - def from_zeros(cls, i: int) -> _BitStore: - x = super().__new__(cls) - x._bitarray = bitarray.bitarray(i) - x.immutable = False - x.modified_length = None - return x - - - @classmethod - def from_bin(cls, s: str) -> _BitStore: - x = super().__new__(cls) - x._bitarray = bitarray.bitarray(s) - x.immutable = False - x.modified_length = None - return x - - @classmethod - def from_bytes(cls, b: Union[bytes, bytearray, memoryview], /) -> _BitStore: - x = super().__new__(cls) - x._bitarray = bitarray.bitarray() - x._bitarray.frombytes(b) - x.immutable = False - x.modified_length = None - return x - - @classmethod - def frombuffer(cls, buffer, /, length: Optional[int] = None) -> _BitStore: - x = super().__new__(cls) - x._bitarray = bitarray.bitarray(buffer=buffer) - x.immutable = True - x.modified_length = length - # Here 'modified' means it shouldn't be changed further, so setting, deleting etc. are disallowed. - if x.modified_length is not None: - if x.modified_length < 0: - raise CreationError("Can't create bitstring with a negative length.") - if x.modified_length > len(x._bitarray): - raise CreationError( - f"Can't create bitstring with a length of {x.modified_length} from {len(x._bitarray)} bits of data.") - return x - - @classmethod - def join(cls, bitstores: Iterable[_BitStore], /) -> _BitStore: - x = super().__new__(cls) - x._bitarray = bitarray.bitarray() - for b in bitstores: - x._bitarray += b._bitarray - x.immutable = False - x.modified_length = None - return x - - @staticmethod - def using_rust_core() -> bool: - return False - - def tobitarray(self) -> bitarray.bitarray: - if self.modified_length is not None: - return self.getslice(0, len(self))._bitarray - return self._bitarray - - def to_bytes(self) -> bytes: - if self.modified_length is not None: - return self._bitarray[:self.modified_length].tobytes() - return self._bitarray.tobytes() - - def to_u(self) -> int: - if self.modified_length is not None: - return bitarray.util.ba2int(self._bitarray[:self.modified_length], signed=False) - return bitarray.util.ba2int(self._bitarray, signed=False) - - def to_i(self) -> int: - if self.modified_length is not None: - return bitarray.util.ba2int(self._bitarray[:self.modified_length], signed=True) - return bitarray.util.ba2int(self._bitarray, signed=True) - - def to_hex(self) -> str: - if self.modified_length is not None: - return bitarray.util.ba2hex(self._bitarray[:self.modified_length]) - return bitarray.util.ba2hex(self._bitarray) - - def to_bin(self) -> str: - if self.modified_length is not None: - return self._bitarray[:self.modified_length].to01() - return self._bitarray.to01() - - def to_oct(self) -> str: - if self.modified_length is not None: - return bitarray.util.ba2base(8, self._bitarray[:self.modified_length]) - return bitarray.util.ba2base(8, self._bitarray) - - def __imul__(self, n: int, /) -> _BitStore: - self._bitarray *= n - return self - - def __ilshift__(self, n: int, /) -> None: - self._bitarray <<= n - - def __irshift__(self, n: int, /) -> None: - self._bitarray >>= n - - def __iadd__(self, other: _BitStore, /) -> _BitStore: - self._bitarray += other._bitarray - return self - - def __add__(self, other: _BitStore, /) -> _BitStore: - bs = self._mutable_copy() - bs += other - return bs - - def __eq__(self, other: Any, /) -> bool: - return self._bitarray == other._bitarray - - def __and__(self, other: _BitStore, /) -> _BitStore: - return _BitStore(self._bitarray & other._bitarray) - - def __or__(self, other: _BitStore, /) -> _BitStore: - return _BitStore(self._bitarray | other._bitarray) - - def __xor__(self, other: _BitStore, /) -> _BitStore: - return _BitStore(self._bitarray ^ other._bitarray) - - def __iand__(self, other: _BitStore, /) -> _BitStore: - self._bitarray &= other._bitarray - return self - - def __ior__(self, other: _BitStore, /) -> _BitStore: - self._bitarray |= other._bitarray - return self - - def __ixor__(self, other: _BitStore, /) -> _BitStore: - self._bitarray ^= other._bitarray - return self - - def __invert__(self) -> _BitStore: - return _BitStore(~self._bitarray) - - def find(self, bs: _BitStore, start: int, end: int, bytealigned: bool = False) -> int | None: - if not bytealigned: - x = self._bitarray.find(bs._bitarray, start, end) - return None if x == -1 else x - try: - return next(self.findall_msb0(bs, start, end, bytealigned)) - except StopIteration: - return None - - def rfind(self, bs: _BitStore, start: int, end: int, bytealigned: bool = False) -> int | None: - if not bytealigned: - x = self._bitarray.find(bs._bitarray, start, end, right=True) - return None if x == -1 else x - try: - return next(self.rfindall_msb0(bs, start, end, bytealigned)) - except StopIteration: - return None - - def findall_msb0(self, bs: _BitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: - if bytealigned is True and len(bs) % 8 == 0: - # Special case, looking for whole bytes on whole byte boundaries - bytes_ = bs.to_bytes() - # Round up start byte to next byte, and round end byte down. - # We're only looking for whole bytes, so can ignore bits at either end. - start_byte = (start + 7) // 8 - end_byte = end // 8 - b = self._bitarray[start_byte * 8: end_byte * 8].tobytes() - byte_pos = 0 - bytes_to_search = end_byte - start_byte - while byte_pos < bytes_to_search: - byte_pos = b.find(bytes_, byte_pos) - if byte_pos == -1: - break - yield (byte_pos + start_byte) * 8 - byte_pos = byte_pos + 1 - return - # General case - i = self._bitarray.search(bs._bitarray, start, end) - if not bytealigned: - for p in i: - yield p - else: - for p in i: - if (p % 8) == 0: - yield p - - def rfindall_msb0(self, bs: _BitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: - i = self._bitarray.search(bs._bitarray, start, end, right=True) - if not bytealigned: - for p in i: - yield p - else: - for p in i: - if (p % 8) == 0: - yield p - - def count(self, value, /) -> int: - return self._bitarray.count(value) - - def clear(self) -> None: - self._bitarray.clear() - - def reverse(self) -> None: - self._bitarray.reverse() - - def __iter__(self) -> Iterable[bool]: - for i in range(len(self)): - yield self.getindex(i) - - def _mutable_copy(self) -> _BitStore: - """Always creates a copy, even if instance is immutable.""" - return _BitStore(self._bitarray, immutable=False) - - def as_immutable(self) -> _BitStore: - return _BitStore(self._bitarray, immutable=True) - - def copy(self) -> _BitStore: - return self if self.immutable else self._mutable_copy() - - def __getitem__(self, item: Union[int, slice], /) -> Union[int, _BitStore]: - # Use getindex or getslice instead - raise NotImplementedError - - def getindex_msb0(self, index: int, /) -> bool: - return bool(self._bitarray.__getitem__(index)) - - def getslice_withstep_msb0(self, key: slice, /) -> _BitStore: - if self.modified_length is not None: - key = slice(*key.indices(self.modified_length)) - return _BitStore(self._bitarray.__getitem__(key)) - - def getslice_withstep_lsb0(self, key: slice, /) -> _BitStore: - key = offset_slice_indices_lsb0(key, len(self)) - return _BitStore(self._bitarray.__getitem__(key)) - - def getslice_msb0(self, start: Optional[int], stop: Optional[int], /) -> _BitStore: - if self.modified_length is not None: - key = slice(*slice(start, stop, None).indices(self.modified_length)) - start = key.start - stop = key.stop - return _BitStore(self._bitarray[start:stop]) - - def getslice_lsb0(self, start: Optional[int], stop: Optional[int], /) -> _BitStore: - s = offset_slice_indices_lsb0(slice(start, stop, None), len(self)) - return _BitStore(self._bitarray[s.start:s.stop]) - - def getindex_lsb0(self, index: int, /) -> bool: - return bool(self._bitarray.__getitem__(-index - 1)) - - @overload - def setitem_lsb0(self, key: int, value: int, /) -> None: - ... - - @overload - def setitem_lsb0(self, key: slice, value: _BitStore, /) -> None: - ... - - def setitem_lsb0(self, key: Union[int, slice], value: Union[int, _BitStore], /) -> None: - if isinstance(key, slice): - new_slice = offset_slice_indices_lsb0(key, len(self)) - self._bitarray.__setitem__(new_slice, value._bitarray) - else: - self._bitarray.__setitem__(-key - 1, value) - - def delitem_lsb0(self, key: Union[int, slice], /) -> None: - if isinstance(key, slice): - new_slice = offset_slice_indices_lsb0(key, len(self)) - self._bitarray.__delitem__(new_slice) - else: - self._bitarray.__delitem__(-key - 1) - - def invert_msb0(self, index: Optional[int] = None, /) -> None: - if index is not None: - self._bitarray.invert(index) - else: - self._bitarray.invert() - - def invert_lsb0(self, index: Optional[int] = None, /) -> None: - if index is not None: - self._bitarray.invert(-index - 1) - else: - self._bitarray.invert() - - def extend_left(self, other: _BitStore, /) -> None: - self._bitarray = other._bitarray + self._bitarray - - def any(self) -> bool: - return self._bitarray.any() - - def all(self) -> bool: - return self._bitarray.all() - - def __len__(self) -> int: - return self.modified_length if self.modified_length is not None else len(self._bitarray) - - def setitem_msb0(self, key, value, /): - if isinstance(value, _BitStore): - self._bitarray.__setitem__(key, value._bitarray) - else: - self._bitarray.__setitem__(key, value) - - def delitem_msb0(self, key, /): - self._bitarray.__delitem__(key) - - -ConstBitStore = _BitStore -MutableBitStore = _BitStore \ No newline at end of file diff --git a/bitstring/bitstore_bitarray_helpers.py b/bitstring/bitstore_bitarray_helpers.py deleted file mode 100644 index 336957df..00000000 --- a/bitstring/bitstore_bitarray_helpers.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -import struct -import math -import functools -from typing import Union, Optional, Dict, Callable -import bitarray -import bitstring -from bitstring.fp8 import p4binary_fmt, p3binary_fmt -from bitstring.mxfp import (e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, - e5m2mxfp_saturate_fmt, e4m3mxfp_overflow_fmt, e5m2mxfp_overflow_fmt) -from bitstring.helpers import tidy_input_string - -ConstBitStore = bitstring.bitstore.ConstBitStore -MutableBitStore = bitstring.bitstore.MutableBitStore - - -def bin2bitstore(binstring: str) -> ConstBitStore: - binstring = tidy_input_string(binstring) - binstring = binstring.replace('0b', '') - try: - return ConstBitStore.from_bin(binstring) - except ValueError: - raise bitstring.CreationError(f"Invalid character in bin initialiser {binstring}.") - - -def hex2bitstore(hexstring: str) -> ConstBitStore: - hexstring = tidy_input_string(hexstring) - hexstring = hexstring.replace('0x', '') - try: - ba = bitarray.util.hex2ba(hexstring) - except ValueError: - raise bitstring.CreationError("Invalid symbol in hex initialiser.") - return ConstBitStore(ba) - - -def oct2bitstore(octstring: str) -> ConstBitStore: - octstring = tidy_input_string(octstring) - octstring = octstring.replace('0o', '') - try: - ba = bitarray.util.base2ba(8, octstring) - except ValueError: - raise bitstring.CreationError("Invalid symbol in oct initialiser.") - return ConstBitStore(ba) - - -def int2bitstore(i: int, length: int, signed: bool) -> ConstBitStore: - i = int(i) - try: - x = ConstBitStore(bitarray.util.int2ba(i, length=length, endian='big', signed=signed)) - except OverflowError as e: - if signed: - if i >= (1 << (length - 1)) or i < -(1 << (length - 1)): - raise bitstring.CreationError(f"{i} is too large a signed integer for a bitstring of length {length}. " - f"The allowed range is [{-(1 << (length - 1))}, {(1 << (length - 1)) - 1}].") - else: - if i >= (1 << length): - raise bitstring.CreationError(f"{i} is too large an unsigned integer for a bitstring of length {length}. " - f"The allowed range is [0, {(1 << length) - 1}].") - if i < 0: - raise bitstring.CreationError("uint cannot be initialised with a negative number.") - raise e - return x - - -def intle2bitstore(i: int, length: int, signed: bool) -> ConstBitStore: - x = int2bitstore(i, length, signed).to_bytes() - return ConstBitStore.from_bytes(x[::-1]) - - -def float2bitstore(f: Union[str, float], length: int, big_endian: bool) -> ConstBitStore: - f = float(f) - fmt = {16: '>e', 32: '>f', 64: '>d'}[length] if big_endian else {16: ' 0 else float('-inf')) - return ConstBitStore.from_bytes(b) - diff --git a/bitstring/bitstore_common_helpers.py b/bitstring/bitstore_common_helpers.py deleted file mode 100644 index 5bc409e2..00000000 --- a/bitstring/bitstore_common_helpers.py +++ /dev/null @@ -1,192 +0,0 @@ -from __future__ import annotations - -import struct -import math -from typing import Union, Dict, Callable, Optional -import functools -import bitstring -from bitstring.fp8 import p4binary_fmt, p3binary_fmt -from bitstring.mxfp import (e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, - e5m2mxfp_saturate_fmt, e4m3mxfp_overflow_fmt, e5m2mxfp_overflow_fmt) - -helpers = bitstring.bitstore_helpers -ConstBitStore = bitstring.bitstore.ConstBitStore -MutableBitStore = bitstring.bitstore.MutableBitStore - - -CACHE_SIZE = 256 - -@functools.lru_cache(CACHE_SIZE) -def str_to_bitstore(s: str) -> ConstBitStore: - _, tokens = bitstring.utils.tokenparser(s) - constbitstores = [bitstore_from_token(*token) for token in tokens] - return ConstBitStore.join(constbitstores) - - -literal_bit_funcs: Dict[str, Callable[..., ConstBitStore]] = { - '0x': helpers.hex2bitstore, - '0X': helpers.hex2bitstore, - '0b': helpers.bin2bitstore, - '0B': helpers.bin2bitstore, - '0o': helpers.oct2bitstore, - '0O': helpers.oct2bitstore, -} - - -def bitstore_from_token(name: str, token_length: Optional[int], value: Optional[str]) -> ConstBitStore: - if name in literal_bit_funcs: - return literal_bit_funcs[name](value) - try: - d = bitstring.dtypes.Dtype(name, token_length) - except ValueError as e: - raise bitstring.CreationError(f"Can't parse token: {e}") - if value is None and name != 'pad': - raise ValueError(f"Token {name} requires a value.") - bs = d.build(value)._bitstore - if token_length is not None and len(bs) != d.bitlength: - raise bitstring.CreationError(f"Token with length {token_length} packed with value of length {len(bs)} " - f"({name}:{token_length}={value}).") - return bs - - - -def ue2bitstore(i: Union[str, int]) -> ConstBitStore: - i = int(i) - if i < 0: - raise bitstring.CreationError("Cannot use negative initialiser for unsigned exponential-Golomb.") - if i == 0: - return ConstBitStore.from_bin('1') - tmp = i + 1 - leadingzeros = -1 - while tmp > 0: - tmp >>= 1 - leadingzeros += 1 - remainingpart = i + 1 - (1 << leadingzeros) - return ConstBitStore.from_bin('0' * leadingzeros + '1') + helpers.int2bitstore(remainingpart, leadingzeros, False) - - -def se2bitstore(i: Union[str, int]) -> ConstBitStore: - i = int(i) - if i > 0: - u = (i * 2) - 1 - else: - u = -2 * i - return ue2bitstore(u) - - -def uie2bitstore(i: Union[str, int]) -> ConstBitStore: - i = int(i) - if i < 0: - raise bitstring.CreationError("Cannot use negative initialiser for unsigned interleaved exponential-Golomb.") - return ConstBitStore.from_bin('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1') - - -def sie2bitstore(i: Union[str, int]) -> ConstBitStore: - i = int(i) - if i == 0: - return ConstBitStore.from_bin('1') - else: - return uie2bitstore(abs(i)) + (ConstBitStore.from_bin('1') if i < 0 else ConstBitStore.from_bin('0')) - - -def bfloat2bitstore(f: Union[str, float], big_endian: bool) -> ConstBitStore: - f = float(f) - fmt = '>f' if big_endian else ' 0 else float('-inf')) - return ConstBitStore.from_bytes(b[0:2]) if big_endian else ConstBitStore.from_bytes(b[2:4]) - - -def p4binary2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - u = p4binary_fmt.float_to_int8(f) - return helpers.int2bitstore(u, 8, False) - - -def p3binary2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - u = p3binary_fmt.float_to_int8(f) - return helpers.int2bitstore(u, 8, False) - - -def e4m3mxfp2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if bitstring.options.mxfp_overflow == 'saturate': - u = e4m3mxfp_saturate_fmt.float_to_int(f) - else: - u = e4m3mxfp_overflow_fmt.float_to_int(f) - return helpers.int2bitstore(u, 8, False) - - -def e5m2mxfp2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if bitstring.options.mxfp_overflow == 'saturate': - u = e5m2mxfp_saturate_fmt.float_to_int(f) - else: - u = e5m2mxfp_overflow_fmt.float_to_int(f) - return helpers.int2bitstore(u, 8, False) - - -def e3m2mxfp2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if math.isnan(f): - raise ValueError("Cannot convert float('nan') to e3m2mxfp format as it has no representation for it.") - u = e3m2mxfp_fmt.float_to_int(f) - return helpers.int2bitstore(u, 6, False) - - -def e2m3mxfp2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if math.isnan(f): - raise ValueError("Cannot convert float('nan') to e2m3mxfp format as it has no representation for it.") - u = e2m3mxfp_fmt.float_to_int(f) - return helpers.int2bitstore(u, 6, False) - - -def e2m1mxfp2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if math.isnan(f): - raise ValueError("Cannot convert float('nan') to e2m1mxfp format as it has no representation for it.") - u = e2m1mxfp_fmt.float_to_int(f) - return helpers.int2bitstore(u, 4, False) - - -e8m0mxfp_allowed_values = [float(2 ** x) for x in range(-127, 128)] - - -def e8m0mxfp2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if math.isnan(f): - return ConstBitStore.from_bin('11111111') - try: - i = e8m0mxfp_allowed_values.index(f) - except ValueError: - raise ValueError(f"{f} is not a valid e8m0mxfp value. It must be exactly 2 ** i, for -127 <= i <= 127 or float('nan') as no rounding will be done.") - return helpers.int2bitstore(i, 8, False) - - -def mxint2bitstore(f: Union[str, float]) -> ConstBitStore: - f = float(f) - if math.isnan(f): - raise ValueError("Cannot convert float('nan') to mxint format as it has no representation for it.") - f *= 2 ** 6 # Remove the implicit scaling factor - if f > 127: # 1 + 63/64 - return ConstBitStore.from_bin('01111111') - if f <= -128: # -2 - return ConstBitStore.from_bin('10000000') - # Want to round to nearest, so move by 0.5 away from zero and round down by converting to int - if f >= 0.0: - f += 0.5 - i = int(f) - # For ties-round-to-even - if f - i == 0.0 and i % 2: - i -= 1 - else: - f -= 0.5 - i = int(f) - if f - i == 0.0 and i % 2: - i += 1 - return helpers.int2bitstore(i, 8, True) \ No newline at end of file diff --git a/bitstring/bitstore_helpers.py b/bitstring/bitstore_helpers.py new file mode 100644 index 00000000..a5bbb10e --- /dev/null +++ b/bitstring/bitstore_helpers.py @@ -0,0 +1,263 @@ +from __future__ import annotations + +from tibs import Mutibs + +import struct +import math +from typing import Union, Dict, Callable, Optional +import functools +import bitstring +from bitstring.fp8 import p4binary_fmt, p3binary_fmt +from bitstring.mxfp import (e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, + e5m2mxfp_saturate_fmt, e4m3mxfp_overflow_fmt, e5m2mxfp_overflow_fmt) + + +MutableBitStore = bitstring.bitstore.MutableBitStore + +from bitstring.helpers import tidy_input_string + + +def bin2bitstore(binstring: str) -> MutableBitStore: + binstring = tidy_input_string(binstring) + binstring = binstring.replace('0b', '') + mb = Mutibs.from_bin(binstring) + return MutableBitStore(mb) + + +def hex2bitstore(hexstring: str) -> MutableBitStore: + hexstring = tidy_input_string(hexstring) + hexstring = hexstring.replace('0x', '') + mb = Mutibs.from_hex(hexstring) + return MutableBitStore(mb) + + +def oct2bitstore(octstring: str) -> MutableBitStore: + octstring = tidy_input_string(octstring) + octstring = octstring.replace('0o', '') + mb = Mutibs.from_oct(octstring) + return MutableBitStore(mb) + + +def int2bitstore(i: int, length: int, signed: bool) -> MutableBitStore: + i = int(i) + if length <= 128: + try: + if signed: + mb = Mutibs.from_i(i, length=length) + else: + mb = Mutibs.from_u(i, length=length) + except OverflowError as e: + raise ValueError(e) + else: + b = i.to_bytes((length + 7) // 8, byteorder="big", signed=signed) + offset = 8 - (length % 8) + mb = Mutibs.from_bytes(b) + if offset != 8: + mb = mb[offset:] + return MutableBitStore(mb) + + +def intle2bitstore(i: int, length: int, signed: bool) -> MutableBitStore: + i = int(i) + if length <= 128: + try: + if signed: + mb = Mutibs.from_i(i, length=length) + else: + mb = Mutibs.from_u(i, length=length) + except OverflowError as e: + raise ValueError(e) + mb.byte_swap() + else: + b = i.to_bytes((length + 7) // 8, byteorder="little", signed=signed) + offset = 8 - (length % 8) + mb = Mutibs.from_bytes(b) + if offset != 8: + mb = mb[offset:] + return MutableBitStore(mb) + + +def float2bitstore(f: Union[str, float], length: int, big_endian: bool) -> MutableBitStore: + f = float(f) + mb = Mutibs.from_f(f, length) + if not big_endian: + mb.byte_swap() + return MutableBitStore(mb) + + +CACHE_SIZE = 256 + +@functools.lru_cache(CACHE_SIZE) +def str_to_bitstore(s: str) -> MutableBitStore: + _, tokens = bitstring.utils.tokenparser(s) + constbitstores = [bitstore_from_token(*token) for token in tokens] + return MutableBitStore.join(constbitstores) + + +literal_bit_funcs: Dict[str, Callable[..., MutableBitStore]] = { + '0x': hex2bitstore, + '0X': hex2bitstore, + '0b': bin2bitstore, + '0B': bin2bitstore, + '0o': oct2bitstore, + '0O': oct2bitstore, +} + + +def bitstore_from_token(name: str, token_length: Optional[int], value: Optional[str]) -> MutableBitStore: + if name in literal_bit_funcs: + return literal_bit_funcs[name](value) + try: + d = bitstring.dtypes.Dtype(name, token_length) + except ValueError as e: + raise bitstring.CreationError(f"Can't parse token: {e}") + if value is None and name != 'pad': + raise ValueError(f"Token {name} requires a value.") + bs = d.build(value)._bitstore + if token_length is not None and len(bs) != d.bitlength: + raise bitstring.CreationError(f"Token with length {token_length} packed with value of length {len(bs)} " + f"({name}:{token_length}={value}).") + return bs + + + +def ue2bitstore(i: Union[str, int]) -> MutableBitStore: + i = int(i) + if i < 0: + raise bitstring.CreationError("Cannot use negative initialiser for unsigned exponential-Golomb.") + if i == 0: + return MutableBitStore.from_bin('1') + tmp = i + 1 + leadingzeros = -1 + while tmp > 0: + tmp >>= 1 + leadingzeros += 1 + remainingpart = i + 1 - (1 << leadingzeros) + return MutableBitStore.from_bin('0' * leadingzeros + '1') + int2bitstore(remainingpart, leadingzeros, False) + + +def se2bitstore(i: Union[str, int]) -> MutableBitStore: + i = int(i) + if i > 0: + u = (i * 2) - 1 + else: + u = -2 * i + return ue2bitstore(u) + + +def uie2bitstore(i: Union[str, int]) -> MutableBitStore: + i = int(i) + if i < 0: + raise bitstring.CreationError("Cannot use negative initialiser for unsigned interleaved exponential-Golomb.") + return MutableBitStore.from_bin('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1') + + +def sie2bitstore(i: Union[str, int]) -> MutableBitStore: + i = int(i) + if i == 0: + return MutableBitStore.from_bin('1') + else: + return uie2bitstore(abs(i)) + (MutableBitStore.from_bin('1') if i < 0 else MutableBitStore.from_bin('0')) + + +def bfloat2bitstore(f: Union[str, float], big_endian: bool) -> MutableBitStore: + f = float(f) + fmt = '>f' if big_endian else ' 0 else float('-inf')) + return MutableBitStore.from_bytes(b[0:2]) if big_endian else MutableBitStore.from_bytes(b[2:4]) + + +def p4binary2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + u = p4binary_fmt.float_to_int8(f) + return int2bitstore(u, 8, False) + + +def p3binary2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + u = p3binary_fmt.float_to_int8(f) + return int2bitstore(u, 8, False) + + +def e4m3mxfp2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if bitstring.options.mxfp_overflow == 'saturate': + u = e4m3mxfp_saturate_fmt.float_to_int(f) + else: + u = e4m3mxfp_overflow_fmt.float_to_int(f) + return int2bitstore(u, 8, False) + + +def e5m2mxfp2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if bitstring.options.mxfp_overflow == 'saturate': + u = e5m2mxfp_saturate_fmt.float_to_int(f) + else: + u = e5m2mxfp_overflow_fmt.float_to_int(f) + return int2bitstore(u, 8, False) + + +def e3m2mxfp2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to e3m2mxfp format as it has no representation for it.") + u = e3m2mxfp_fmt.float_to_int(f) + return int2bitstore(u, 6, False) + + +def e2m3mxfp2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to e2m3mxfp format as it has no representation for it.") + u = e2m3mxfp_fmt.float_to_int(f) + return int2bitstore(u, 6, False) + + +def e2m1mxfp2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to e2m1mxfp format as it has no representation for it.") + u = e2m1mxfp_fmt.float_to_int(f) + return int2bitstore(u, 4, False) + + +e8m0mxfp_allowed_values = [float(2 ** x) for x in range(-127, 128)] + + +def e8m0mxfp2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if math.isnan(f): + return MutableBitStore.from_bin('11111111') + try: + i = e8m0mxfp_allowed_values.index(f) + except ValueError: + raise ValueError(f"{f} is not a valid e8m0mxfp value. It must be exactly 2 ** i, for -127 <= i <= 127 or float('nan') as no rounding will be done.") + return int2bitstore(i, 8, False) + + +def mxint2bitstore(f: Union[str, float]) -> MutableBitStore: + f = float(f) + if math.isnan(f): + raise ValueError("Cannot convert float('nan') to mxint format as it has no representation for it.") + f *= 2 ** 6 # Remove the implicit scaling factor + if f > 127: # 1 + 63/64 + return MutableBitStore.from_bin('01111111') + if f <= -128: # -2 + return MutableBitStore.from_bin('10000000') + # Want to round to nearest, so move by 0.5 away from zero and round down by converting to int + if f >= 0.0: + f += 0.5 + i = int(f) + # For ties-round-to-even + if f - i == 0.0 and i % 2: + i -= 1 + else: + f -= 0.5 + i = int(f) + if f - i == 0.0 and i % 2: + i += 1 + return int2bitstore(i, 8, True) \ No newline at end of file diff --git a/bitstring/bitstore_tibs.py b/bitstring/bitstore_tibs.py index f11c42aa..f6b8b87c 100644 --- a/bitstring/bitstore_tibs.py +++ b/bitstring/bitstore_tibs.py @@ -7,147 +7,129 @@ from bitstring.helpers import offset_slice_indices_lsb0 - class ConstBitStore: """A light wrapper around tibs.Tibs that does the LSB0 stuff""" - __slots__ = ('_bits',) + __slots__ = ('tibs',) - def __init__(self, initializer: Union[Tibs, None] = None) -> None: - if initializer is not None: - self._bits = initializer - else: - self._bits = Tibs() + def __init__(self, initializer: Tibs) -> None: + self.tibs = initializer @classmethod def join(cls, bitstores: Iterable[ConstBitStore], /) -> ConstBitStore: x = super().__new__(cls) - x._bits = Tibs.from_joined(b._bits for b in bitstores) + x.tibs = Tibs.from_joined(b.tibs for b in bitstores) return x @classmethod def from_zeros(cls, i: int): x = super().__new__(cls) - x._bits = Tibs.from_zeros(i) - return x - - @classmethod - def from_tibs(cls, tb: Tibs): - x = super().__new__(cls) - x._bits = tb + x.tibs = Tibs.from_zeros(i) return x @classmethod def from_bytes(cls, b: Union[bytes, bytearray, memoryview], /) -> ConstBitStore: x = super().__new__(cls) - x._bits = Tibs.from_bytes(b) + x.tibs = Tibs.from_bytes(b) return x @classmethod def frombuffer(cls, buffer, /, length: Optional[int] = None) -> ConstBitStore: x = super().__new__(cls) # TODO: tibs needs a Tibs.from_buffer method. - x._bits = Tibs.from_bytes(bytes(buffer)) + x.tibs = Tibs.from_bytes(bytes(buffer)) if length is not None: if length < 0: raise CreationError("Can't create bitstring with a negative length.") - if length > len(x._bits): + if length > len(x.tibs): raise CreationError( - f"Can't create bitstring with a length of {length} from {len(x._bits)} bits of data.") + f"Can't create bitstring with a length of {length} from {len(x.tibs)} bits of data.") return x.getslice(0, length) if length is not None else x @classmethod def from_bin(cls, s: str) -> ConstBitStore: x = super().__new__(cls) - x._bits = Tibs.from_bin(s) + x.tibs = Tibs.from_bin(s) return x - def set(self, value, pos) -> None: - self._bits.set(value, pos) - - @staticmethod - def using_rust_core() -> bool: - return True - - def tobitarray(self): - raise TypeError("tobitarray() is not available when using the Rust core option.") - - def to_bytes(self, pad_at_end: bool = True) -> bytes: - excess_bits = len(self._bits) % 8 - if excess_bits != 0: - # Pad with zeros to make full bytes - if pad_at_end: - padded_bits = self._bits.to_mutibs().extend(Mutibs.from_zeros(8 - excess_bits)) - else: - padded_bits = self._bits.to_mutibs().extend_left(Mutibs.from_zeros(8 - excess_bits)) - return padded_bits.to_bytes() - return self._bits.to_bytes() + def to_bytes(self) -> bytes: + padding = 8 - len(self.tibs) % 8 + if padding == 8: + return self.tibs.to_bytes() + return (self.tibs + [0] * padding).to_bytes() def to_u(self) -> int: if len(self) > 128: - return int.from_bytes(self.to_bytes(pad_at_end=False), byteorder="big", signed=False) + padding = 8 - len(self.tibs) % 8 + if padding == 8: + b = self.tibs.to_bytes() + else: + b = ([0] * padding + self.tibs).to_bytes() + return int.from_bytes(b, byteorder="big", signed=False) try: - return self._bits.to_u() + return self.tibs.to_u() except OverflowError as e: raise ValueError(e) def to_i(self) -> int: if len(self) > 128: - return int.from_bytes(self.to_bytes(pad_at_end=False), byteorder="big", signed=True) + padding = 8 - len(self.tibs) % 8 + if padding == 8: + b = self.tibs.to_bytes() + else: + pad_bit = self.tibs[0] # Keep sign when padding + b = ([pad_bit] * padding + self.tibs).to_bytes() + return int.from_bytes(b, byteorder="big", signed=True) try: - return self._bits.to_i() + return self.tibs.to_i() except OverflowError as e: raise ValueError(e) def to_hex(self) -> str: - return self._bits.to_hex() + return self.tibs.to_hex() def to_bin(self) -> str: - return self._bits.to_bin() + return self.tibs.to_bin() def to_oct(self) -> str: - return self._bits.to_oct() + return self.tibs.to_oct() def __add__(self, other: ConstBitStore, /) -> ConstBitStore: - newbits = self._bits + other._bits - return ConstBitStore.from_tibs(newbits) + return ConstBitStore(self.tibs + other.tibs) def __eq__(self, other: Any, /) -> bool: - return self._bits == other._bits + return self.tibs == other.tibs def __and__(self, other: ConstBitStore, /) -> ConstBitStore: - return ConstBitStore.from_tibs(self._bits & other._bits) + return ConstBitStore(self.tibs & other.tibs) def __or__(self, other: ConstBitStore, /) -> ConstBitStore: - return ConstBitStore.from_tibs(self._bits | other._bits) + return ConstBitStore(self.tibs | other.tibs) def __xor__(self, other: ConstBitStore, /) -> ConstBitStore: - return ConstBitStore.from_tibs(self._bits ^ other._bits) + return ConstBitStore(self.tibs ^ other.tibs) def __invert__(self) -> ConstBitStore: - return ConstBitStore.from_tibs(~self._bits) + return ConstBitStore(~self.tibs) def find(self, bs: ConstBitStore, start: int, end: int, bytealigned: bool = False) -> int | None: assert start >= 0 - return self._bits.find(bs._bits, start, end, byte_aligned=bytealigned) + return self.tibs.find(bs.tibs, start, end, byte_aligned=bytealigned) def rfind(self, bs: ConstBitStore, start: int, end: int, bytealigned: bool = False) -> int | None: assert start >= 0 - return self._bits.rfind(bs._bits, start, end, byte_aligned=bytealigned) + return self.tibs.rfind(bs.tibs, start, end, byte_aligned=bytealigned) def findall_msb0(self, bs: ConstBitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: - x = self._bits - for p in x.find_all(bs._bits, start=start, end=end, byte_aligned=bytealigned): + x = self.tibs + for p in x.find_all(bs.tibs, start=start, end=end, byte_aligned=bytealigned): yield p def rfindall_msb0(self, bs: ConstBitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: - x = self._bits - for p in x.rfind_all(bs._bits, start=start, end=end, byte_aligned=bytealigned): + x = self.tibs + for p in x.rfind_all(bs.tibs, start=start, end=end, byte_aligned=bytealigned): yield p - def count(self, value, /) -> int: - return self._bits.count(value) - def __iter__(self) -> Iterable[bool]: length = len(self) for i in range(length): @@ -155,222 +137,182 @@ def __iter__(self) -> Iterable[bool]: def _mutable_copy(self) -> MutableBitStore: """Always creates a copy, even if instance is immutable.""" - return MutableBitStore.from_mutibs(self._bits.to_mutibs()) + return MutableBitStore(self.tibs.to_mutibs()) def copy(self) -> ConstBitStore: - return self if isinstance(self._bits, Tibs) else self._mutable_copy() + return self def __getitem__(self, item: Union[int, slice], /) -> Union[int, ConstBitStore]: # Use getindex or getslice instead raise NotImplementedError def getindex_msb0(self, index: int, /) -> bool: - return self._bits.__getitem__(index) + return self.tibs.__getitem__(index) def getslice_withstep_msb0(self, key: slice, /) -> ConstBitStore: - return ConstBitStore(self._bits.__getitem__(key)) + return ConstBitStore(self.tibs.__getitem__(key)) def getslice_withstep_lsb0(self, key: slice, /) -> ConstBitStore: key = offset_slice_indices_lsb0(key, len(self)) - return ConstBitStore(self._bits.__getitem__(key)) + return ConstBitStore(self.tibs.__getitem__(key)) def getslice_msb0(self, start: Optional[int], stop: Optional[int], /) -> ConstBitStore: - return ConstBitStore(self._bits[start:stop]) + return ConstBitStore(self.tibs[start:stop]) def getslice_lsb0(self, start: Optional[int], stop: Optional[int], /) -> ConstBitStore: s = offset_slice_indices_lsb0(slice(start, stop, None), len(self)) - return ConstBitStore(self._bits[s.start:s.stop]) + return ConstBitStore(self.tibs[s.start:s.stop]) def getindex_lsb0(self, index: int, /) -> bool: - return self._bits.__getitem__(-index - 1) + return self.tibs.__getitem__(-index - 1) def any(self) -> bool: - return self._bits.any() + return self.tibs.any() def all(self) -> bool: - return self._bits.all() + return self.tibs.all() def __len__(self) -> int: - return len(self._bits) + return len(self.tibs) class MutableBitStore: """A light wrapper around tibs.Mutibs that does the LSB0 stuff""" - __slots__ = ('_bits',) + __slots__ = ('tibs',) - def __init__(self, initializer: Union[Mutibs, Tibs, None] = None) -> None: - if initializer is not None: - self._bits = initializer - else: - self._bits = Mutibs() + def __init__(self, initializer: Mutibs) -> None: + self.tibs = initializer @classmethod - def from_zeros(cls, i: int): + def join(cls, bitstores: Iterable[MutableBitStore], /) -> MutableBitStore: x = super().__new__(cls) - x._bits = Mutibs.from_zeros(i) + x.tibs = Mutibs.from_joined(b.tibs for b in bitstores) return x @classmethod - def from_mutibs(cls, mb: Mutibs): - assert isinstance(mb, Mutibs) + def from_zeros(cls, i: int): x = super().__new__(cls) - x._bits = mb + x.tibs = Mutibs.from_zeros(i) return x @classmethod def from_bytes(cls, b: Union[bytes, bytearray, memoryview], /) -> MutableBitStore: x = super().__new__(cls) - x._bits = Mutibs.from_bytes(b) + x.tibs = Mutibs.from_bytes(b) return x - @classmethod - def frombuffer(cls, buffer, /, length: Optional[int] = None) -> MutableBitStore: - x = super().__new__(cls) - # TODO: tibs needs a Bits.from_buffer method. - x._bits = Mutibs.from_bytes(bytes(buffer)) - if length is not None: - if length < 0: - raise CreationError("Can't create bitstring with a negative length.") - if length > len(x._bits): - raise CreationError( - f"Can't create bitstring with a length of {length} from {len(x._bits)} bits of data.") - return x.getslice(0, length) if length is not None else x - @classmethod def from_bin(cls, s: str) -> MutableBitStore: x = super().__new__(cls) - x._bits = Mutibs.from_bin(s) + x.tibs = Mutibs.from_bin(s) return x - def set(self, value, pos) -> None: - self._bits.set(value, pos) - - @staticmethod - def using_rust_core() -> bool: - return True - - def tobitarray(self): - raise TypeError("tobitarray() is not available when using the Rust core option.") - def to_bytes(self, pad_at_end: bool = True) -> bytes: - excess_bits = len(self._bits) % 8 + excess_bits = len(self.tibs) % 8 if excess_bits != 0: # Pad with zeros to make full bytes if pad_at_end: - padded_bits = self._bits + Mutibs.from_zeros(8 - excess_bits) + padded_bits = self.tibs + Mutibs.from_zeros(8 - excess_bits) else: - padded_bits = Mutibs.from_zeros(8 - excess_bits) + self._bits + padded_bits = Mutibs.from_zeros(8 - excess_bits) + self.tibs return padded_bits.to_bytes() - return self._bits.to_bytes() + return self.tibs.to_bytes() def to_u(self) -> int: if len(self) > 128: - return int.from_bytes(self.to_bytes(pad_at_end=False), byteorder="big", signed=False) + padding = 8 - len(self.tibs) % 8 + if padding == 8: + b = self.tibs.to_bytes() + else: + b = ([0] * padding + self.tibs).to_bytes() + return int.from_bytes(b, byteorder="big", signed=False) try: - return self._bits.to_u() + return self.tibs.to_u() except OverflowError as e: raise ValueError(e) def to_i(self) -> int: if len(self) > 128: - return int.from_bytes(self.to_bytes(pad_at_end=False), byteorder="big", signed=True) + padding = 8 - len(self.tibs) % 8 + if padding == 8: + b = self.tibs.to_bytes() + else: + pad_bit = self.tibs[0] # Keep sign when padding + b = ([pad_bit] * padding + self.tibs).to_bytes() + return int.from_bytes(b, byteorder="big", signed=True) try: - return self._bits.to_i() + return self.tibs.to_i() except OverflowError as e: raise ValueError(e) def to_hex(self) -> str: - return self._bits.to_hex() + return self.tibs.to_hex() def to_bin(self) -> str: - return self._bits.to_bin() + return self.tibs.to_bin() def to_oct(self) -> str: - return self._bits.to_oct() - - def __imul__(self, n: int, /) -> None: - self._bits *= n + return self.tibs.to_oct() def __ilshift__(self, n: int, /) -> None: - self._bits <<= n + self.tibs <<= n def __irshift__(self, n: int, /) -> None: - self._bits >>= n - - def __iadd__(self, other: MutableBitStore, /) -> MutableBitStore: - self._bits += other._bits - return self + self.tibs >>= n def __add__(self, other: MutableBitStore, /) -> MutableBitStore: - bs = self._mutable_copy() - bs += other - return bs + return MutableBitStore(self.tibs + other.tibs) def __eq__(self, other: Any, /) -> bool: - return self._bits == other._bits + return self.tibs == other.tibs def __and__(self, other: MutableBitStore, /) -> MutableBitStore: - return MutableBitStore.from_mutibs(self._bits & other._bits) + return MutableBitStore(self.tibs & other.tibs) def __or__(self, other: MutableBitStore, /) -> MutableBitStore: - return MutableBitStore.from_mutibs(self._bits | other._bits) + return MutableBitStore(self.tibs | other.tibs) def __xor__(self, other: MutableBitStore, /) -> MutableBitStore: - return MutableBitStore.from_mutibs(self._bits ^ other._bits) - - def __iand__(self, other: MutableBitStore, /) -> MutableBitStore: - self._bits &= other._bits - return self - - def __ior__(self, other: MutableBitStore, /) -> MutableBitStore: - self._bits |= other._bits - return self - - def __ixor__(self, other: MutableBitStore, /) -> MutableBitStore: - self._bits ^= other._bits - return self + return MutableBitStore(self.tibs ^ other.tibs) def __invert__(self) -> MutableBitStore: - return MutableBitStore.from_mutibs(~self._bits) + return MutableBitStore(~self.tibs) def find(self, bs: MutableBitStore, start: int, end: int, bytealigned: bool = False) -> int: assert start >= 0 - return self._bits.find(bs._bits, start, end, byte_aligned=bytealigned) + return self.tibs.find(bs.tibs, start, end, byte_aligned=bytealigned) def rfind(self, bs: MutableBitStore, start: int, end: int, bytealigned: bool = False): assert start >= 0 - return self._bits.rfind(bs._bits, start, end, byte_aligned=bytealigned) + return self.tibs.rfind(bs.tibs, start, end, byte_aligned=bytealigned) def findall_msb0(self, bs: MutableBitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: - x = self._bits.to_tibs() - for p in x.find_all(bs._bits, start=start, end=end, byte_aligned=bytealigned): + x = self.tibs.to_tibs() + for p in x.find_all(bs.tibs, start=start, end=end, byte_aligned=bytealigned): yield p def rfindall_msb0(self, bs: MutableBitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]: - x = self._bits.to_tibs() - for p in x.rfind_all(bs._bits, start=start, end=end, byte_aligned=bytealigned): + x = self.tibs.to_tibs() + for p in x.rfind_all(bs.tibs, start=start, end=end, byte_aligned=bytealigned): yield p - def count(self, value, /) -> int: - return self._bits.count(value) - def clear(self) -> None: - self._bits.clear() + self.tibs.clear() def reverse(self) -> None: - self._bits.reverse() + self.tibs.reverse() def __iter__(self) -> Iterable[bool]: for i in range(len(self)): yield self.getindex(i) def extend_left(self, other: MutableBitStore, /) -> None: - self._bits.extend_left(other._bits) + self.tibs.extend_left(other.tibs) def _mutable_copy(self) -> MutableBitStore: """Always creates a copy, even if instance is immutable.""" - return MutableBitStore.from_mutibs(self._bits.__copy__()) + return MutableBitStore(self.tibs.__copy__()) def copy(self) -> MutableBitStore: return self._mutable_copy() @@ -380,24 +322,24 @@ def __getitem__(self, item: Union[int, slice], /) -> Union[int, MutableBitStore] raise NotImplementedError def getindex_msb0(self, index: int, /) -> bool: - return self._bits.__getitem__(index) + return self.tibs.__getitem__(index) def getslice_withstep_msb0(self, key: slice, /) -> MutableBitStore: - return MutableBitStore(self._bits.__getitem__(key)) + return MutableBitStore(self.tibs.__getitem__(key)) def getslice_withstep_lsb0(self, key: slice, /) -> MutableBitStore: key = offset_slice_indices_lsb0(key, len(self)) - return MutableBitStore(self._bits.__getitem__(key)) + return MutableBitStore(self.tibs.__getitem__(key)) def getslice_msb0(self, start: Optional[int], stop: Optional[int], /) -> MutableBitStore: - return MutableBitStore(self._bits[start:stop]) + return MutableBitStore(self.tibs[start:stop]) def getslice_lsb0(self, start: Optional[int], stop: Optional[int], /) -> MutableBitStore: s = offset_slice_indices_lsb0(slice(start, stop, None), len(self)) - return MutableBitStore(self._bits[s.start:s.stop]) + return MutableBitStore(self.tibs[s.start:s.stop]) def getindex_lsb0(self, index: int, /) -> bool: - return self._bits.__getitem__(-index - 1) + return self.tibs.__getitem__(-index - 1) @overload def setitem_lsb0(self, key: int, value: int, /) -> None: @@ -410,45 +352,45 @@ def setitem_lsb0(self, key: slice, value: MutableBitStore, /) -> None: def setitem_lsb0(self, key: Union[int, slice], value: Union[int, MutableBitStore], /) -> None: if isinstance(key, slice): new_slice = offset_slice_indices_lsb0(key, len(self)) - self._bits.__setitem__(new_slice, value._bits) + self.tibs.__setitem__(new_slice, value.tibs) else: - self._bits.__setitem__(-key - 1, bool(value)) + self.tibs.__setitem__(-key - 1, bool(value)) def delitem_lsb0(self, key: Union[int, slice], /) -> None: if isinstance(key, slice): new_slice = offset_slice_indices_lsb0(key, len(self)) - self._bits.__delitem__(new_slice) + self.tibs.__delitem__(new_slice) else: - self._bits.__delitem__(-key - 1) + self.tibs.__delitem__(-key - 1) def invert_msb0(self, index: Optional[int] = None, /) -> None: if index is not None: - self._bits.invert(index) + self.tibs.invert(index) else: - self._bits.invert() + self.tibs.invert() def invert_lsb0(self, index: Optional[int] = None, /) -> None: if index is not None: - self._bits.invert(-index - 1) + self.tibs.invert(-index - 1) else: - self._bits.invert() + self.tibs.invert() def any(self) -> bool: - return self._bits.any() + return self.tibs.any() def all(self) -> bool: - return self._bits.all() + return self.tibs.all() def __len__(self) -> int: - return len(self._bits) + return len(self.tibs) def setitem_msb0(self, key, value, /): if isinstance(value, (MutableBitStore, ConstBitStore)): - self._bits.__setitem__(key, value._bits) + self.tibs.__setitem__(key, value.tibs) else: if isinstance(key, slice): key = range(*key.indices(len(self))) - self._bits.set(value, key) + self.tibs.set(value, key) def delitem_msb0(self, key, /): - self._bits.__delitem__(key) + self.tibs.__delitem__(key) diff --git a/bitstring/bitstore_tibs_helpers.py b/bitstring/bitstore_tibs_helpers.py deleted file mode 100644 index 3b4f7f54..00000000 --- a/bitstring/bitstore_tibs_helpers.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -from typing import Union -from tibs import Tibs, Mutibs -import bitstring - -MutableBitStore = bitstring.bitstore.MutableBitStore -ConstBitStore = bitstring.bitstore.ConstBitStore - -from bitstring.helpers import tidy_input_string - - -def bin2bitstore(binstring: str) -> ConstBitStore: - binstring = tidy_input_string(binstring) - binstring = binstring.replace('0b', '') - mb = Tibs.from_bin(binstring) - return ConstBitStore.from_tibs(mb) - - -def hex2bitstore(hexstring: str) -> ConstBitStore: - hexstring = tidy_input_string(hexstring) - hexstring = hexstring.replace('0x', '') - mb = Tibs.from_hex(hexstring) - return ConstBitStore.from_tibs(mb) - - -def oct2bitstore(octstring: str) -> ConstBitStore: - octstring = tidy_input_string(octstring) - octstring = octstring.replace('0o', '') - mb = Tibs.from_oct(octstring) - return ConstBitStore.from_tibs(mb) - - -def int2bitstore(i: int, length: int, signed: bool) -> ConstBitStore: - i = int(i) - if length <= 128: - try: - if signed: - mb = Tibs.from_i(i, length=length) - else: - mb = Tibs.from_u(i, length=length) - except OverflowError as e: - raise ValueError(e) - else: - b = i.to_bytes((length + 7) // 8, byteorder="big", signed=signed) - offset = 8 - (length % 8) - mb = Tibs.from_bytes(b) - if offset != 8: - mb = mb[offset:] - return ConstBitStore.from_tibs(mb) - - -def intle2bitstore(i: int, length: int, signed: bool) -> ConstBitStore: - i = int(i) - if length <= 128: - try: - if signed: - mb = Mutibs.from_i(i, length=length) - else: - mb = Mutibs.from_u(i, length=length) - except OverflowError as e: - raise ValueError(e) - mb.byte_swap() - else: - b = i.to_bytes((length + 7) // 8, byteorder="little", signed=signed) - offset = 8 - (length % 8) - mb = Mutibs.from_bytes(b) - if offset != 8: - mb = mb[offset:] - return ConstBitStore.from_tibs(mb.as_tibs()) - - -def float2bitstore(f: Union[str, float], length: int, big_endian: bool) -> ConstBitStore: - f = float(f) - mb = Mutibs.from_f(f, length) - if not big_endian: - mb.byte_swap() - return ConstBitStore.from_tibs(mb.as_tibs()) diff --git a/bitstring/bitstream.py b/bitstring/bitstream.py index 3d10bda1..7ed6ed40 100644 --- a/bitstring/bitstream.py +++ b/bitstring/bitstream.py @@ -6,8 +6,8 @@ from typing import Union, List, Any, Optional, overload, TypeVar, Tuple import copy import numbers +import bitstring.bitstore_helpers as helpers -common_helpers = bitstring.bitstore_common_helpers TConstBitStream = TypeVar("TConstBitStream", bound='ConstBitStream') @@ -33,7 +33,6 @@ class ConstBitStream(Bits): rfind() -- Seek backwards to find a sub-bitstring. split() -- Create generator of chunks split by a delimiter. startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobitarray() -- Return bitstring as a bitarray from the bitarray package. tobytes() -- Return bitstring as bytes, padding if needed. tofile() -- Write bitstring to file, padding if needed. unpack() -- Interpret bits using format string. @@ -473,7 +472,7 @@ def bytealign(self) -> int: @classmethod def fromstring(cls: TBits, s: str, /) -> TBits: x = super().__new__(cls) - x._bitstore = common_helpers.str_to_bitstore(s) + x._bitstore = helpers.str_to_bitstore(s) x._pos = 0 return x @@ -545,7 +544,6 @@ class BitStream(ConstBitStream, bitstring.BitArray): set() -- Set bit(s) to 1 or 0. split() -- Create generator of chunks split by a delimiter. startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobitarray() -- Return bitstring as a bitarray from the bitarray package. tobytes() -- Return bitstring as bytes, padding if needed. tofile() -- Write bitstring to file, padding if needed. unpack() -- Interpret bits using format string. @@ -611,7 +609,7 @@ def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optio @classmethod def fromstring(cls: TBits, s: str, /) -> TBits: x = super().__new__(cls) - b = common_helpers.str_to_bitstore(s) + b = helpers.str_to_bitstore(s) x._bitstore = b._mutable_copy() x._pos = 0 return x diff --git a/bitstring/bitstring_options.py b/bitstring/bitstring_options.py index 306ea4fb..e3c8272d 100644 --- a/bitstring/bitstring_options.py +++ b/bitstring/bitstring_options.py @@ -20,11 +20,6 @@ def __init__(self): no_color = os.getenv('NO_COLOR') self.no_color = True if no_color else False - @property - def using_rust_core(self): - x = ConstBitStore() - return x.using_rust_core() - @property def mxfp_overflow(self) -> str: return self._mxfp_overflow diff --git a/bitstring/fp8.py b/bitstring/fp8.py index 575dbbb4..6e857c97 100644 --- a/bitstring/fp8.py +++ b/bitstring/fp8.py @@ -7,9 +7,9 @@ import struct import zlib import array -import bitarray from bitstring.luts import binary8_luts_compressed import math +import tibs class Binary8Format: @@ -66,17 +66,17 @@ def createLUT_for_binary8_to_float(self): """Create a LUT to convert an int in range 0-255 representing a float8 into a Python float""" i2f = [] for i in range(256): - b = bitarray.util.int2ba(i, length=8, endian='big', signed=False) + b = tibs.Tibs.from_u(i, 8) sign = b[0] - exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits]) + exponent = b[1:1 + self.exp_bits].to_u() significand = b[1 + self.exp_bits:] if exponent == 0: - significand = bitarray.bitarray('0') + significand + significand = [0] + significand exponent = -self.bias + 1 else: - significand = bitarray.bitarray('1') + significand + significand = [1] + significand exponent -= self.bias - f = float(bitarray.util.ba2int(significand)) / (2.0 ** (7 - self.exp_bits)) + f = float(significand.to_u()) / (2.0 ** (7 - self.exp_bits)) f *= 2 ** exponent i2f.append(f if not sign else -f) # One special case for minus zero diff --git a/bitstring/methods.py b/bitstring/methods.py index c224dc9a..c9de3868 100644 --- a/bitstring/methods.py +++ b/bitstring/methods.py @@ -5,11 +5,9 @@ from bitstring.utils import tokenparser from bitstring.exceptions import CreationError from typing import Union, List +import bitstring.bitstore_helpers as helpers MutableBitStore = bitstring.bitstore.MutableBitStore -helpers = bitstring.bitstore_helpers -common_helpers = bitstring.bitstore_common_helpers - def pack(fmt: Union[str, List[str]], *values, **kwargs) -> BitStream: """Pack the values according to the format string and return a new BitStream. @@ -78,7 +76,7 @@ def pack(fmt: Union[str, List[str]], *values, **kwargs) -> BitStream: raise CreationError(f"Token with length {length} packed with value of length {len(value)}.") bsl.append(value._bitstore) continue - bsl.append(common_helpers.bitstore_from_token(name, length, value)) + bsl.append(helpers.bitstore_from_token(name, length, value)) except StopIteration: raise CreationError(f"Not enough parameters present to pack according to the " f"format. {len(tokens)} values are needed.") diff --git a/bitstring/mxfp.py b/bitstring/mxfp.py index d675c359..e9acabb7 100644 --- a/bitstring/mxfp.py +++ b/bitstring/mxfp.py @@ -1,7 +1,7 @@ import array import math import struct -import bitarray +import tibs from bitstring.luts import mxfp_luts_compressed import zlib from typing import Optional @@ -127,17 +127,17 @@ def createLUT_for_int_to_float(self) -> array.array: i2f = [] length = 1 + self.exp_bits + self.mantissa_bits for i in range(1 << length): - b = bitarray.util.int2ba(i, length=length, endian='big', signed=False) + b = tibs.Tibs.from_u(i, length) sign = b[0] - exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits]) + exponent = b[1:1 + self.exp_bits].to_u() significand = b[1 + self.exp_bits:] if exponent == 0: - significand = bitarray.bitarray('0') + significand + significand = [0] + significand exponent = -self.bias + 1 else: - significand = bitarray.bitarray('1') + significand + significand = [1] + significand exponent -= self.bias - f = float(bitarray.util.ba2int(significand)) / (2.0 ** self.mantissa_bits) + f = float(significand.to_u()) / (2.0 ** self.mantissa_bits) f *= 2 ** exponent if length == 8: # Some special cases diff --git a/doc/bits.rst b/doc/bits.rst index 7b2ba49f..f705a1e4 100644 --- a/doc/bits.rst +++ b/doc/bits.rst @@ -250,16 +250,6 @@ Methods >>> s.startswith('0b111011') True -.. method:: Bits.tobitarray() -> bitarray.bitarray - - Returns the bitstring as a ``bitarray`` object. - - Converts the bitstring to an equivalent ``bitarray`` object from the ``bitarray`` package. - This shouldn't be confused with the ``BitArray`` type provided in the ``bitstring`` package - the ``bitarray`` package is a separate third-party way of representing binary objects. - - Note that ``BitStream`` and ``ConstBitStream`` types that have a bit position do support this method but the bit position information will be lost. - - .. method:: Bits.tobytes() -> bytes diff --git a/doc/functions.rst b/doc/functions.rst index 9ff2d7a3..94b0a764 100644 --- a/doc/functions.rst +++ b/doc/functions.rst @@ -228,30 +228,6 @@ See https://no-color.org for more information. The terminal colours can also be turned off by setting ``bitstring.options.no_color`` to ``True``. -using_rust_core -^^^^^^^^^^^^^^^ - -.. data:: bitstring.using_rust_core : bool - -By default the C-based bitarray library is used to optimise the core operations in bitstring. -This being replaced with the `tibs `_ library, which is written in Rust and by the -author of bitstring. -For now both options are available, and bitarray remains the default. - -The ``using_rust_core`` flag is read-only - to try the new library set the ``BITSTRING_USE_RUST_CORE`` environment variable before running your -program, tests or Python interpreter. :: - - % BITSTRING_USE_RUST_CORE=1 python - Python 3.13.5 (main, Jul 23 2025, 00:30:58) [Clang 20.1.4 ] on darwin - Type "help", "copyright", "credits" or "license" for more information. - >>> import bitstring - >>> bitstring.options.using_rust_core - True - -This should work identically in terms of features, except that the ``tobitarray()`` method will be unavailable. - -The plan is to make the Rust core the only option from version 5 of bitstring. - ---- Command Line Usage diff --git a/doc/introduction.rst b/doc/introduction.rst index c7c4c5cc..0613718e 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -87,7 +87,6 @@ The first parameter when creating a bitstring is a positional only parameter, re * A file object, opened in binary mode, from which the bitstring will be formed. * A ``bytearray`` or ``bytes`` object. * An ``array`` object from the built-in ``array`` module. This is used after being converted to it's constituent byte data via its ``tobytes`` method. -* A ``bitarray`` or ``frozenbitarray`` object from the 3rd party ``bitarray`` package. If it is a string then that string will be parsed into tokens to construct the binary data: @@ -141,7 +140,7 @@ Integers won't be promoted, but instead will raise a ``TypeError``:: ``BitsType`` ^^^^^^^^^^^^ -.. class:: BitsType(Bits | str | Iterable[Any] | bool | BinaryIO | bytearray | bytes | memoryview | bitarray.bitarray) +.. class:: BitsType(Bits | str | Iterable[Any] | bool | BinaryIO | bytearray | bytes | memoryview) The ``BitsType`` type is used in the documentation in a number of places where an object of any type that can be promoted to a bitstring is acceptable. diff --git a/doc/optimisation.rst b/doc/optimisation.rst index 1939607b..3d2a4fb9 100644 --- a/doc/optimisation.rst +++ b/doc/optimisation.rst @@ -3,7 +3,7 @@ Optimisation Techniques ======================= -The :mod:`bitstring` module aims to be as fast as reasonably possible, and since version 4.1 has used the ``bitarray`` C extension to power its core. +The :mod:`bitstring` module aims to be as fast as reasonably possible, and since version 5.0 has used the ``tibs`` Rust module to power its core. There are however some pointers you should follow to make your code efficient, so if you need things to run faster then this is the section for you. diff --git a/doc/quick_reference.rst b/doc/quick_reference.rst index d83520cc..d9cd1702 100644 --- a/doc/quick_reference.rst +++ b/doc/quick_reference.rst @@ -91,7 +91,6 @@ Methods * :meth:`~Bits.rfind` -- Seek backwards to find a sub-bitstring. * :meth:`~Bits.split` -- Create generator of chunks split by a delimiter. * :meth:`~Bits.startswith` -- Return whether the bitstring starts with a sub-bitstring. -* :meth:`~Bits.tobitarray` -- Return bitstring as a ``bitarray`` object from the `bitarray `_ package. * :meth:`~Bits.tobytes` -- Return bitstring as bytes, padding if needed. * :meth:`~Bits.tofile` -- Write bitstring to file, padding if needed. * :meth:`~Bits.unpack` -- Interpret bits using format string. diff --git a/pyproject.toml b/pyproject.toml index 64b27c41..bd2b0deb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bitstring" -version = "4.4.0" +version = "5.0.0_beta1" license = { text = "MIT" } authors = [ { name="Scott Griffiths", email="dr.scottgriffiths@gmail.com" }, @@ -29,7 +29,6 @@ classifiers = [ ] keywords = ["binary", "bitarray", "bitvector", "bitfield"] dependencies = [ - "bitarray >= 3.0.0, < 4.0", "tibs >= 0.5.6, < 0.6", ] diff --git a/tests/test_bitarray.py b/tests/test_bitarray.py index 2e4d5fa0..0954898e 100644 --- a/tests/test_bitarray.py +++ b/tests/test_bitarray.py @@ -6,7 +6,6 @@ import pytest import sys import os -import bitarray import bitstring from bitstring import BitArray, Bits @@ -911,72 +910,6 @@ def test_native_endian_string_initialisers(self): THIS_DIR = os.path.dirname(os.path.abspath(__file__)) -class TestBitarray: - - def teardown_method(self) -> None: - bitstring.lsb0 = False - - def test_to_bitarray(self): - a = BitArray('0xff, 0b0') - if a._bitstore.using_rust_core(): - with pytest.raises(TypeError): - _ = a.tobitarray() - else: - b = a.tobitarray() - assert type(b) == bitarray.bitarray - assert b == bitarray.bitarray('111111110') - - def test_to_bitarray_lsb0(self): - bitstring.lsb0 = True - a = bitstring.Bits('0xff, 0b0') - if a._bitstore.using_rust_core(): - with pytest.raises(TypeError): - _ = a.tobitarray() - else: - b = a.tobitarray() - assert type(b) == bitarray.bitarray - assert b == bitarray.bitarray('111111110') - - def test_from_file(self): - a = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) - if a._bitstore.using_rust_core(): - with pytest.raises(TypeError): - _ = a.tobitarray() - else: - b = a.tobitarray() - assert a.bin == b.to01() - - def test_with_offset(self): - a = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) - b = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile'), offset=11) - assert len(a) == len(b) + 11 - if a._bitstore.using_rust_core(): - with pytest.raises(TypeError): - _ = a.tobitarray() - else: - assert a[11:].tobitarray() == b.tobitarray() - - def test_with_length(self): - a = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) - b = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile'), length=11) - assert len(b) == 11 - if a._bitstore.using_rust_core(): - with pytest.raises(TypeError): - _ = a.tobitarray() - else: - assert a[:11].tobitarray() == b.tobitarray() - - def test_with_offset_and_length(self): - a = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) - b = bitstring.ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile'), offset=17, length=7) - assert len(b) == 7 - if a._bitstore.using_rust_core(): - with pytest.raises(TypeError): - _ = a.tobitarray() - else: - assert a[17:24].tobitarray() == b.tobitarray() - - try: import numpy as np numpy_installed = True diff --git a/tests/test_bits.py b/tests/test_bits.py index bd44b966..b030acd9 100644 --- a/tests/test_bits.py +++ b/tests/test_bits.py @@ -2,30 +2,32 @@ import pytest import io import sys -import bitarray import bitstring import array import os import re from bitstring import InterpretError, Bits, BitArray -from hypothesis import given, assume, reproduce_failure, settings +from hypothesis import given, settings import hypothesis.strategies as st from bitstring.helpers import offset_slice_indices_lsb0 -sys.path.insert(0, '..') +sys.path.insert(0, "..") THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + def remove_unprintable(s: str) -> str: - colour_escape = re.compile(r'(?:\x1B[@-_])[0-?]*[ -/]*[@-~]') - return colour_escape.sub('', s) + colour_escape = re.compile(r"(?:\x1B[@-_])[0-?]*[ -/]*[@-~]") + return colour_escape.sub("", s) @settings(max_examples=500) -@given(length=st.integers(0, 9), - start=st.integers(-20, 20), - stop=st.integers(-20, 20), - step=st.integers(0, 7)) +@given( + length=st.integers(0, 9), + start=st.integers(-20, 20), + stop=st.integers(-20, 20), + step=st.integers(0, 7), +) def test_lsb0_slicing(length, start, stop, step): if start == -20: start = None @@ -42,17 +44,17 @@ def test_lsb0_slicing(length, start, stop, step): values1 = values_fwd[start1:stop1:step1] lsb0key = offset_slice_indices_lsb0(slice(start, stop, step), length) - values2 = values_bwd[lsb0key.start:lsb0key.stop:lsb0key.step] + values2 = values_bwd[lsb0key.start : lsb0key.stop : lsb0key.step] values2.reverse() assert values1 == values2 class TestCreation: def test_creation_from_bytes(self): - s = Bits(bytes=b'\xa0\xff') - assert (s.len, s.hex) == (16, 'a0ff') - s = Bits(bytes=b'abc', length=0) - assert s == '' + s = Bits(bytes=b"\xa0\xff") + assert (s.len, s.hex) == (16, "a0ff") + s = Bits(bytes=b"abc", length=0) + assert s == "" @given(st.binary()) def test_creation_from_bytes_roundtrip(self, data): @@ -61,52 +63,52 @@ def test_creation_from_bytes_roundtrip(self, data): def test_creation_from_bytes_errors(self): with pytest.raises(bitstring.CreationError): - Bits(bytes=b'abc', length=25) + Bits(bytes=b"abc", length=25) def test_creation_from_data_with_offset(self): - s1 = Bits(bytes=b'\x0b\x1c\x2f', offset=0, length=20) - s2 = Bits(bytes=b'\xa0\xb1\xC2', offset=4) - assert (s2.len, s2.hex) == (20, '0b1c2') - assert (s1.len, s1.hex) == (20, '0b1c2') + s1 = Bits(bytes=b"\x0b\x1c\x2f", offset=0, length=20) + s2 = Bits(bytes=b"\xa0\xb1\xc2", offset=4) + assert (s2.len, s2.hex) == (20, "0b1c2") + assert (s1.len, s1.hex) == (20, "0b1c2") assert s1 == s2 def test_creation_from_hex(self): - s = Bits(hex='0xA0ff') - assert (s.len, s.hex) == (16, 'a0ff') - s = Bits(hex='0x0x0X') - assert (s.length, s.hex) == (0, '') + s = Bits(hex="0xA0ff") + assert (s.len, s.hex) == (16, "a0ff") + s = Bits(hex="0x0x0X") + assert (s.length, s.hex) == (0, "") def test_creation_from_hex_with_whitespace(self): - s = Bits(hex=' \n0 X a 4e \r3 \n') - assert s.hex == 'a4e3' + s = Bits(hex=" \n0 X a 4e \r3 \n") + assert s.hex == "a4e3" - @pytest.mark.parametrize("bad_val", ['0xx0', '0xX0', '0Xx0', '-2e']) + @pytest.mark.parametrize("bad_val", ["0xx0", "0xX0", "0Xx0", "-2e"]) def test_creation_from_hex_errors(self, bad_val: str): with pytest.raises(bitstring.CreationError): Bits(hex=bad_val) with pytest.raises(bitstring.CreationError): - Bits('0x2', length=2) + Bits("0x2", length=2) with pytest.raises(bitstring.CreationError): - Bits('0x3', offset=1) + Bits("0x3", offset=1) def test_creation_from_bin(self): - s = Bits(bin='1010000011111111') - assert (s.length, s.hex) == (16, 'a0ff') - s = Bits(bin='00')[:1] - assert s.bin == '0' - s = Bits(bin=' 0000 \n 0001\r ') - assert s.bin == '00000001' + s = Bits(bin="1010000011111111") + assert (s.length, s.hex) == (16, "a0ff") + s = Bits(bin="00")[:1] + assert s.bin == "0" + s = Bits(bin=" 0000 \n 0001\r ") + assert s.bin == "00000001" def test_creation_from_bin_with_whitespace(self): - s = Bits(bin=' \r\r\n0 B 00 1 1 \t0 ') - assert s.bin == '00110' + s = Bits(bin=" \r\r\n0 B 00 1 1 \t0 ") + assert s.bin == "00110" def test_creation_from_oct_errors(self): - s = Bits('0b00011') + s = Bits("0b00011") with pytest.raises(bitstring.InterpretError): _ = s.oct with pytest.raises(bitstring.CreationError): - _ = Bits('oct=8') + _ = Bits("oct=8") def test_creation_from_uint_with_offset(self): with pytest.raises(bitstring.CreationError): @@ -126,11 +128,11 @@ def test_creation_from_uint_errors(self): def test_creation_from_int(self): s = Bits(int=0, length=4) - assert s.bin == '0000' + assert s.bin == "0000" s = Bits(int=1, length=2) - assert s.bin == '01' + assert s.bin == "01" s = Bits(int=-1, length=11) - assert s.bin == '11111111111' + assert s.bin == "11111111111" s = Bits(int=12, length=7) assert s.int == 12 s = Bits(int=-243, length=108) @@ -161,8 +163,8 @@ def test_creation_from_se_errors(self): with pytest.raises(bitstring.CreationError): Bits(se=-5, length=33) with pytest.raises(bitstring.CreationError): - Bits('se2=0') - s = Bits(bin='001000') + Bits("se2=0") + s = Bits(bin="001000") with pytest.raises(bitstring.InterpretError): _ = s.se @@ -179,23 +181,23 @@ def test_creation_from_ue_errors(self): Bits(ue=-1) with pytest.raises(bitstring.CreationError): Bits(ue=1, length=12) - s = Bits(bin='10') + s = Bits(bin="10") with pytest.raises(bitstring.InterpretError): _ = s.ue def test_creation_from_bool(self): - a = Bits('bool=1') - assert a == 'bool=1' - b = Bits('bool:1=0') + a = Bits("bool=1") + assert a == "bool=1" + b = Bits("bool:1=0") assert b == [0] - c = bitstring.pack('bool=1, 2*bool', 0, 1) - assert c == '0b101' - d = bitstring.pack('bool:1=1, 2*bool1', 1, 0) - assert d == '0b110' + c = bitstring.pack("bool=1, 2*bool", 0, 1) + assert c == "0b101" + d = bitstring.pack("bool:1=1, 2*bool1", 1, 0) + assert d == "0b110" def test_creation_from_bool_errors(self): with pytest.raises(ValueError): - _ = Bits('bool=3') + _ = Bits("bool=3") with pytest.raises(bitstring.CreationError): _ = Bits(bool=0, length=2) @@ -203,78 +205,49 @@ def test_creation_keyword_error(self): with pytest.raises(bitstring.CreationError): Bits(squirrel=5) - @pytest.mark.skipif(bool(os.environ.get('BITSTRING_USE_RUST_CORE')), reason="bitarray not supported with Rust backend") - def test_creation_from_bitarray(self): - ba = bitarray.bitarray('0010') - bs = Bits(ba) - assert bs.bin == '0010' - bs2 = Bits(bitarray=ba) - assert bs2.bin == '0010' - - @pytest.mark.skipif(bool(os.environ.get('BITSTRING_USE_RUST_CORE')), reason="bitarray not supported with Rust backend") - def test_creation_from_frozen_bitarray(self): - fba = bitarray.frozenbitarray('111100001') - ba = Bits(fba) - assert ba.bin == '111100001' - bs2 = Bits(bitarray=fba) - assert bs2.bin == '111100001' - bs3 = Bits(bitarray=fba, offset=4) - assert bs3.bin == '00001' - bs3 = Bits(bitarray=fba, offset=4, length=4) - assert bs3.bin == '0000' - - def test_creation_from_bitarray_errors(self): - ba = bitarray.bitarray('0101') - with pytest.raises(bitstring.CreationError): - _ = Bits(bitarray=ba, length=5) - with pytest.raises(bitstring.CreationError): - _ = Bits(bitarray=ba, offset=5) - with pytest.raises(bitstring.CreationError): - _ = Bits(ba, length=-1) - def test_creation_from_memoryview(self): x = bytes(bytearray(range(20))) m = memoryview(x[10:15]) b = Bits(m) - assert b.unpack('5*u8') == [10, 11, 12, 13, 14] + assert b.unpack("5*u8") == [10, 11, 12, 13, 14] class TestInitialisation: def test_empty_init(self): a = Bits() - assert a == '' + assert a == "" def test_no_pos(self): - a = Bits('0xabcdef') + a = Bits("0xabcdef") with pytest.raises(AttributeError): _ = a.pos def test_find(self): - a = Bits('0xabcd') - r = a.find('0xbc') + a = Bits("0xabcd") + r = a.find("0xbc") assert r[0] == 4 - r = a.find('0x23462346246', bytealigned=True) + r = a.find("0x23462346246", bytealigned=True) assert not r def test_rfind(self): - a = Bits('0b11101010010010') - b = a.rfind('0b010') + a = Bits("0b11101010010010") + b = a.rfind("0b010") assert b[0] == 11 def test_find_all(self): - a = Bits('0b0010011') + a = Bits("0b0010011") b = list(a.findall([1])) assert b == [2, 5, 6] - t = BitArray('0b10') - tp = list(t.findall('0b1')) + t = BitArray("0b10") + tp = list(t.findall("0b1")) assert tp == [0] class TestCut: def test_cut(self): - s = Bits('0b000111'*10) + s = Bits("0b000111" * 10) for t in s.cut(6): - assert t.bin == '000111' + assert t.bin == "000111" class TestInterleavedExpGolomb: @@ -304,7 +277,7 @@ def test_interpretation(self): assert Bits(sie=x).sie == x def test_errors(self): - for f in ['sie=100, 0b1001', '0b00', 'uie=100, 0b1001']: + for f in ["sie=100, 0b1001", "0b00", "uie=100, 0b1001"]: s = Bits.fromstring(f) with pytest.raises(bitstring.InterpretError): _ = s.sie @@ -316,20 +289,20 @@ def test_errors(self): class TestFileBased: def setup_method(self): - filename = os.path.join(THIS_DIR, 'smalltestfile') + filename = os.path.join(THIS_DIR, "smalltestfile") self.a = Bits(filename=filename) self.b = Bits(filename=filename, offset=16) self.c = Bits(filename=filename, offset=20, length=16) self.d = Bits(filename=filename, offset=20, length=4) def test_creation_with_offset(self): - assert str(self.a) == '0x0123456789abcdef' - assert str(self.b) == '0x456789abcdef' - assert str(self.c) == '0x5678' + assert str(self.a) == "0x0123456789abcdef" + assert str(self.b) == "0x456789abcdef" + assert str(self.c) == "0x5678" def test_bit_operators(self): x = self.b[4:20] - assert x == '0x5678' + assert x == "0x5678" assert (x & self.c).hex == self.c.hex assert self.c ^ self.b[4:20] == Bits(16) assert self.a[23:36] | self.c[3:] == self.c[3:] @@ -338,14 +311,14 @@ def test_bit_operators(self): assert repr(y) == repr(self.c) def test_addition(self): - _ = self.d + '0x1' + _ = self.d + "0x1" x = self.a[20:24] + self.c[-4:] + self.c[8:12] - assert x == '0x587' + assert x == "0x587" x = self.b + x - assert x.h == '456789abcdef587' + assert x.h == "456789abcdef587" x = BitArray(x) del x[12:24] - assert x == '0x456abcdef587' + assert x == "0x456abcdef587" class TestComparisons: @@ -363,21 +336,21 @@ def test_unorderable(self): class TestSubclassing: - def test_is_instance(self): class SubBits(bitstring.Bits): pass + a = SubBits() assert isinstance(a, SubBits) def test_class_type(self): class SubBits(bitstring.Bits): pass + assert SubBits().__class__ == SubBits class TestLongBoolConversion: - def test_long_bool(self): a = Bits(1000) b = bool(a) @@ -385,47 +358,45 @@ def test_long_bool(self): class TestPadToken: - def test_creation(self): - a = Bits.fromstring('pad:10') + a = Bits.fromstring("pad:10") assert a == Bits(10) - b = Bits('pad:0') + b = Bits("pad:0") assert b == Bits() - c = Bits('0b11, pad:1, 0b111') - assert c == Bits('0b110111') + c = Bits("0b11, pad:1, 0b111") + assert c == Bits("0b110111") def test_pack(self): - s = bitstring.pack('0b11, pad:3, 0b1') - assert s.bin == '110001' - d = bitstring.pack('pad:c', c=12) + s = bitstring.pack("0b11, pad:3, 0b1") + assert s.bin == "110001" + d = bitstring.pack("pad:c", c=12) assert d == Bits(12) - e = bitstring.pack('0xf, uint12, pad:1, bin, pad4, 0b10', 0, '111') - assert e.bin == '11110000000000000111000010' + e = bitstring.pack("0xf, uint12, pad:1, bin, pad4, 0b10", 0, "111") + assert e.bin == "11110000000000000111000010" def test_unpack(self): - s = Bits('0b111000111') - x, y = s.unpack('3, pad:3, 3') - assert (x, y.u) == ('0b111', 7) - x, y = s.unpack('2, pad2, bin') - assert (x.u2, y) == (3, '00111') - x = s.unpack('pad:1, pad:2, pad:3') + s = Bits("0b111000111") + x, y = s.unpack("3, pad:3, 3") + assert (x, y.u) == ("0b111", 7) + x, y = s.unpack("2, pad2, bin") + assert (x.u2, y) == (3, "00111") + x = s.unpack("pad:1, pad:2, pad:3") assert x == [] def test_unpack_bug(self): - t = Bits('0o755, ue=12, int3=-1') - a, b = t.unpack('pad:9, ue, int3') + t = Bits("0o755, ue=12, int3=-1") + a, b = t.unpack("pad:9, ue, int3") assert (a, b) == (12, -1) class TestModifiedByAddingBug: - def test_adding(self): - a = Bits('0b0') - b = Bits('0b11') + a = Bits("0b0") + b = Bits("0b11") c = a + b - assert c == '0b011' - assert a == '0b0' - assert b == '0b11' + assert c == "0b011" + assert a == "0b0" + assert b == "0b11" def test_adding2(self): a = Bits(100) @@ -437,32 +408,30 @@ def test_adding2(self): class TestWrongTypeBug: - def test_append_to_bits(self): a = Bits(BitArray()) with pytest.raises(AttributeError): - a.append('0b1') + a.append("0b1") assert type(a) == Bits b = bitstring.ConstBitStream(bitstring.BitStream()) assert type(b) == bitstring.ConstBitStream class TestInitFromArray: - - @given(st.sampled_from(['B', 'H', 'I', 'L', 'Q', 'f', 'd'])) + @given(st.sampled_from(["B", "H", "I", "L", "Q", "f", "d"])) def test_empty_array(self, t): a = array.array(t) b = Bits(a) assert b.length == 0 def test_single_byte(self): - a = array.array('B', b'\xff') + a = array.array("B", b"\xff") b = Bits(a) assert b.length == 8 - assert b.hex == 'ff' + assert b.hex == "ff" def test_signed_short(self): - a = array.array('h') + a = array.array("h") a.append(10) a.append(-1) b = Bits(a) @@ -470,15 +439,14 @@ def test_signed_short(self): assert b.bytes == a.tobytes() def test_double(self): - a = array.array('d', [0.0, 1.0, 2.5]) + a = array.array("d", [0.0, 1.0, 2.5]) b = Bits(a) assert b.length == 192 - c, d, e = b.unpack('3*floatne:64') + c, d, e = b.unpack("3*floatne:64") assert (c, d, e) == (0.0, 1.0, 2.5) class TestIteration: - def test_iterate_empty_bits(self): assert list(Bits([])) == [] assert list(Bits([1, 0])[1:1]) == [] @@ -488,43 +456,39 @@ def test_iterate_non_empty_bits(self): assert list(Bits([1, 0, 0, 1])[1:3]) == [False, False] def test_iterate_long_bits(self): - assert list(Bits([1, 0]) * 1024) == \ - [True, False] * 1024 + assert list(Bits([1, 0]) * 1024) == [True, False] * 1024 - -class TestContainsBug: +class TestContainsBug: def test_contains(self): - a = Bits('0b1, 0x0001dead0001') - assert '0xdead' in a - assert not '0xfeed' in a + a = Bits("0b1, 0x0001dead0001") + assert "0xdead" in a + assert "0xfeed" not in a - assert '0b1' in Bits('0xf') - assert not '0b0' in Bits('0xf') + assert "0b1" in Bits("0xf") + assert "0b0" not in Bits("0xf") class TestByteStoreImmutablity: - def test_immutability_bug_append(self): - a = Bits('0b111') - b = a + '0b000' + a = Bits("0b111") + b = a + "0b000" c = BitArray(b) c[1] = 0 - assert c.bin == '101000' - assert a.b3 == '111' - assert b.bin == '111000' + assert c.bin == "101000" + assert a.b3 == "111" + assert b.bin == "111000" def test_immutability_bug_prepend(self): - a = Bits('0b111') - b = '0b000' + a + a = Bits("0b111") + b = "0b000" + a c = BitArray(b) c[1] = 1 - assert b.bin == '000111' - assert c.bin == '010111' + assert b.bin == "000111" + assert c.bin == "010111" class TestLsb0Indexing: - @classmethod def setup_class(cls): bitstring.lsb0 = True @@ -534,7 +498,7 @@ def teardown_class(cls): bitstring.lsb0 = False def test_get_single_bit(self): - a = Bits('0b000001111') + a = Bits("0b000001111") assert a[0] is True assert a[3] is True assert a[4] is False @@ -549,54 +513,53 @@ def test_get_single_bit(self): _ = a[-10] def test_simple_slicing(self): - a = Bits('0xabcdef') - assert a[0:4] == '0xf' - assert a[4:8] == '0xe' - assert a[:] == '0xabcdef' - assert a[4:] == '0xabcde' - assert a[-4:] == '0xa' - assert a[-8:-4] == '0xb' - assert a[:-8] == '0xcdef' + a = Bits("0xabcdef") + assert a[0:4] == "0xf" + assert a[4:8] == "0xe" + assert a[:] == "0xabcdef" + assert a[4:] == "0xabcde" + assert a[-4:] == "0xa" + assert a[-8:-4] == "0xb" + assert a[:-8] == "0xcdef" def test_extended_slicing(self): - a = Bits('0b0100000100100100') - assert a[2::3] == '0b10111' + a = Bits("0b0100000100100100") + assert a[2::3] == "0b10111" def test_all(self): - a = Bits('0b000111') + a = Bits("0b000111") assert a.all(1, [0, 1, 2]) assert a.all(0, [3, 4, 5]) def test_any(self): - a = Bits('0b00000110') + a = Bits("0b00000110") assert a.any(1, [0, 1]) assert a.any(0, [5, 6]) def test_startswith(self): - a = Bits('0b0000000111') - assert a.startswith('0b111') - assert not a.startswith('0b0') - assert a.startswith('0b011', start=1) - assert not a.startswith('0b0111', end=3) - assert a.startswith('0b0111', end=4) + a = Bits("0b0000000111") + assert a.startswith("0b111") + assert not a.startswith("0b0") + assert a.startswith("0b011", start=1) + assert not a.startswith("0b0111", end=3) + assert a.startswith("0b0111", end=4) def test_ends_with(self): - a = Bits('0x1234abcd') - assert a.endswith('0x123') - assert not a.endswith('0xabcd') + a = Bits("0x1234abcd") + assert a.endswith("0x123") + assert not a.endswith("0xabcd") def test_lsb0_slicing_error(self): - a = Bits('0b01') + a = Bits("0b01") b = a[::-1] - assert b == '0b10' - t = Bits('0xf0a')[::-1] - assert t == '0x50f' - s = Bits('0xf0a')[::-1][::-1] - assert s == '0xf0a' + assert b == "0b10" + t = Bits("0xf0a")[::-1] + assert t == "0x50f" + s = Bits("0xf0a")[::-1][::-1] + assert s == "0xf0a" class TestLsb0Interpretations: - @classmethod def setup_class(cls): bitstring.lsb0 = True @@ -606,8 +569,8 @@ def teardown_class(cls): bitstring.lsb0 = False def test_uint(self): - a = Bits('0x01') - assert a == '0b00000001' + a = Bits("0x01") + assert a == "0b00000001" assert a.uint == 1 assert a[0] is True @@ -633,112 +596,136 @@ def test_golomb(self): _ = Bits(sie=2) def test_bytes(self): - a = Bits.fromstring('0xabcdef') + a = Bits.fromstring("0xabcdef") b = a.bytes - assert b == b'\xab\xcd\xef' + assert b == b"\xab\xcd\xef" b = a.bytes3 - assert b == b'\xab\xcd\xef' + assert b == b"\xab\xcd\xef" class TestUnderscoresInLiterals: - def test_hex_creation(self): - a = Bits(hex='ab_cd__ef') - assert a.hex == 'abcdef' - b = Bits('0x0102_0304') + a = Bits(hex="ab_cd__ef") + assert a.hex == "abcdef" + b = Bits("0x0102_0304") assert b.uint == 0x0102_0304 def test_binary_creation(self): - a = Bits(bin='0000_0001_0010') - assert a.bin == '000000010010' - b = Bits.fromstring('0b0011_1100_1111_0000') - assert b.bin == '0011110011110000' + a = Bits(bin="0000_0001_0010") + assert a.bin == "000000010010" + b = Bits.fromstring("0b0011_1100_1111_0000") + assert b.bin == "0011110011110000" v = 0b1010_0000 c = Bits(uint=0b1010_0000, length=8) assert c.uint == v def test_octal_creation(self): - a = Bits(oct='0011_2233_4455_6677') + a = Bits(oct="0011_2233_4455_6677") assert a.uint == 0o001122334455_6677 - b = Bits('0o123_321_123_321') + b = Bits("0o123_321_123_321") assert b.uint == 0o123_321_123321 class TestPrettyPrinting: - def test_simplest_cases(self): - a = Bits('0b101011110000') + a = Bits("0b101011110000") s = io.StringIO() a.pp(stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 10101111 0000 ] """ + ) s = io.StringIO() - a.pp('hex', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("hex", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: af 0 ] """ + ) s = io.StringIO() - a.pp('oct', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("oct", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 5360 ] """ + ) def test_small_width(self): a = Bits(20) s = io.StringIO() - a.pp(fmt='b', stream=s, width=5) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp(fmt="b", stream=s, width=5) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 00000000 8: 00000000 16: 0000 ] """ + ) def test_separator(self): - a = Bits('0x0f0f')*9 + a = Bits("0x0f0f") * 9 s = io.StringIO() - a.pp('hex:32', sep='!-!', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("hex:32", sep="!-!", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 0f0f0f0f!-!0f0f0f0f!-!0f0f0f0f!-!0f0f0f0f ] + trailing_bits = 0x0f0f """ + ) def test_multi_line(self): a = Bits(100) s = io.StringIO() - a.pp('bin', sep='', stream=s, width=80) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("bin", sep="", stream=s, width=80) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 000000000000000000000000000000000000000000000000000000000000000000000000 72: 0000000000000000000000000000 ] """ + ) def test_multiformat(self): - a = Bits('0b1111000011110000') + a = Bits("0b1111000011110000") s = io.StringIO() - a.pp(stream=s, fmt='bin, hex') - assert remove_unprintable(s.getvalue()) == """ [ + a.pp(stream=s, fmt="bin, hex") + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 11110000 11110000 : f0 f0 ] """ + ) s = io.StringIO() - a.pp(stream=s, fmt='hex, bin:12') - assert remove_unprintable(s.getvalue()) == """ [ + a.pp(stream=s, fmt="hex, bin:12") + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: f0f : 111100001111 ] + trailing_bits = 0x0 """ + ) def test_multi_line_multi_format(self): a = Bits(int=-1, length=112) s = io.StringIO() - a.pp(stream=s, fmt='bin:8, hex:8', width=42) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp(stream=s, fmt="bin:8, hex:8", width=42) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 11111111 11111111 11111111 : ff ff ff 24: 11111111 11111111 11111111 : ff ff ff 48: 11111111 11111111 11111111 : ff ff ff @@ -746,9 +733,12 @@ def test_multi_line_multi_format(self): 96: 11111111 11111111 : ff ff ] """ + ) s = io.StringIO() - a.pp(stream=s, fmt='bin, hex', width=41) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp(stream=s, fmt="bin, hex", width=41) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 11111111 11111111 : ff ff 16: 11111111 11111111 : ff ff 32: 11111111 11111111 : ff ff @@ -758,29 +748,33 @@ def test_multi_line_multi_format(self): 96: 11111111 11111111 : ff ff ] """ + ) a = bytearray(range(0, 256)) b = Bits(bytes=a) s = io.StringIO() - b.pp(stream=s, fmt='bytes') - assert remove_unprintable(s.getvalue()) == r""" [ + b.pp(stream=s, fmt="bytes") + assert ( + remove_unprintable(s.getvalue()) + == r""" [ 0: ĀāĂă ĄąĆć ĈĉĊċ ČčĎď ĐđĒē ĔĕĖė ĘęĚě ĜĝĞğ !"# $%&' ()*+ ,-./ 0123 4567 89:; <=>? @ABC DEFG HIJK LMNO PQRS TUVW XYZ[ 736: \]^_ `abc defg hijk lmno pqrs tuvw xyz{ |}~ſ ƀƁƂƃ ƄƅƆƇ ƈƉƊƋ ƌƍƎƏ ƐƑƒƓ ƔƕƖƗ Ƙƙƚƛ ƜƝƞƟ ƠơƢƣ ƤƥƦƧ ƨƩƪƫ ƬƭƮƯ ưƱƲƳ ƴƵƶƷ 1472: Ƹƹƺƻ Ƽƽƾƿ ǀǁǂǃ DŽDždžLJ LjljNJNj njǍǎǏ ǐǑǒǓ ǔǕǖǗ ǘǙǚǛ ǜǝǞǟ ǠǡǢǣ ǤǥǦǧ ǨǩǪǫ ǬǭǮǯ ǰDZDzdz ǴǵǶǷ ǸǹǺǻ ǼǽǾÿ ] """ + ) def test_group_size_errors(self): a = Bits(120) with pytest.raises(ValueError): - a.pp('hex:3') + a.pp("hex:3") with pytest.raises(ValueError): - a.pp('hex:4, oct') + a.pp("hex:4, oct") def test_zero_group_size(self): a = Bits(600) s = io.StringIO() - a.pp('b0', stream=s, show_offset=False) + a.pp("b0", stream=s, show_offset=False) expected_output = """ [ 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -793,7 +787,7 @@ def test_zero_group_size(self): a = Bits(400) s = io.StringIO() - a.pp(stream=s, fmt='hex:0', show_offset=False, width=80) + a.pp(stream=s, fmt="hex:0", show_offset=False, width=80) expected_output = """ [ 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000 @@ -803,7 +797,7 @@ def test_zero_group_size(self): s = io.StringIO() a = Bits(uint=10, length=48) - a.pp(stream=s, width=20, fmt='hex:0, oct:0', show_offset=False) + a.pp(stream=s, width=20, fmt="hex:0, oct:0", show_offset=False) expected_output = """ [ 000000 : 00000000 00000a : 00000012 @@ -812,9 +806,9 @@ def test_zero_group_size(self): assert remove_unprintable(s.getvalue()) == expected_output def test_oct(self): - a = Bits('0o01234567'*20) + a = Bits("0o01234567" * 20) s = io.StringIO() - a.pp(stream=s, fmt='o', show_offset=False, width=20) + a.pp(stream=s, fmt="o", show_offset=False, width=20) expected_output = """ [ 0123 4567 0123 4567 0123 4567 0123 4567 @@ -831,7 +825,7 @@ def test_oct(self): assert remove_unprintable(s.getvalue()) == expected_output t = io.StringIO() - a.pp('h, oct:0', width=1, show_offset=False, stream=t) + a.pp("h, oct:0", width=1, show_offset=False, stream=t) expected_output = """ [ 053977 : 01234567 053977 : 01234567 @@ -858,31 +852,28 @@ def test_oct(self): assert remove_unprintable(t.getvalue()) == expected_output def test_bytes(self): - a = Bits(bytes=b'helloworld!!'*5) + a = Bits(bytes=b"helloworld!!" * 5) s = io.StringIO() - a.pp(stream=s, fmt='bytes', show_offset=False, width=48) - expected_output = ( -""" [ + a.pp(stream=s, fmt="bytes", show_offset=False, width=48) + expected_output = """ [ hell owor ld!! hell owor ld!! hell owor ld!! hell owor ld!! hell owor ld!! ] -""") +""" assert remove_unprintable(s.getvalue()) == expected_output s = io.StringIO() - a.pp(stream=s, fmt='bytes0', show_offset=False, width=40) - expected_output = ( -""" [ + a.pp(stream=s, fmt="bytes0", show_offset=False, width=40) + expected_output = """ [ helloworld!!helloworld!!helloworld!!hell oworld!!helloworld!! ] """ - ) assert remove_unprintable(s.getvalue()) == expected_output def test_bool(self): - a = Bits('0b1100') + a = Bits("0b1100") s = io.StringIO() - a.pp(stream=s, fmt='bool', show_offset=False, width=20) + a.pp(stream=s, fmt="bool", show_offset=False, width=20) expected_output = """ [ 1 1 0 0 ] @@ -891,26 +882,24 @@ def test_bool(self): class TestPrettyPrintingErrors: - def test_wrong_formats(self): - a = Bits('0x12341234') + a = Bits("0x12341234") with pytest.raises(ValueError): - a.pp('binary') + a.pp("binary") with pytest.raises(ValueError): - a.pp('bin, bin, bin') + a.pp("bin, bin, bin") def test_interpret_problems(self): a = Bits(7) with pytest.raises(InterpretError): - a.pp('oct') + a.pp("oct") with pytest.raises(InterpretError): - a.pp('hex') + a.pp("hex") with pytest.raises(InterpretError): - a.pp('bin, bytes') + a.pp("bin, bytes") class TestPrettyPrinting_LSB0: - def setup_method(self) -> None: bitstring.lsb0 = True @@ -918,38 +907,49 @@ def teardown_method(self) -> None: bitstring.lsb0 = False def test_bin(self): - a = Bits(bin='1111 0000 0000 1111 1010') + a = Bits(bin="1111 0000 0000 1111 1010") s = io.StringIO() - a.pp('bin', stream=s, width=5) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("bin", stream=s, width=5) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 11111010 :0 00000000 :8 1111 :16 ] """ + ) -class TestPrettyPrinting_NewFormats: +class TestPrettyPrinting_NewFormats: def test_float(self): - a = Bits('float32=10.5') + a = Bits("float32=10.5") s = io.StringIO() - a.pp('float32', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("float32", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 10.5 ] """ + ) s = io.StringIO() - a.pp('float16', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("float16", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 2.578125 0.0 ] """ + ) def test_uint(self): a = Bits().join([Bits(uint=x, length=12) for x in range(40, 105)]) s = io.StringIO() - a.pp('uint, h12', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("uint, h12", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 40 41 42 43 44 45 46 47 48 49 50 51 : 028 029 02a 02b 02c 02d 02e 02f 030 031 032 033 144: 52 53 54 55 56 57 58 59 60 61 62 63 : 034 035 036 037 038 039 03a 03b 03c 03d 03e 03f 288: 64 65 66 67 68 69 70 71 72 73 74 75 : 040 041 042 043 044 045 046 047 048 049 04a 04b @@ -958,51 +958,62 @@ def test_uint(self): 720: 100 101 102 103 104 : 064 065 066 067 068 ] """ + ) def test_float(self): - a = BitArray(float=76.25, length=64) + '0b11111' + a = BitArray(float=76.25, length=64) + "0b11111" s = io.StringIO() - a.pp('i64, float', stream=s) - assert remove_unprintable(s.getvalue()) == """ [ + a.pp("i64, float", stream=s) + assert ( + remove_unprintable(s.getvalue()) + == """ [ 0: 4635066033680416768 : 76.25 ] + trailing_bits = 0b11111 """ + ) -class TestCopy: +class TestCopy: def test_copy_method(self): - s = Bits('0xc00dee') + s = Bits("0xc00dee") t = s.copy() assert s == t class TestNativeEndianIntegers: - def test_uintne(self): s = Bits(uintne=454, length=160) - t = Bits('uintne160=454') + t = Bits("uintne160=454") assert s == t def test_intne(self): s = Bits(intne=-1000, length=64) - t = Bits('intne:64=-1000') + t = Bits("intne:64=-1000") assert s == t class TestNonNativeEndianIntegers: - def setup_method(self) -> None: - bitstring.byteorder = 'little' if bitstring.byteorder == 'big' else 'little' + bitstring.byteorder = "little" if bitstring.byteorder == "big" else "little" def teardown_method(self) -> None: self.setup_method() def test_uintne(self): s = Bits(uintne=454, length=160) - t = Bits('uintne160=454') + t = Bits("uintne160=454") assert s == t def test_intne(self): s = Bits(intne=-1000, length=64) - t = Bits('intne:64=-1000') + t = Bits("intne:64=-1000") assert s == t + + +def test_large_ints(): + s = Bits(int=-1, length=123) + assert s.int == -1 + s = Bits(int=-1, length=201) + assert s.int == -1 + s = Bits(uint=12, length=201) + assert s.uint == 12 diff --git a/tests/test_bitstring.py b/tests/test_bitstring.py index 98babebb..be125496 100644 --- a/tests/test_bitstring.py +++ b/tests/test_bitstring.py @@ -198,11 +198,3 @@ def test_reading_float_with_no_length(self): a = bitstring.BitStream(float=14, length=16) b = a.read('float') assert b == 14.0 - -def test_rust_core(): - using_rust = bitstring.options.using_rust_core - x = bitstring.BitStream('0x1') - if hasattr(x._bitstore, "_bitarray"): - assert using_rust is False - else: - assert using_rust is True \ No newline at end of file