pax_global_header00006660000000000000000000000064146244475260014530gustar00rootroot0000000000000052 comment=2cf71ac420b569eee0a1f63747fe53b08dd986ec bitstring-bitstring-4.2.3/000077500000000000000000000000001462444752600155465ustar00rootroot00000000000000bitstring-bitstring-4.2.3/.github/000077500000000000000000000000001462444752600171065ustar00rootroot00000000000000bitstring-bitstring-4.2.3/.github/workflows/000077500000000000000000000000001462444752600211435ustar00rootroot00000000000000bitstring-bitstring-4.2.3/.github/workflows/ci.yml000066400000000000000000000114631462444752600222660ustar00rootroot00000000000000name: CI on: push: branches: - stable - main - 4.1-update tags: - v* pull_request: branches: - '**' concurrency: group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref == 'refs/heads/main' && github.sha || '' }} cancel-in-progress: true defaults: run: shell: bash jobs: build: name: Build ${{ matrix.os.name }} ${{ matrix.python.name }} runs-on: ${{ matrix.os.runs-on }} strategy: fail-fast: false matrix: os: - name: 🐧 runs-on: ubuntu-latest python: - name: CPython 3.10 major_dot_minor: '3.10' action: '3.10' steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 # with: # This allows the matrix to specify just the major.minor version while still # expanding it to get the latest patch version including alpha releases. # This avoids the need to update for each new alpha, beta, release candidate, # and then finally an actual release version. actions/setup-python doesn't # support this for PyPy presently so we get no help there. # # CPython -> 3.9.0-alpha - 3.9.X # PyPy -> pypy-3.7 # 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 -m pip install build - name: Build sdist and wheel run: | python -m build - name: Publish package files if: always() uses: actions/upload-artifact@v4 with: name: packages path: dist/* if-no-files-found: error test: name: Test ${{ matrix.os.name }} ${{ matrix.python.name }} needs: - build runs-on: ${{ matrix.os.runs-on }} strategy: fail-fast: false matrix: os: - name: 🐧 runs-on: ubuntu-latest - name: 🍎 # This would preferably be macos-latest, but it has pip updating bugs right now. runs-on: macos-12 - 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' # Failing due to error using temporary file in a unittest. I think it's a PyPy bug, not a bitstring one! # - name: PyPy 3.8 # major_dot_minor: '3.8' # action: 'pypy-3.8' 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: # This allows the matrix to specify just the major.minor version while still # expanding it to get the latest patch version including alpha releases. # This avoids the need to update for each new alpha, beta, release candidate, # and then finally an actual release version. actions/setup-python doesn't # support this for PyPy presently so we get no help there. # # CPython -> 3.9.0-alpha - 3.9.X # PyPy -> pypy-3.7 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 run: | python -m pytest tests/ --benchmark-disable all: name: All successful runs-on: ubuntu-latest # The always() part is very important. # If not set, the job will be skipped on failing dependencies. if: always() needs: # This is the list of CI job that we are interested to be green before # a merge. - build - test steps: - name: Require all successes uses: re-actors/alls-green@v1.2.2 with: jobs: ${{ toJSON(needs) }} bitstring-bitstring-4.2.3/.gitignore000066400000000000000000000001001462444752600175250ustar00rootroot00000000000000venv .git *cache* .idea build dist manifest *egg-info doc/_buildbitstring-bitstring-4.2.3/.readthedocs.yaml000066400000000000000000000013011462444752600207700ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # You can also specify other tool versions: # nodejs: "16" # rust: "1.55" # golang: "1.17" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/conf.py # If using Sphinx, optionally build your docs in additional formats such as PDF #formats: # - pdf # Optionally declare the Python requirements required to build your docs python: install: - requirements: doc/requirements.txtbitstring-bitstring-4.2.3/LICENSE000066400000000000000000000021221462444752600165500ustar00rootroot00000000000000The MIT License Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bitstring-bitstring-4.2.3/MANIFEST.in000066400000000000000000000002551462444752600173060ustar00rootroot00000000000000include tests/test.m1v include tests/smalltestfile include tests/__init__.py include release_notes.md include README.md include bitstring/py.typed prune doc include LICENSE bitstring-bitstring-4.2.3/README.md000066400000000000000000000075701462444752600170360ustar00rootroot00000000000000 ![bitstring](https://raw.githubusercontent.com/scott-griffiths/bitstring/main/doc/bitstring_logo_small.png "bitstring") **bitstring** is a Python module to help make the creation and analysis of all types of bit-level binary data as simple and efficient as possible. It has been actively maintained since 2006. [![CI badge](https://github.com/scott-griffiths/bitstring/actions/workflows/.github/workflows/ci.yml/badge.svg)](https://github.com/scott-griffiths/bitstring/actions/workflows/ci.yml) [![Docs](https://img.shields.io/readthedocs/bitstring?logo=readthedocs&logoColor=white)](https://bitstring.readthedocs.io/en/latest/) [![Codacy Badge](https://img.shields.io/codacy/grade/8869499b2eed44548fa1a5149dd451f4?logo=codacy)](https://app.codacy.com/gh/scott-griffiths/bitstring/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/pypi/bitstring?logo=libraries.io&logoColor=white)](https://libraries.io/pypi/bitstring)     [![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://www.pepy.tech/projects/bitstring) [![PyPI - Downloads](https://img.shields.io/pypi/dm/bitstring?label=%40&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring) ---- > [!NOTE] > To see what been added, improved or fixed, and also to see what's coming in the next version, see the [release notes](https://github.com/scott-griffiths/bitstring/blob/main/release_notes.md). # Overview * Efficiently store and manipulate binary data in idiomatic Python. * Create bitstrings from hex, octal, binary, files, formatted strings, bytes, integers and floats of different endiannesses. * Powerful binary packing and unpacking functions. * Bit-level slicing, joining, searching, replacing and more. * Create and manipulate arrays of fixed-length bitstrings. * Read from and interpret bitstrings as streams of binary data. * Rich API - chances are that whatever you want to do there's a simple and elegant way of doing it. * Open source software, released under the MIT licence. # Documentation Extensive documentation for the bitstring module is available. Some starting points are given below: * [Overview](https://bitstring.readthedocs.io/en/stable/index.html) * [Quick Reference](https://bitstring.readthedocs.io/en/stable/quick_reference.html) * [Full Reference](https://bitstring.readthedocs.io/en/stable/reference.html) There is also an introductory walkthrough notebook on [binder](https://mybinder.org/v2/gh/scott-griffiths/bitstring/main?labpath=doc%2Fwalkthrough.ipynb). # Examples ### Installation ``` $ pip install bitstring ``` ### Creation ```pycon >>> from bitstring import Bits, BitArray, BitStream, pack >>> a = BitArray(bin='00101') >>> b = Bits(a_file_object) >>> c = BitArray('0xff, 0b101, 0o65, uint6=22') >>> d = pack('intle16, hex=a, 0b1', 100, a='0x34f') >>> e = pack('<16h', *range(16)) ``` ### Different interpretations, slicing and concatenation ```pycon >>> a = BitArray('0x3348') >>> a.hex, a.bin, a.uint, a.float, a.bytes ('3348', '0011001101001000', 13128, 0.2275390625, b'3H') >>> a[10:3:-1].bin '0101100' >>> '0b100' + 3*a BitArray('0x866906690669, 0b000') ``` ### Reading data sequentially ```pycon >>> b = BitStream('0x160120f') >>> b.read(12).hex '160' >>> b.pos = 0 >>> b.read('uint12') 352 >>> b.readlist('uint12, bin3') [288, '111'] ``` ### Searching, inserting and deleting ```pycon >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' >>> c.find('0x48') (8,) >>> c.replace('0b001', '0xabc') >>> c.insert('0b0000', pos=3) >>> del c[12:16] ``` ### Arrays of fixed-length formats ```pycon >>> from bitstring import Array >>> a = Array('uint7', [9, 100, 3, 1]) >>> a.data BitArray('0x1390181') >>> a[::2] *= 5 >>> a Array('uint7', [45, 100, 15, 1]) ``` Copyright (c) 2006 - 2024 Scott Griffiths bitstring-bitstring-4.2.3/bitstring/000077500000000000000000000000001462444752600175535ustar00rootroot00000000000000bitstring-bitstring-4.2.3/bitstring/__init__.py000066400000000000000000000331131462444752600216650ustar00rootroot00000000000000#!/usr/bin/env python r""" This package defines classes that simplify bit-wise creation, manipulation and interpretation of data. Classes: Bits -- An immutable container for binary data. BitArray -- A mutable container for binary data. ConstBitStream -- An immutable container with streaming methods. BitStream -- A mutable container with streaming methods. Array -- An efficient list-like container where each item has a fixed-length binary format. Dtype -- Encapsulate the data types used in the other classes. Functions: pack -- Create a BitStream from a format string. Data: options -- Module-wide options. Exceptions: Error -- Module exception base class. CreationError -- Error during creation. InterpretError -- Inappropriate interpretation of binary data. ByteAlignError -- Whole byte position or length needed. ReadError -- Reading or peeking past the end of a bitstring. https://github.com/scott-griffiths/bitstring """ __licence__ = """ The MIT License Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ __version__ = "4.2.3" __author__ = "Scott Griffiths" import sys from .bits import Bits from .bitstring_options import Options from .bitarray_ import BitArray from .bitstream import ConstBitStream, BitStream from .methods import pack from .array_ import Array from .exceptions import Error, ReadError, InterpretError, ByteAlignError, CreationError from .dtypes import DtypeDefinition, dtype_register, Dtype import types from typing import List, Tuple, Literal from .mxfp import decompress_luts as mxfp_decompress_luts from .fp8 import decompress_luts as binary8_decompress_luts # Decompress the LUTs for the exotic floating point formats mxfp_decompress_luts() binary8_decompress_luts() # The Options class returns a singleton. options = Options() # These get defined properly by the module magic below. This just stops mypy complaining about them. bytealigned = lsb0 = None # An opaque way of adding module level properties. Taken from https://peps.python.org/pep-0549/ # This is now deprecated. Use the options object directly instead. class _MyModuleType(types.ModuleType): @property def bytealigned(self) -> bool: """Determines whether a number of methods default to working only on byte boundaries.""" return options.bytealigned @bytealigned.setter def bytealigned(self, value: bool) -> None: """Determines whether a number of methods default to working only on byte boundaries.""" options.bytealigned = value @property def lsb0(self) -> bool: """If True, the least significant bit (the final bit) is indexed as bit zero.""" return options.lsb0 @lsb0.setter def lsb0(self, value: bool) -> None: """If True, the least significant bit (the final bit) is indexed as bit zero.""" options.lsb0 = value sys.modules[__name__].__class__ = _MyModuleType # These methods convert a bit length to the number of characters needed to print it for different interpretations. def hex_bits2chars(bitlength: int): # One character for every 4 bits return bitlength // 4 def oct_bits2chars(bitlength: int): # One character for every 3 bits return bitlength // 3 def bin_bits2chars(bitlength: int): # One character for each bit return bitlength def bytes_bits2chars(bitlength: int): # One character for every 8 bits return bitlength // 8 def uint_bits2chars(bitlength: int): # How many characters is largest possible int of this length? return len(str((1 << bitlength) - 1)) def int_bits2chars(bitlength: int): # How many characters is largest negative int of this length? (To include minus sign). return len(str((-1 << (bitlength - 1)))) def float_bits2chars(bitlength: Literal[16, 32, 64]): # These bit lengths were found by looking at lots of possible values if bitlength in [16, 32]: return 23 # Empirical value else: return 24 # Empirical value def p3binary_bits2chars(_: Literal[8]): return 19 # Empirical value def p4binary_bits2chars(_: Literal[8]): # Found by looking at all the possible values return 13 # Empirical value def e4m3mxfp_bits2chars(_: Literal[8]): return 13 def e5m2mxfp_bits2chars(_: Literal[8]): return 19 def e3m2mxfp_bits2chars(_: Literal[6]): # Not sure what the best value is here. It's 7 without considering the scale that could be applied. return 7 def e2m3mxfp_bits2chars(_: Literal[6]): # Not sure what the best value is here. return 7 def e2m1mxfp_bits2chars(_: Literal[4]): # Not sure what the best value is here. return 7 def e8m0mxfp_bits2chars(_: Literal[8]): # Has same range as float32 return 23 def mxint_bits2chars(_: Literal[8]): # Not sure what the best value is here. return 10 def bfloat_bits2chars(_: Literal[16]): # Found by looking at all the possible values return 23 # Empirical value def bits_bits2chars(bitlength: int): # For bits type we can see how long it needs to be printed by trying any value temp = Bits(bitlength) return len(str(temp)) def bool_bits2chars(_: Literal[1]): # Bools are printed as 1 or 0, not True or False, so are one character each return 1 dtype_definitions = [ # Integer types DtypeDefinition('uint', Bits._setuint, Bits._getuint, int, False, uint_bits2chars, description="a two's complement unsigned int"), DtypeDefinition('uintle', Bits._setuintle, Bits._getuintle, int, False, uint_bits2chars, allowed_lengths=(8, 16, 24, ...), description="a two's complement little-endian unsigned int"), DtypeDefinition('uintbe', Bits._setuintbe, Bits._getuintbe, int, False, uint_bits2chars, allowed_lengths=(8, 16, 24, ...), description="a two's complement big-endian unsigned int"), DtypeDefinition('int', Bits._setint, Bits._getint, int, True, int_bits2chars, description="a two's complement signed int"), DtypeDefinition('intle', Bits._setintle, Bits._getintle, int, True, int_bits2chars, allowed_lengths=(8, 16, 24, ...), description="a two's complement little-endian signed int"), DtypeDefinition('intbe', Bits._setintbe, Bits._getintbe, int, True, int_bits2chars, allowed_lengths=(8, 16, 24, ...), description="a two's complement big-endian signed int"), # String types DtypeDefinition('hex', Bits._sethex, Bits._gethex, str, False, hex_bits2chars, allowed_lengths=(0, 4, 8, ...), description="a hexadecimal string"), DtypeDefinition('bin', Bits._setbin_safe, Bits._getbin, str, False, bin_bits2chars, description="a binary string"), DtypeDefinition('oct', Bits._setoct, Bits._getoct, str, False, oct_bits2chars, allowed_lengths=(0, 3, 6, ...), description="an octal string"), # Float types DtypeDefinition('float', Bits._setfloatbe, Bits._getfloatbe, float, True, float_bits2chars, allowed_lengths=(16, 32, 64), description="a big-endian floating point number"), DtypeDefinition('floatle', Bits._setfloatle, Bits._getfloatle, float, True, float_bits2chars, allowed_lengths=(16, 32, 64), description="a little-endian floating point number"), DtypeDefinition('bfloat', Bits._setbfloatbe, Bits._getbfloatbe, float, True, bfloat_bits2chars, allowed_lengths=(16,), description="a 16 bit big-endian bfloat floating point number"), DtypeDefinition('bfloatle', Bits._setbfloatle, Bits._getbfloatle, float, True, bfloat_bits2chars, allowed_lengths=(16,), description="a 16 bit little-endian bfloat floating point number"), # Other known length types DtypeDefinition('bits', Bits._setbits, Bits._getbits, Bits, False, bits_bits2chars, description="a bitstring object"), DtypeDefinition('bool', Bits._setbool, Bits._getbool, bool, False, bool_bits2chars, allowed_lengths=(1,), description="a bool (True or False)"), DtypeDefinition('bytes', Bits._setbytes, Bits._getbytes, bytes, False, bytes_bits2chars, multiplier=8, description="a bytes object"), # Unknown length types DtypeDefinition('se', Bits._setse, Bits._getse, int, True, None, variable_length=True, description="a signed exponential-Golomb code"), DtypeDefinition('ue', Bits._setue, Bits._getue, int, False, None, variable_length=True, description="an unsigned exponential-Golomb code"), DtypeDefinition('sie', Bits._setsie, Bits._getsie, int, True, None, variable_length=True, description="a signed interleaved exponential-Golomb code"), DtypeDefinition('uie', Bits._setuie, Bits._getuie, int, False, None, variable_length=True, description="an unsigned interleaved exponential-Golomb code"), # Special case pad type DtypeDefinition('pad', Bits._setpad, Bits._getpad, None, False, None, description="a skipped section of padding"), # MXFP and IEEE 8-bit float types DtypeDefinition('p3binary', Bits._setp3binary, Bits._getp3binary, float, True, p3binary_bits2chars, allowed_lengths=(8,), description="an 8 bit float with binary8p3 format"), DtypeDefinition('p4binary', Bits._setp4binary, Bits._getp4binary, float, True, p4binary_bits2chars, allowed_lengths=(8,), description="an 8 bit float with binary8p4 format"), DtypeDefinition('e4m3mxfp', Bits._sete4m3mxfp, Bits._gete4m3mxfp, float, True, e4m3mxfp_bits2chars, allowed_lengths=(8,), description="an 8 bit float with MXFP E4M3 format"), DtypeDefinition('e5m2mxfp', Bits._sete5m2mxfp, Bits._gete5m2mxfp, float, True, e5m2mxfp_bits2chars, allowed_lengths=(8,), description="an 8 bit float with MXFP E5M2 format"), DtypeDefinition('e3m2mxfp', Bits._sete3m2mxfp, Bits._gete3m2mxfp, float, True, e3m2mxfp_bits2chars, allowed_lengths=(6,), description="a 6 bit float with MXFP E3M2 format"), DtypeDefinition('e2m3mxfp', Bits._sete2m3mxfp, Bits._gete2m3mxfp, float, True, e2m3mxfp_bits2chars, allowed_lengths=(6,), description="a 6 bit float with MXFP E2M3 format"), DtypeDefinition('e2m1mxfp', Bits._sete2m1mxfp, Bits._gete2m1mxfp, float, True, e2m1mxfp_bits2chars, allowed_lengths=(4,), description="a 4 bit float with MXFP E2M1 format"), DtypeDefinition('e8m0mxfp', Bits._sete8m0mxfp, Bits._gete8m0mxfp, float, False, e8m0mxfp_bits2chars, allowed_lengths=(8,), description="an 8 bit float with MXFP E8M0 format"), DtypeDefinition('mxint', Bits._setmxint, Bits._getmxint, float, True, mxint_bits2chars, allowed_lengths=(8,), description="an 8 bit float with MXFP INT8 format"), ] aliases: List[Tuple[str, str]] = [ # Floats default to big endian ('float', 'floatbe'), ('bfloat', 'bfloatbe'), # Some single letter aliases for popular types ('int', 'i'), ('uint', 'u'), ('hex', 'h'), ('oct', 'o'), ('bin', 'b'), ('float', 'f'), ] # Create native-endian aliases depending on the byteorder of the system byteorder: str = sys.byteorder if byteorder == 'little': aliases.extend([ ('uintle', 'uintne'), ('intle', 'intne'), ('floatle', 'floatne'), ('bfloatle', 'bfloatne'), ]) else: aliases.extend([ ('uintbe', 'uintne'), ('intbe', 'intne'), ('floatbe', 'floatne'), ('bfloatbe', 'bfloatne'), ]) for dt in dtype_definitions: dtype_register.add_dtype(dt) for alias in aliases: dtype_register.add_dtype_alias(alias[0], alias[1]) property_docstrings = [f'{name} -- Interpret as {dtype_register[name].description}.' for name in dtype_register.names] property_docstring = '\n '.join(property_docstrings) # We can't be sure the docstrings are present, as it might be compiled without docstrings. if Bits.__doc__ is not None: Bits.__doc__ = Bits.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) if BitArray.__doc__ is not None: BitArray.__doc__ = BitArray.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) if ConstBitStream.__doc__ is not None: ConstBitStream.__doc__ = ConstBitStream.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) if BitStream.__doc__ is not None: BitStream.__doc__ = BitStream.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring) __all__ = ['ConstBitStream', 'BitStream', 'BitArray', 'Array', 'Bits', 'pack', 'Error', 'ReadError', 'InterpretError', 'ByteAlignError', 'CreationError', 'bytealigned', 'lsb0', 'Dtype', 'options'] bitstring-bitstring-4.2.3/bitstring/__main__.py000066400000000000000000000031721462444752600216500ustar00rootroot00000000000000import sys from bitstring.bits import Bits from bitstring.dtypes import Register dtype_register = Register() def main() -> None: # check if final parameter is an interpretation string fp = sys.argv[-1] if fp in ['-h', '--help'] or len(sys.argv) == 1: print("""Create and interpret a bitstring from command-line parameters. Command-line parameters are concatenated and a bitstring created from them. If the final parameter is either an interpretation string or ends with a '.' followed by an interpretation string then that interpretation of the bitstring will be used when printing it. Typical usage might be invoking the Python module from a console as a one-off calculation: $ python -m bitstring int:16=-400 0xfe70 $ python -m bitstring float:32=0.2 bin 00111110010011001100110011001101 $ python -m bitstring 0xff 3*0b01,0b11 uint 65367 $ python -m bitstring hex=01, uint:12=352.hex 01160 """) return if fp in dtype_register.names: # concatenate all other parameters and interpret using the final one b1 = Bits(','.join(sys.argv[1: -1])) print(b1._readtoken(fp, 0, b1.__len__())[0]) else: # does final parameter end with a dot then an interpretation string? interp = fp[fp.rfind('.') + 1:] if interp in dtype_register.names: sys.argv[-1] = fp[:fp.rfind('.')] b1 = Bits(','.join(sys.argv[1:])) print(b1._readtoken(interp, 0, b1.__len__())[0]) else: # No interpretation - just use default print b1 = Bits(','.join(sys.argv[1:])) print(b1) if __name__ == '__main__': main() bitstring-bitstring-4.2.3/bitstring/array_.py000066400000000000000000001065511462444752600214120ustar00rootroot00000000000000from __future__ import annotations import math import numbers from collections.abc import Sized from bitstring.exceptions import CreationError from typing import Union, List, Iterable, Any, Optional, BinaryIO, overload, TextIO from bitstring.bits import Bits, BitsType from bitstring.bitarray_ import BitArray from bitstring.dtypes import Dtype, dtype_register from bitstring import utils from bitstring.bitstring_options import Options, Colour import copy import array import operator import io import sys # The possible types stored in each element of the Array ElementType = Union[float, str, int, bytes, bool, Bits] options = Options() class Array: """Return an Array whose elements are initialised according to the fmt string. The dtype string can be typecode as used in the struct module or any fixed-length bitstring format. a = Array('>H', [1, 15, 105]) b = Array('int5', [-9, 0, 4]) The Array data is stored compactly as a BitArray object and the Array behaves very like a list of items of the given format. Both the Array data and fmt properties can be freely modified after creation. If the data length is not a multiple of the fmt length then the Array will have 'trailing_bits' which will prevent some methods from appending to the Array. Methods: append() -- Append a single item to the end of the Array. byteswap() -- Change byte endianness of all items. count() -- Count the number of occurences of a value. extend() -- Append new items to the end of the Array from an iterable. fromfile() -- Append items read from a file object. insert() -- Insert an item at a given position. pop() -- Remove and return an item. pp() -- Pretty print the Array. reverse() -- Reverse the order of all items. tobytes() -- Return Array data as bytes object, padding with zero bits at the end if needed. tofile() -- Write Array data to a file, padding with zero bits at the end if needed. tolist() -- Return Array items as a list. Special methods: Also available are the operators [], ==, !=, +, *, <<, >>, &, |, ^, plus the mutating operators [], +=, *=, <<=, >>=, &=, |=, ^=. Properties: data -- The BitArray binary data of the Array. Can be freely modified. dtype -- The format string or typecode. Can be freely modified. itemsize -- The length *in bits* of a single item. Read only. trailing_bits -- If the data length is not a multiple of the fmt length, this BitArray gives the leftovers at the end of the data. """ def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Array, array.array, Iterable, Bits, bytes, bytearray, memoryview, BinaryIO]] = None, trailing_bits: Optional[BitsType] = None) -> None: self.data = BitArray() if isinstance(dtype, Dtype) and dtype.scale == 'auto': if isinstance(initializer, (int, Bits, bytes, bytearray, memoryview, BinaryIO)): raise TypeError("An Array with an 'auto' scale factor can only be created from an iterable of values.") auto_scale = self._calculate_auto_scale(initializer, dtype.name, dtype.length) dtype = Dtype(dtype.name, dtype.length, scale=auto_scale) try: self._set_dtype(dtype) except ValueError as e: raise CreationError(e) if isinstance(initializer, numbers.Integral): self.data = BitArray(initializer * self._dtype.bitlength) elif isinstance(initializer, (Bits, bytes, bytearray, memoryview)): self.data += initializer elif isinstance(initializer, io.BufferedReader): self.fromfile(initializer) elif initializer is not None: self.extend(initializer) if trailing_bits is not None: self.data += BitArray._create_from_bitstype(trailing_bits) _largest_values = None @staticmethod def _calculate_auto_scale(initializer, name: str, length: Optional[int]) -> float: # Now need to find the largest power of 2 representable with this format. if Array._largest_values is None: Array._largest_values = { 'mxint8': Bits('0b01111111').mxint8, # 1.0 + 63.0/64.0, 'e2m1mxfp4': Bits('0b0111').e2m1mxfp4, # 6.0 'e2m3mxfp6': Bits('0b011111').e2m3mxfp6, # 7.5 'e3m2mxfp6': Bits('0b011111').e3m2mxfp6, # 28.0 'e4m3mxfp8': Bits('0b01111110').e4m3mxfp8, # 448.0 'e5m2mxfp8': Bits('0b01111011').e5m2mxfp8, # 57344.0 'p4binary8': Bits('0b01111110').p4binary8, # 224.0 'p3binary8': Bits('0b01111110').p3binary8, # 49152.0 'float16': Bits('0x7bff').float16, # 65504.0 # The bfloat range is so large the scaling algorithm doesn't work well, so I'm disallowing it. # 'bfloat16': Bits('0x7f7f').bfloat16, # 3.38953139e38, } if f'{name}{length}' in Array._largest_values.keys(): float_values = Array('float64', initializer).tolist() if not float_values: raise ValueError("Can't calculate an 'auto' scale with an empty Array initializer.") max_float_value = max(abs(x) for x in float_values) if max_float_value == 0: # This special case isn't covered in the standard. I'm choosing to return no scale. return 1.0 # We need to find the largest power of 2 that is less than the max value log2 = math.floor(math.log2(max_float_value)) lp2 = math.floor(math.log2(Array._largest_values[f'{name}{length}'])) lg_scale = log2 - lp2 # Saturate at values representable in E8M0 format. if lg_scale > 127: lg_scale = 127 elif lg_scale < -127: lg_scale = -127 return 2 ** lg_scale else: raise ValueError(f"Can't calculate auto scale for format '{name}{length}'. " f"This feature is only available for these formats: {list(Array._largest_values.keys())}.") @property def itemsize(self) -> int: return self._dtype.length @property def trailing_bits(self) -> BitArray: trailing_bit_length = len(self.data) % self._dtype.bitlength return BitArray() if trailing_bit_length == 0 else self.data[-trailing_bit_length:] @property def dtype(self) -> Dtype: return self._dtype @dtype.setter def dtype(self, new_dtype: Union[str, Dtype]) -> None: self._set_dtype(new_dtype) def _set_dtype(self, new_dtype: Union[str, Dtype]) -> None: if isinstance(new_dtype, Dtype): self._dtype = new_dtype else: try: dtype = Dtype(new_dtype) except ValueError: name_length = utils.parse_single_struct_token(new_dtype) if name_length is not None: dtype = Dtype(name_length[0], name_length[1]) else: raise ValueError(f"Inappropriate Dtype for Array: '{new_dtype}'.") if dtype.length is None: raise ValueError(f"A fixed length format is needed for an Array, received '{new_dtype}'.") self._dtype = dtype if self._dtype.scale == 'auto': raise ValueError("A Dtype with an 'auto' scale factor can only be used when creating a new Array.") def _create_element(self, value: ElementType) -> Bits: """Create Bits from value according to the token_name and token_length""" b = self._dtype.build(value) if len(b) != self._dtype.length: raise ValueError(f"The value {value!r} has the wrong length for the format '{self._dtype}'.") return b def __len__(self) -> int: return len(self.data) // self._dtype.length @overload def __getitem__(self, key: slice) -> Array: ... @overload def __getitem__(self, key: int) -> ElementType: ... def __getitem__(self, key: Union[slice, int]) -> Union[Array, ElementType]: if isinstance(key, slice): start, stop, step = key.indices(len(self)) if step != 1: d = BitArray() for s in range(start * self._dtype.length, stop * self._dtype.length, step * self._dtype.length): d.append(self.data[s: s + self._dtype.length]) a = self.__class__(self._dtype) a.data = d return a else: a = self.__class__(self._dtype) a.data = self.data[start * self._dtype.length: stop * self._dtype.length] return a else: if key < 0: key += len(self) if key < 0 or key >= len(self): raise IndexError(f"Index {key} out of range for Array of length {len(self)}.") return self._dtype.read_fn(self.data, start=self._dtype.length * key) @overload def __setitem__(self, key: slice, value: Iterable[ElementType]) -> None: ... @overload def __setitem__(self, key: int, value: ElementType) -> None: ... def __setitem__(self, key: Union[slice, int], value: Union[Iterable[ElementType], ElementType]) -> None: if isinstance(key, slice): start, stop, step = key.indices(len(self)) if not isinstance(value, Iterable): raise TypeError("Can only assign an iterable to a slice.") if step == 1: new_data = BitArray() for x in value: new_data += self._create_element(x) self.data[start * self._dtype.length: stop * self._dtype.length] = new_data return items_in_slice = len(range(start, stop, step)) if not isinstance(value, Sized): value = list(value) if len(value) == items_in_slice: for s, v in zip(range(start, stop, step), value): self.data.overwrite(self._create_element(v), s * self._dtype.length) else: raise ValueError(f"Can't assign {len(value)} values to an extended slice of length {items_in_slice}.") else: if key < 0: key += len(self) if key < 0 or key >= len(self): raise IndexError(f"Index {key} out of range for Array of length {len(self)}.") start = self._dtype.length * key self.data.overwrite(self._create_element(value), start) return def __delitem__(self, key: Union[slice, int]) -> None: if isinstance(key, slice): start, stop, step = key.indices(len(self)) if step == 1: self.data.__delitem__(slice(start * self._dtype.length, stop * self._dtype.length)) return # We need to delete from the end or the earlier positions will change r = reversed(range(start, stop, step)) if step > 0 else range(start, stop, step) for s in r: self.data.__delitem__(slice(s * self._dtype.length, (s + 1) * self._dtype.length)) else: if key < 0: key += len(self) if key < 0 or key >= len(self): raise IndexError start = self._dtype.length * key del self.data[start: start + self._dtype.length] def __repr__(self) -> str: list_str = f"{self.tolist()}" trailing_bit_length = len(self.data) % self._dtype.length final_str = "" if trailing_bit_length == 0 else ", trailing_bits=" + repr( self.data[-trailing_bit_length:]) return f"Array('{self._dtype}', {list_str}{final_str})" def astype(self, dtype: Union[str, Dtype]) -> Array: """Return Array with elements of new dtype, initialised from current Array.""" new_array = self.__class__(dtype, self.tolist()) return new_array def tolist(self) -> List[ElementType]: return [self._dtype.read_fn(self.data, start=start) for start in range(0, len(self.data) - self._dtype.length + 1, self._dtype.length)] def append(self, x: ElementType) -> None: if len(self.data) % self._dtype.length != 0: raise ValueError("Cannot append to Array as its length is not a multiple of the format length.") self.data += self._create_element(x) def extend(self, iterable: Union[Array, array.array, Iterable[Any]]) -> None: if len(self.data) % self._dtype.length != 0: raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).") if isinstance(iterable, Array): if self._dtype.name != iterable._dtype.name or self._dtype.length != iterable._dtype.length: raise TypeError( f"Cannot extend an Array with format '{self._dtype}' from an Array of format '{iterable._dtype}'.") # No need to iterate over the elements, we can just append the data self.data.append(iterable.data) elif isinstance(iterable, array.array): # array.array types are always native-endian, hence the '=' name_value = utils.parse_single_struct_token('=' + iterable.typecode) if name_value is None: raise ValueError(f"Cannot extend from array with typecode {iterable.typecode}.") other_dtype = dtype_register.get_dtype(*name_value, scale=None) if self._dtype.name != other_dtype.name or self._dtype.length != other_dtype.length: raise ValueError( f"Cannot extend an Array with format '{self._dtype}' from an array with typecode '{iterable.typecode}'.") self.data += iterable.tobytes() else: if isinstance(iterable, str): raise TypeError("Can't extend an Array with a str.") for item in iterable: self.data += self._create_element(item) def insert(self, i: int, x: ElementType) -> None: """Insert a new element into the Array at position i. """ i = min(i, len(self)) # Inserting beyond len of array inserts at the end (copying standard behaviour) self.data.insert(self._create_element(x), i * self._dtype.length) def pop(self, i: int = -1) -> ElementType: """Return and remove an element of the Array. Default is to return and remove the final element. """ if len(self) == 0: raise IndexError("Can't pop from an empty Array.") x = self[i] del self[i] return x def byteswap(self) -> None: """Change the endianness in-place of all items in the Array. If the Array format is not a whole number of bytes a ValueError will be raised. """ if self._dtype.length % 8 != 0: raise ValueError( f"byteswap can only be used for whole-byte elements. The '{self._dtype}' format is {self._dtype.length} bits long.") self.data.byteswap(self.itemsize // 8) def count(self, value: ElementType) -> int: """Return count of Array items that equal value. value -- The quantity to compare each Array element to. Type should be appropriate for the Array format. For floating point types using a value of float('nan') will count the number of elements that are NaN. """ if math.isnan(value): return sum(math.isnan(i) for i in self) else: return sum(i == value for i in self) def tobytes(self) -> bytes: """Return the Array data as a bytes object, padding with zero bits if needed. Up to seven zero bits will be added at the end to byte align. """ return self.data.tobytes() def tofile(self, f: BinaryIO) -> None: """Write the Array data to a file object, padding with zero bits if needed. Up to seven zero bits will be added at the end to byte align. """ self.data.tofile(f) def fromfile(self, f: BinaryIO, n: Optional[int] = None) -> None: trailing_bit_length = len(self.data) % self._dtype.bitlength if trailing_bit_length != 0: raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.bitlength} bits).") new_data = Bits(f) max_items = len(new_data) // self._dtype.length items_to_append = max_items if n is None else min(n, max_items) self.data += new_data[0: items_to_append * self._dtype.bitlength] if n is not None and items_to_append < n: raise EOFError(f"Only {items_to_append} were appended, not the {n} items requested.") def reverse(self) -> None: trailing_bit_length = len(self.data) % self._dtype.length if trailing_bit_length != 0: raise ValueError(f"Cannot reverse the items in the Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).") for start_bit in range(0, len(self.data) // 2, self._dtype.length): start_swap_bit = len(self.data) - start_bit - self._dtype.length temp = self.data[start_bit: start_bit + self._dtype.length] self.data[start_bit: start_bit + self._dtype.length] = self.data[ start_swap_bit: start_swap_bit + self._dtype.length] self.data[start_swap_bit: start_swap_bit + self._dtype.length] = temp def pp(self, fmt: Optional[str] = None, width: int = 120, show_offset: bool = True, stream: TextIO = sys.stdout) -> None: """Pretty-print the Array contents. fmt -- Data format string. Defaults to current Array dtype. width -- Max width of printed lines in characters. Defaults to 120. A single group will always be printed per line even if it exceeds the max width. show_offset -- If True shows the element offset in the first column of each line. stream -- A TextIO object with a write() method. Defaults to sys.stdout. """ colour = Colour(not options.no_color) sep = ' ' dtype2 = None tidy_fmt = None if fmt is None: fmt = self.dtype dtype1 = self.dtype tidy_fmt = "dtype='" + colour.purple + str(self.dtype) + "'" + colour.off else: token_list = utils.preprocess_tokens(fmt) if len(token_list) not in [1, 2]: raise ValueError(f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {len(token_list)} tokens.") name1, length1 = utils.parse_name_length_token(token_list[0]) dtype1 = Dtype(name1, length1) if len(token_list) == 2: name2, length2 = utils.parse_name_length_token(token_list[1]) dtype2 = Dtype(name2, length2) token_length = dtype1.bitlength if dtype2 is not None: # For two types we're OK as long as they don't have different lengths given. if dtype1.bitlength is not None and dtype2.bitlength is not None and dtype1.bitlength != dtype2.bitlength: raise ValueError(f"Two different format lengths specified ('{fmt}'). Either specify just one, or two the same length.") if token_length is None: token_length = dtype2.bitlength if token_length is None: token_length = self.itemsize trailing_bit_length = len(self.data) % token_length format_sep = " : " # String to insert on each line between multiple formats if tidy_fmt is None: tidy_fmt = colour.purple + str(dtype1) + colour.off if dtype2 is not None: tidy_fmt += ', ' + colour.blue + str(dtype2) + colour.off tidy_fmt = "fmt='" + tidy_fmt + "'" data = self.data if trailing_bit_length == 0 else self.data[0: -trailing_bit_length] length = len(self.data) // token_length len_str = colour.green + str(length) + colour.off stream.write(f"<{self.__class__.__name__} {tidy_fmt}, length={len_str}, itemsize={token_length} bits, total data size={(len(self.data) + 7) // 8} bytes> [\n") data._pp(dtype1, dtype2, token_length, width, sep, format_sep, show_offset, stream, False, token_length) stream.write("]") if trailing_bit_length != 0: stream.write(" + trailing_bits = " + str(self.data[-trailing_bit_length:])) stream.write("\n") def equals(self, other: Any) -> bool: """Return True if format and all Array items are equal.""" if isinstance(other, Array): if self._dtype.length != other._dtype.length: return False if self._dtype.name != other._dtype.name: return False if self.data != other.data: return False return True elif isinstance(other, array.array): # Assume we are comparing with an array type if self.trailing_bits: return False # array's itemsize is in bytes, not bits. if self.itemsize != other.itemsize * 8: return False if len(self) != len(other): return False if self.tolist() != other.tolist(): return False return True return False def __iter__(self) -> Iterable[ElementType]: start = 0 for _ in range(len(self)): yield self._dtype.read_fn(self.data, start=start) start += self._dtype.length def __copy__(self) -> Array: a_copy = self.__class__(self._dtype) a_copy.data = copy.copy(self.data) return a_copy def _apply_op_to_all_elements(self, op, value: Union[int, float, None], is_comparison: bool = False) -> Array: """Apply op with value to each element of the Array and return a new Array""" new_array = self.__class__('bool' if is_comparison else self._dtype) new_data = BitArray() failures = index = 0 msg = '' if value is not None: def partial_op(a): return op(a, value) else: def partial_op(a): return op(a) for i in range(len(self)): v = self._dtype.read_fn(self.data, start=self._dtype.length * i) try: new_data.append(new_array._create_element(partial_op(v))) except (CreationError, ZeroDivisionError, ValueError) as e: if failures == 0: msg = str(e) index = i failures += 1 if failures != 0: raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. " f'First error at index {index} was: "{msg}"') new_array.data = new_data return new_array def _apply_op_to_all_elements_inplace(self, op, value: Union[int, float]) -> Array: """Apply op with value to each element of the Array in place.""" # This isn't really being done in-place, but it's simpler and faster for now? new_data = BitArray() failures = index = 0 msg = '' for i in range(len(self)): v = self._dtype.read_fn(self.data, start=self._dtype.length * i) try: new_data.append(self._create_element(op(v, value))) except (CreationError, ZeroDivisionError, ValueError) as e: if failures == 0: msg = str(e) index = i failures += 1 if failures != 0: raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. " f'First error at index {index} was: "{msg}"') self.data = new_data return self def _apply_bitwise_op_to_all_elements(self, op, value: BitsType) -> Array: """Apply op with value to each element of the Array as an unsigned integer and return a new Array""" a_copy = self[:] a_copy._apply_bitwise_op_to_all_elements_inplace(op, value) return a_copy def _apply_bitwise_op_to_all_elements_inplace(self, op, value: BitsType) -> Array: """Apply op with value to each element of the Array as an unsigned integer in place.""" value = BitArray._create_from_bitstype(value) if len(value) != self._dtype.length: raise ValueError(f"Bitwise op needs a bitstring of length {self._dtype.length} to match format {self._dtype}.") for start in range(0, len(self) * self._dtype.length, self._dtype.length): self.data[start: start + self._dtype.length] = op(self.data[start: start + self._dtype.length], value) return self def _apply_op_between_arrays(self, op, other: Array, is_comparison: bool = False) -> Array: if len(self) != len(other): msg = f"Cannot operate element-wise on Arrays with different lengths ({len(self)} and {len(other)})." if op in [operator.add, operator.iadd]: msg += " Use extend() method to concatenate Arrays." if op in [operator.eq, operator.ne]: msg += " Use equals() method to compare Arrays for a single boolean result." raise ValueError(msg) if is_comparison: new_type = dtype_register.get_dtype('bool', 1) else: new_type = self._promotetype(self._dtype, other._dtype) new_array = self.__class__(new_type) new_data = BitArray() failures = index = 0 msg = '' for i in range(len(self)): a = self._dtype.read_fn(self.data, start=self._dtype.length * i) b = other._dtype.read_fn(other.data, start=other._dtype.length * i) try: new_data.append(new_array._create_element(op(a, b))) except (CreationError, ValueError, ZeroDivisionError) as e: if failures == 0: msg = str(e) index = i failures += 1 if failures != 0: raise ValueError(f"Applying operator '{op.__name__}' between Arrays caused {failures} errors. " f'First error at index {index} was: "{msg}"') new_array.data = new_data return new_array @classmethod def _promotetype(cls, type1: Dtype, type2: Dtype) -> Dtype: """When combining types which one wins? 1. We only deal with types representing floats or integers. 2. One of the two types gets returned. We never create a new one. 3. Floating point types always win against integer types. 4. Signed integer types always win against unsigned integer types. 5. Longer types win against shorter types. 6. In a tie the first type wins against the second type. """ def is_float(x): return x.return_type is float def is_int(x): return x.return_type is int or x.return_type is bool if is_float(type1) + is_int(type1) + is_float(type2) + is_int(type2) != 2: raise ValueError(f"Only integer and floating point types can be combined - not '{type1}' and '{type2}'.") # If same type choose the widest if type1.name == type2.name: return type1 if type1.length > type2.length else type2 # We choose floats above integers, irrespective of the widths if is_float(type1) and is_int(type2): return type1 if is_int(type1) and is_float(type2): return type2 if is_float(type1) and is_float(type2): return type2 if type2.length > type1.length else type1 assert is_int(type1) and is_int(type2) if type1.is_signed and not type2.is_signed: return type1 if type2.is_signed and not type1.is_signed: return type2 return type2 if type2.length > type1.length else type1 # Operators between Arrays or an Array and scalar value def __add__(self, other: Union[int, float, Array]) -> Array: """Add int or float to all elements.""" if isinstance(other, Array): return self._apply_op_between_arrays(operator.add, other) return self._apply_op_to_all_elements(operator.add, other) def __iadd__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.add, other) return self._apply_op_to_all_elements_inplace(operator.add, other) def __isub__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.sub, other) return self._apply_op_to_all_elements_inplace(operator.sub, other) def __sub__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.sub, other) return self._apply_op_to_all_elements(operator.sub, other) def __mul__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.mul, other) return self._apply_op_to_all_elements(operator.mul, other) def __imul__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.mul, other) return self._apply_op_to_all_elements_inplace(operator.mul, other) def __floordiv__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.floordiv, other) return self._apply_op_to_all_elements(operator.floordiv, other) def __ifloordiv__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.floordiv, other) return self._apply_op_to_all_elements_inplace(operator.floordiv, other) def __truediv__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.truediv, other) return self._apply_op_to_all_elements(operator.truediv, other) def __itruediv__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.truediv, other) return self._apply_op_to_all_elements_inplace(operator.truediv, other) def __rshift__(self, other: Union[int, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.rshift, other) return self._apply_op_to_all_elements(operator.rshift, other) def __lshift__(self, other: Union[int, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.lshift, other) return self._apply_op_to_all_elements(operator.lshift, other) def __irshift__(self, other: Union[int, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.rshift, other) return self._apply_op_to_all_elements_inplace(operator.rshift, other) def __ilshift__(self, other: Union[int, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.lshift, other) return self._apply_op_to_all_elements_inplace(operator.lshift, other) def __mod__(self, other: Union[int, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.mod, other) return self._apply_op_to_all_elements(operator.mod, other) def __imod__(self, other: Union[int, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.mod, other) return self._apply_op_to_all_elements_inplace(operator.mod, other) # Bitwise operators def __and__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements(operator.iand, other) def __iand__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements_inplace(operator.iand, other) def __or__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements(operator.ior, other) def __ior__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements_inplace(operator.ior, other) def __xor__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements(operator.ixor, other) def __ixor__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements_inplace(operator.ixor, other) # Reverse operators between a scalar value and an Array def __rmul__(self, other: Union[int, float]) -> Array: return self._apply_op_to_all_elements(operator.mul, other) def __radd__(self, other: Union[int, float]) -> Array: return self._apply_op_to_all_elements(operator.add, other) def __rsub__(self, other: Union[int, float]) -> Array: # i - A == (-A) + i neg = self._apply_op_to_all_elements(operator.neg, None) return neg._apply_op_to_all_elements(operator.add, other) # Reverse operators between a scalar and something that can be a BitArray. def __rand__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements(operator.iand, other) def __ror__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements(operator.ior, other) def __rxor__(self, other: BitsType) -> Array: return self._apply_bitwise_op_to_all_elements(operator.ixor, other) # Comparison operators def __lt__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.lt, other, is_comparison=True) return self._apply_op_to_all_elements(operator.lt, other, is_comparison=True) def __gt__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.gt, other, is_comparison=True) return self._apply_op_to_all_elements(operator.gt, other, is_comparison=True) def __ge__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.ge, other, is_comparison=True) return self._apply_op_to_all_elements(operator.ge, other, is_comparison=True) def __le__(self, other: Union[int, float, Array]) -> Array: if isinstance(other, Array): return self._apply_op_between_arrays(operator.le, other, is_comparison=True) return self._apply_op_to_all_elements(operator.le, other, is_comparison=True) def _eq_ne(self, op, other: Any) -> Array: if isinstance(other, (int, float, str, Bits)): return self._apply_op_to_all_elements(op, other, is_comparison=True) try: other = self.__class__(self.dtype, other) except: return NotImplemented finally: return self._apply_op_between_arrays(op, other, is_comparison=True) def __eq__(self, other: Any) -> Array: return self._eq_ne(operator.eq, other) def __ne__(self, other: Any) -> Array: return self._eq_ne(operator.ne, other) # Unary operators def __neg__(self): return self._apply_op_to_all_elements(operator.neg, None) def __abs__(self): return self._apply_op_to_all_elements(operator.abs, None)bitstring-bitstring-4.2.3/bitstring/bitarray_.py000066400000000000000000000534571462444752600221170ustar00rootroot00000000000000from __future__ import annotations import copy import numbers import re from collections import abc from typing import Union, List, Iterable, Any, Optional from bitstring import utils from bitstring.exceptions import CreationError, Error from bitstring.bits import Bits, BitsType, TBits import bitstring.dtypes class BitArray(Bits): """A container holding a mutable sequence of bits. Subclass of the immutable Bits class. Inherits all of its methods (except __hash__) and adds mutating methods. Mutating methods: append() -- Append a bitstring. byteswap() -- Change byte endianness in-place. clear() -- Remove all bits from the bitstring. insert() -- Insert a bitstring. invert() -- Flip bit(s) between one and zero. overwrite() -- Overwrite a section with a new bitstring. prepend() -- Prepend a bitstring. replace() -- Replace occurrences of one bitstring with another. reverse() -- Reverse bits in-place. rol() -- Rotate bits to the left. ror() -- Rotate bits to the right. set() -- Set bit(s) to 1 or 0. Methods inherited from Bits: all() -- Check if all specified bits are set to 1 or 0. any() -- Check if any of specified bits are set to 1 or 0. copy() -- Return a copy of the bitstring. count() -- Count the number of bits set to 1 or 0. cut() -- Create generator of constant sized chunks. endswith() -- Return whether the bitstring ends with a sub-string. find() -- Find a sub-bitstring in the current bitstring. findall() -- Find all occurrences of a sub-bitstring in the current bitstring. fromstring() -- Create a bitstring from a formatted string. join() -- Join bitstrings together using current bitstring. pp() -- Pretty print the bitstring. 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. Special methods: Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^. Properties: [GENERATED_PROPERTY_DESCRIPTIONS] len -- Length of the bitstring in bits. """ __slots__ = () # As BitArray objects are mutable, we shouldn't allow them to be hashed. __hash__: None = None def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, offset: Optional[int] = None, **kwargs) -> None: """Either specify an 'auto' initialiser: A string of comma separated tokens, an integer, a file object, a bytearray, a boolean iterable or another bitstring. Or initialise via **kwargs with one (and only one) of: bin -- binary string representation, e.g. '0b001010'. hex -- hexadecimal string representation, e.g. '0x2ef' oct -- octal string representation, e.g. '0o777'. bytes -- raw data as a bytes object, for example read from a binary file. int -- a signed integer. uint -- an unsigned integer. float / floatbe -- a big-endian floating point number. bool -- a boolean (True or False). se -- a signed exponential-Golomb code. ue -- an unsigned exponential-Golomb code. sie -- a signed interleaved exponential-Golomb code. uie -- an unsigned interleaved exponential-Golomb code. floatle -- a little-endian floating point number. floatne -- a native-endian floating point number. bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. bfloatle -- a little-endian bfloat format 16-bit floating point number. bfloatne -- a native-endian bfloat format 16-bit floating point number. intbe -- a signed big-endian whole byte integer. intle -- a signed little-endian whole byte integer. intne -- a signed native-endian whole byte integer. uintbe -- an unsigned big-endian whole byte integer. uintle -- an unsigned little-endian whole byte integer. uintne -- an unsigned native-endian whole byte integer. filename -- the path of a file which will be opened in binary read-only mode. Other keyword arguments: length -- length of the bitstring in bits, if needed and appropriate. It must be supplied for all integer and float initialisers. offset -- bit offset to the data. These offset bits are ignored and this is intended for use when initialising using 'bytes' or 'filename'. """ if self._bitstore.immutable: self._bitstore = self._bitstore._copy() self._bitstore.immutable = False def copy(self: TBits) -> TBits: """Return a copy of the bitstring.""" return self.__copy__() def __setattr__(self, attribute, value) -> None: try: # First try the ordinary attribute setter super().__setattr__(attribute, value) except AttributeError: dtype = bitstring.dtypes.Dtype(attribute) x = object.__new__(Bits) if (set_fn := dtype.set_fn) is None: raise AttributeError(f"Cannot set attribute '{attribute}' as it does not have a set_fn.") set_fn(x, value) if len(x) != dtype.bitlength: raise CreationError(f"Can't initialise with value of length {len(x)} bits, " f"as attribute has length of {dtype.bitlength} bits.") self._bitstore = x._bitstore return def __iadd__(self, bs: BitsType) -> BitArray: """Append bs to current bitstring. Return self. bs -- the bitstring to append. """ self._append(bs) return self def __copy__(self) -> BitArray: """Return a new copy of the BitArray.""" s_copy = BitArray() s_copy._bitstore = self._bitstore._copy() assert s_copy._bitstore.immutable is False return s_copy def _setitem_int(self, key: int, value: Union[BitsType, int]) -> None: if isinstance(value, numbers.Integral): if value == 0: self._bitstore[key] = 0 return if value in (1, -1): self._bitstore[key] = 1 return raise ValueError(f"Cannot set a single bit with integer {value}.") try: value = self._create_from_bitstype(value) except TypeError: raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.") positive_key = key + len(self) if key < 0 else key if positive_key < 0 or positive_key >= len(self._bitstore): raise IndexError(f"Bit position {key} out of range.") self._bitstore[positive_key: positive_key + 1] = value._bitstore def _setitem_slice(self, key: slice, value: BitsType) -> None: if isinstance(value, numbers.Integral): value = int(value) if key.step not in [None, -1, 1]: if value in [0, 1]: self.set(value, range(*key.indices(len(self)))) return else: raise ValueError("Can't assign an integer except 0 or 1 to a slice with a step value.") # To find the length we first get the slice s = self._bitstore.getslice(key.start, key.stop) length = len(s) # Now create an int of the correct length if value >= 0: value = self.__class__(uint=value, length=length) else: value = self.__class__(int=value, length=length) else: try: value = self._create_from_bitstype(value) except TypeError: raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.") self._bitstore.__setitem__(key, value._bitstore) def __setitem__(self, key: Union[slice, int], value: BitsType) -> None: if isinstance(key, numbers.Integral): self._setitem_int(int(key), value) else: self._setitem_slice(key, value) def __delitem__(self, key: Union[slice, int]) -> None: """Delete item or range. >>> a = BitArray('0x001122') >>> del a[8:16] >>> print a 0x0022 """ self._bitstore.__delitem__(key) return def __ilshift__(self: TBits, n: int) -> TBits: """Shift bits by n to the left in place. Return self. n -- the number of bits to shift. Must be >= 0. """ if n < 0: raise ValueError("Cannot shift by a negative amount.") if not len(self): raise ValueError("Cannot shift an empty bitstring.") if not n: return self n = min(n, len(self)) return self._ilshift(n) def __irshift__(self: TBits, n: int) -> TBits: """Shift bits by n to the right in place. Return self. n -- the number of bits to shift. Must be >= 0. """ if n < 0: raise ValueError("Cannot shift by a negative amount.") if not len(self): raise ValueError("Cannot shift an empty bitstring.") if not n: return self n = min(n, len(self)) return self._irshift(n) def __imul__(self: TBits, n: int) -> TBits: """Concatenate n copies of self in place. Return self. Called for expressions of the form 'a *= 3'. n -- The number of concatenations. Must be >= 0. """ if n < 0: raise ValueError("Cannot multiply by a negative integer.") return self._imul(n) def __ior__(self: TBits, bs: BitsType) -> TBits: bs = self._create_from_bitstype(bs) self._bitstore |= bs._bitstore return self def __iand__(self: TBits, bs: BitsType) -> TBits: bs = self._create_from_bitstype(bs) self._bitstore &= bs._bitstore return self def __ixor__(self: TBits, bs: BitsType) -> TBits: bs = self._create_from_bitstype(bs) self._bitstore ^= bs._bitstore return self def _replace(self, old: Bits, new: Bits, start: int, end: int, count: int, bytealigned: Optional[bool]) -> int: if bytealigned is None: bytealigned = bitstring.options.bytealigned # First find all the places where we want to do the replacements starting_points: List[int] = [] for x in self.findall(old, start, end, bytealigned=bytealigned): if not starting_points: starting_points.append(x) elif x >= starting_points[-1] + len(old): # Can only replace here if it hasn't already been replaced! starting_points.append(x) if count != 0 and len(starting_points) == count: break if not starting_points: return 0 replacement_list = [self._bitstore.getslice(0, starting_points[0])] for i in range(len(starting_points) - 1): replacement_list.append(new._bitstore) replacement_list.append( self._bitstore.getslice(starting_points[i] + len(old), starting_points[i + 1])) # Final replacement replacement_list.append(new._bitstore) replacement_list.append(self._bitstore.getslice(starting_points[-1] + len(old), None)) if bitstring.options.lsb0: # Addition of bitarray is always on the right, so assemble from other end replacement_list.reverse() self._bitstore.clear() for r in replacement_list: self._bitstore += r return len(starting_points) def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int: """Replace all occurrences of old with new in place. Returns number of replacements made. old -- The bitstring to replace. new -- The replacement bitstring. start -- Any occurrences that start before this will not be replaced. Defaults to 0. end -- Any occurrences that finish after this will not be replaced. Defaults to len(self). count -- The maximum number of replacements to make. Defaults to replace all occurrences. bytealigned -- If True replacements will only be made on byte boundaries. Raises ValueError if old is empty or if start or end are out of range. """ if count == 0: return 0 old = self._create_from_bitstype(old) new = self._create_from_bitstype(new) if len(old) == 0: raise ValueError("Empty bitstring cannot be replaced.") start, end = self._validate_slice(start, end) if new is self: # Prevent self assignment woes new = copy.copy(self) return self._replace(old, new, start, end, 0 if count is None else count, bytealigned) def insert(self, bs: BitsType, pos: int) -> None: """Insert bs at bit position pos. bs -- The bitstring to insert. pos -- The bit position to insert at. Raises ValueError if pos < 0 or pos > len(self). """ bs = self._create_from_bitstype(bs) if len(bs) == 0: return if bs is self: bs = self._copy() if pos < 0: pos += len(self) if not 0 <= pos <= len(self): raise ValueError("Invalid insert position.") self._insert(bs, pos) def overwrite(self, bs: BitsType, pos: int) -> None: """Overwrite with bs at bit position pos. bs -- The bitstring to overwrite with. pos -- The bit position to begin overwriting from. Raises ValueError if pos < 0 or pos > len(self). """ bs = self._create_from_bitstype(bs) if len(bs) == 0: return if pos < 0: pos += len(self) if pos < 0 or pos > len(self): raise ValueError("Overwrite starts outside boundary of bitstring.") self._overwrite(bs, pos) def append(self, bs: BitsType) -> None: """Append a bitstring to the current bitstring. bs -- The bitstring to append. """ self._append(bs) def prepend(self, bs: BitsType) -> None: """Prepend a bitstring to the current bitstring. bs -- The bitstring to prepend. """ self._prepend(bs) def _append_msb0(self, bs: BitsType) -> None: self._addright(self._create_from_bitstype(bs)) def _append_lsb0(self, bs: BitsType) -> None: bs = self._create_from_bitstype(bs) self._addleft(bs) def reverse(self, start: Optional[int] = None, end: Optional[int] = None) -> None: """Reverse bits in-place. start -- Position of first bit to reverse. Defaults to 0. end -- One past the position of the last bit to reverse. Defaults to len(self). Using on an empty bitstring will have no effect. Raises ValueError if start < 0, end > len(self) or end < start. """ start, end = self._validate_slice(start, end) if start == 0 and end == len(self): self._bitstore.reverse() return s = self._slice(start, end) s._bitstore.reverse() self[start:end] = s def set(self, value: Any, pos: Optional[Union[int, Iterable[int]]] = None) -> None: """Set one or many bits to 1 or 0. value -- If bool(value) is True bits are set to 1, otherwise they are set to 0. pos -- Either a single bit position or an iterable of bit positions. Negative numbers are treated in the same way as slice indices. Defaults to the entire bitstring. Raises IndexError if pos < -len(self) or pos >= len(self). """ if pos is None: # Set all bits to either 1 or 0 self._setint(-1 if value else 0) return if not isinstance(pos, abc.Iterable): pos = (pos,) v = 1 if value else 0 if isinstance(pos, range): self._bitstore.__setitem__(slice(pos.start, pos.stop, pos.step), v) return for p in pos: self._bitstore[p] = v def invert(self, pos: Optional[Union[Iterable[int], int]] = None) -> None: """Invert one or many bits from 0 to 1 or vice versa. pos -- Either a single bit position or an iterable of bit positions. Negative numbers are treated in the same way as slice indices. Raises IndexError if pos < -len(self) or pos >= len(self). """ if pos is None: self._invert_all() return if not isinstance(pos, abc.Iterable): pos = (pos,) length = len(self) for p in pos: if p < 0: p += length if not 0 <= p < length: raise IndexError(f"Bit position {p} out of range.") self._invert(p) def ror(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: """Rotate bits to the right in-place. bits -- The number of bits to rotate by. start -- Start of slice to rotate. Defaults to 0. end -- End of slice to rotate. Defaults to len(self). Raises ValueError if bits < 0. """ if not len(self): raise Error("Cannot rotate an empty bitstring.") if bits < 0: raise ValueError("Cannot rotate by negative amount.") self._ror(bits, start, end) def _ror_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: start, end = self._validate_slice(start, end) # the _slice deals with msb0/lsb0 bits %= (end - start) if not bits: return rhs = self._slice(end - bits, end) self._delete(bits, end - bits) self._insert(rhs, start) def rol(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None: """Rotate bits to the left in-place. bits -- The number of bits to rotate by. start -- Start of slice to rotate. Defaults to 0. end -- End of slice to rotate. Defaults to len(self). Raises ValueError if bits < 0. """ if not len(self): raise Error("Cannot rotate an empty bitstring.") if bits < 0: raise ValueError("Cannot rotate by negative amount.") self._rol(bits, start, end) def _rol_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None): start, end = self._validate_slice(start, end) bits %= (end - start) if bits == 0: return lhs = self._slice(start, start + bits) self._delete(bits, start) self._insert(lhs, end - bits) def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]] = None, start: Optional[int] = None, end: Optional[int] = None, repeat: bool = True) -> int: """Change the endianness in-place. Return number of repeats of fmt done. fmt -- A compact structure string, an integer number of bytes or an iterable of integers. Defaults to 0, which byte reverses the whole bitstring. start -- Start bit position, defaults to 0. end -- End bit position, defaults to len(self). repeat -- If True (the default) the byte swapping pattern is repeated as much as possible. """ start_v, end_v = self._validate_slice(start, end) if fmt is None or fmt == 0: # reverse all of the whole bytes. bytesizes = [(end_v - start_v) // 8] elif isinstance(fmt, numbers.Integral): if fmt < 0: raise ValueError(f"Improper byte length {fmt}.") bytesizes = [fmt] elif isinstance(fmt, str): if not (m := utils.BYTESWAP_STRUCT_PACK_RE.match(fmt)): raise ValueError(f"Cannot parse format string {fmt}.") # Split the format string into a list of 'q', '4h' etc. formatlist = re.findall(utils.STRUCT_SPLIT_RE, m.group('fmt')) # Now deal with multiplicative factors, 4h -> hhhh etc. bytesizes = [] for f in formatlist: if len(f) == 1: bytesizes.append(utils.PACK_CODE_SIZE[f]) else: bytesizes.extend([utils.PACK_CODE_SIZE[f[-1]]] * int(f[:-1])) elif isinstance(fmt, abc.Iterable): bytesizes = fmt for bytesize in bytesizes: if not isinstance(bytesize, numbers.Integral) or bytesize < 0: raise ValueError(f"Improper byte length {bytesize}.") else: raise TypeError("Format must be an integer, string or iterable.") repeats = 0 totalbitsize: int = 8 * sum(bytesizes) if not totalbitsize: return 0 if repeat: # Try to repeat up to the end of the bitstring. finalbit = end_v else: # Just try one (set of) byteswap(s). finalbit = start_v + totalbitsize for patternend in range(start_v + totalbitsize, finalbit + 1, totalbitsize): bytestart = patternend - totalbitsize for bytesize in bytesizes: byteend = bytestart + bytesize * 8 self._reversebytes(bytestart, byteend) bytestart += bytesize * 8 repeats += 1 return repeats def clear(self) -> None: """Remove all bits, reset to zero length.""" self._clear() bitstring-bitstring-4.2.3/bitstring/bits.py000066400000000000000000002257221462444752600211000ustar00rootroot00000000000000from __future__ import annotations import numbers import pathlib import sys import mmap import struct import array import io from collections import abc import functools from typing import Tuple, Union, List, Iterable, Any, Optional, BinaryIO, TextIO, overload, Iterator, Type, TypeVar import bitarray import bitarray.util import bitstring from bitstring.bitstore import BitStore from bitstring import bitstore_helpers, utils from bitstring.dtypes import Dtype, dtype_register from bitstring.fp8 import p4binary_fmt, p3binary_fmt from bitstring.mxfp import e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt, e5m2mxfp_saturate_fmt from bitstring.bitstring_options import Colour # 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] TBits = TypeVar("TBits", bound='Bits') # Maximum number of digits to use in __str__ and __repr__. MAX_CHARS: int = 250 class Bits: """A container holding an immutable sequence of bits. For a mutable container use the BitArray class instead. Methods: all() -- Check if all specified bits are set to 1 or 0. any() -- Check if any of specified bits are set to 1 or 0. copy() - Return a copy of the bitstring. count() -- Count the number of bits set to 1 or 0. cut() -- Create generator of constant sized chunks. endswith() -- Return whether the bitstring ends with a sub-string. find() -- Find a sub-bitstring in the current bitstring. findall() -- Find all occurrences of a sub-bitstring in the current bitstring. fromstring() -- Create a bitstring from a formatted string. join() -- Join bitstrings together using current bitstring. pp() -- Pretty print the bitstring. 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. Special methods: Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. Properties: [GENERATED_PROPERTY_DESCRIPTIONS] len -- Length of the bitstring in bits. """ __slots__ = ('_bitstore', '_filename') def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, offset: Optional[int] = None, **kwargs) -> None: """Either specify an 'auto' initialiser: A string of comma separated tokens, an integer, a file object, a bytearray, a boolean iterable, an array or another bitstring. Or initialise via **kwargs with one (and only one) of: bin -- binary string representation, e.g. '0b001010'. hex -- hexadecimal string representation, e.g. '0x2ef' oct -- octal string representation, e.g. '0o777'. bytes -- raw data as a bytes object, for example read from a binary file. int -- a signed integer. uint -- an unsigned integer. float / floatbe -- a big-endian floating point number. bool -- a boolean (True or False). se -- a signed exponential-Golomb code. ue -- an unsigned exponential-Golomb code. sie -- a signed interleaved exponential-Golomb code. uie -- an unsigned interleaved exponential-Golomb code. floatle -- a little-endian floating point number. floatne -- a native-endian floating point number. bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. bfloatle -- a little-endian bfloat format 16-bit floating point number. bfloatne -- a native-endian bfloat format 16-bit floating point number. intbe -- a signed big-endian whole byte integer. intle -- a signed little-endian whole byte integer. intne -- a signed native-endian whole byte integer. uintbe -- an unsigned big-endian whole byte integer. uintle -- an unsigned little-endian whole byte integer. uintne -- an unsigned native-endian whole byte integer. filename -- the path of a file which will be opened in binary read-only mode. Other keyword arguments: length -- length of the bitstring in bits, if needed and appropriate. It must be supplied for all integer and float initialisers. offset -- bit offset to the data. These offset bits are ignored and this is mainly intended for use when initialising using 'bytes' or 'filename'. """ self._bitstore.immutable = True def __new__(cls: Type[TBits], auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, offset: Optional[int] = None, pos: Optional[int] = None, **kwargs) -> TBits: 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 = BitStore(length) x._bitstore.setall(0) else: x._bitstore = BitStore() return x x._initialise(auto, length, offset, **kwargs) return x @classmethod def _create_from_bitstype(cls: Type[TBits], auto: BitsType, /) -> TBits: if isinstance(auto, cls): return auto b = super().__new__(cls) b._setauto_no_length_or_offset(auto) return b def _initialise(self, auto: Any, /, length: Optional[int], offset: Optional[int], **kwargs) -> None: if auto is not None: if isinstance(auto, numbers.Integral): # Initialise with s zero bits. if auto < 0: raise bitstring.CreationError(f"Can't create bitstring of negative length {auto}.") self._bitstore = BitStore(int(auto)) self._bitstore.setall(0) return self._setauto(auto, length, offset) return k, v = kwargs.popitem() if k == 'bytes': # Special case for bytes as we want to allow offsets and lengths to work only on creation. self._setbytes_with_truncation(v, length, offset) return if k == 'filename': self._setfile(v, length, offset) return if k == 'bitarray': self._setbitarray(v, length, offset) return if k == 'auto': raise bitstring.CreationError( f"The 'auto' parameter should not be given explicitly - just use the first positional argument. " f"Instead of '{self.__class__.__name__}(auto=x)' use '{self.__class__.__name__}(x)'.") if offset is not None: raise bitstring.CreationError("offset cannot be used when initialising with '{k}'.") try: Dtype(k, length).set_fn(self, v) except ValueError as e: raise bitstring.CreationError(e) def __getattr__(self, attribute: str) -> Any: # Support for arbitrary attributes like u16 or f64. try: d = Dtype(attribute) except ValueError: raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attribute}'.") if d.bitlength is not None and len(self) != d.bitlength: raise ValueError(f"bitstring length {len(self)} doesn't match length {d.bitlength} of property '{attribute}'.") return d.get_fn(self) def __iter__(self) -> Iterable[bool]: return iter(self._bitstore) def __copy__(self: TBits) -> TBits: """Return a new copy of the Bits for the copy module.""" # Note that if you want a new copy (different ID), use _copy instead. # The copy can return self as it's immutable. return self def __lt__(self, other: Any) -> bool: # bitstrings can't really be ordered. return NotImplemented def __gt__(self, other: Any) -> bool: return NotImplemented def __le__(self, other: Any) -> bool: return NotImplemented def __ge__(self, other: Any) -> bool: return NotImplemented def __add__(self: TBits, bs: BitsType) -> TBits: """Concatenate bitstrings and return new bitstring. bs -- the bitstring to append. """ bs = self.__class__._create_from_bitstype(bs) s = self._copy() if len(bs) <= len(self) else bs._copy() if len(bs) <= len(self): s._addright(bs) else: s._addleft(self) return s def __radd__(self: TBits, bs: BitsType) -> TBits: """Append current bitstring to bs and return new bitstring. bs -- An object that can be 'auto' initialised as a bitstring that will be appended to. """ bs = self.__class__._create_from_bitstype(bs) return bs.__add__(self) @overload def __getitem__(self: TBits, key: slice, /) -> TBits: ... @overload def __getitem__(self, key: int, /) -> bool: ... def __getitem__(self: TBits, key: Union[slice, int], /) -> Union[TBits, bool]: """Return a new bitstring representing a slice of the current bitstring. Indices are in units of the step parameter (default 1 bit). Stepping is used to specify the number of bits in each item. >>> print(BitArray('0b00110')[1:4]) '0b011' >>> print(BitArray('0x00112233')[1:3:8]) '0x1122' """ if isinstance(key, numbers.Integral): return bool(self._bitstore.getindex(key)) bs = super().__new__(self.__class__) bs._bitstore = self._bitstore.getslice_withstep(key) return bs def __len__(self) -> int: """Return the length of the bitstring in bits.""" return self._getlength() def __bytes__(self) -> bytes: return self.tobytes() def __str__(self) -> str: """Return approximate string representation of bitstring for printing. Short strings will be given wholly in hexadecimal or binary. Longer strings may be part hexadecimal and part binary. Very long strings will be truncated with '...'. """ length = len(self) if not length: return '' if length > MAX_CHARS * 4: # Too long for hex. Truncate... return ''.join(('0x', self[0:MAX_CHARS*4]._gethex(), '...')) # If it's quite short and we can't do hex then use bin if length < 32 and length % 4 != 0: return '0b' + self.bin # If we can use hex then do so if not length % 4: return '0x' + self.hex # Otherwise first we do as much as we can in hex # then add on 1, 2 or 3 bits on at the end bits_at_end = length % 4 return ''.join(('0x', self[0:length - bits_at_end]._gethex(), ', ', '0b', self[length - bits_at_end:]._getbin())) def _repr(self, classname: str, length: int, pos: int): pos_string = f', pos={pos}' if pos else '' if hasattr(self, '_filename') and self._filename: return f"{classname}(filename={self._filename!r}, length={length}{pos_string})" else: s = self.__str__() lengthstring = '' if s.endswith('...'): lengthstring = f' # length={length}' return f"{classname}('{s}'{pos_string}){lengthstring}" def __repr__(self) -> str: """Return representation that could be used to recreate the bitstring. If the returned string is too long it will be truncated. See __str__(). """ return self._repr(self.__class__.__name__, len(self), 0) def __eq__(self, bs: Any, /) -> bool: """Return True if two bitstrings have the same binary representation. >>> BitArray('0b1110') == '0xe' True """ try: return self._bitstore == Bits._create_from_bitstype(bs)._bitstore except TypeError: return False def __ne__(self, bs: Any, /) -> bool: """Return False if two bitstrings have the same binary representation. >>> BitArray('0b111') == '0x7' False """ return not self.__eq__(bs) def __invert__(self: TBits) -> TBits: """Return bitstring with every bit inverted. Raises Error if the bitstring is empty. """ if len(self) == 0: raise bitstring.Error("Cannot invert empty bitstring.") s = self._copy() s._invert_all() return s def __lshift__(self: TBits, n: int, /) -> TBits: """Return bitstring with bits shifted by n to the left. n -- the number of bits to shift. Must be >= 0. """ if n < 0: raise ValueError("Cannot shift by a negative amount.") if len(self) == 0: raise ValueError("Cannot shift an empty bitstring.") n = min(n, len(self)) s = self._absolute_slice(n, len(self)) s._addright(Bits(n)) return s def __rshift__(self: TBits, n: int, /) -> TBits: """Return bitstring with bits shifted by n to the right. n -- the number of bits to shift. Must be >= 0. """ if n < 0: raise ValueError("Cannot shift by a negative amount.") if len(self) == 0: raise ValueError("Cannot shift an empty bitstring.") if not n: return self._copy() s = self.__class__(length=min(n, len(self))) n = min(n, len(self)) s._addright(self._absolute_slice(0, len(self) - n)) return s def __mul__(self: TBits, n: int, /) -> TBits: """Return bitstring consisting of n concatenations of self. Called for expression of the form 'a = b*3'. n -- The number of concatenations. Must be >= 0. """ if n < 0: raise ValueError("Cannot multiply by a negative integer.") if not n: return self.__class__() s = self._copy() s._imul(n) return s def __rmul__(self: TBits, n: int, /) -> TBits: """Return bitstring consisting of n concatenations of self. Called for expressions of the form 'a = 3*b'. n -- The number of concatenations. Must be >= 0. """ return self.__mul__(n) def __and__(self: TBits, bs: BitsType, /) -> TBits: """Bit-wise 'and' between two bitstrings. Returns new bitstring. bs -- The bitstring to '&' with. Raises ValueError if the two bitstrings have differing lengths. """ if bs is self: return self.copy() bs = Bits._create_from_bitstype(bs) s = object.__new__(self.__class__) s._bitstore = self._bitstore & bs._bitstore return s def __rand__(self: TBits, bs: BitsType, /) -> TBits: """Bit-wise 'and' between two bitstrings. Returns new bitstring. bs -- the bitstring to '&' with. Raises ValueError if the two bitstrings have differing lengths. """ return self.__and__(bs) def __or__(self: TBits, bs: BitsType, /) -> TBits: """Bit-wise 'or' between two bitstrings. Returns new bitstring. bs -- The bitstring to '|' with. Raises ValueError if the two bitstrings have differing lengths. """ if bs is self: return self.copy() bs = Bits._create_from_bitstype(bs) s = object.__new__(self.__class__) s._bitstore = self._bitstore | bs._bitstore return s def __ror__(self: TBits, bs: BitsType, /) -> TBits: """Bit-wise 'or' between two bitstrings. Returns new bitstring. bs -- The bitstring to '|' with. Raises ValueError if the two bitstrings have differing lengths. """ return self.__or__(bs) def __xor__(self: TBits, bs: BitsType, /) -> TBits: """Bit-wise 'xor' between two bitstrings. Returns new bitstring. bs -- The bitstring to '^' with. Raises ValueError if the two bitstrings have differing lengths. """ bs = Bits._create_from_bitstype(bs) s = object.__new__(self.__class__) s._bitstore = self._bitstore ^ bs._bitstore return s def __rxor__(self: TBits, bs: BitsType, /) -> TBits: """Bit-wise 'xor' between two bitstrings. Returns new bitstring. bs -- The bitstring to '^' with. Raises ValueError if the two bitstrings have differing lengths. """ return self.__xor__(bs) def __contains__(self, bs: BitsType, /) -> bool: """Return whether bs is contained in the current bitstring. bs -- The bitstring to search for. """ found = Bits.find(self, bs, bytealigned=False) return bool(found) def __hash__(self) -> int: """Return an integer hash of the object.""" # Only requirement is that equal bitstring should return the same hash. # For equal bitstrings the bytes at the start/end will be the same and they will have the same length # (need to check the length as there could be zero padding when getting the bytes). We do not check any # bit position inside the bitstring as that does not feature in the __eq__ operation. if len(self) <= 2000: # Use the whole bitstring. return hash((self.tobytes(), len(self))) else: # We can't in general hash the whole bitstring (it could take hours!) # So instead take some bits from the start and end. return hash(((self[:800] + self[-800:]).tobytes(), len(self))) def __bool__(self) -> bool: """Return False if bitstring is empty, otherwise return True.""" return len(self) != 0 def _clear(self) -> None: """Reset the bitstring to an empty state.""" self._bitstore = BitStore() 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 = bitstore_helpers.str_to_bitstore(s) elif isinstance(s, Bits): self._bitstore = s._bitstore.copy() elif isinstance(s, (bytes, bytearray, memoryview)): self._bitstore = BitStore.frombytes(bytearray(s)) elif isinstance(s, io.BytesIO): self._bitstore = BitStore.frombytes(s.getvalue()) elif isinstance(s, io.BufferedReader): self._setfile(s.name) elif isinstance(s, bitarray.bitarray): self._bitstore = BitStore(s) elif isinstance(s, array.array): self._bitstore = BitStore.frombytes(s.tobytes()) elif isinstance(s, abc.Iterable): # Evaluate each item as True or False and set bits to 1 or 0. self._setbin_unsafe(''.join(str(int(bool(x))) for x in s)) elif isinstance(s, numbers.Integral): raise TypeError(f"It's no longer possible to auto initialise a bitstring from an integer." f" Use '{self.__class__.__name__}({s})' instead of just '{s}' as this makes it " f"clearer that a bitstring of {int(s)} zero bits will be created.") else: raise TypeError(f"Cannot initialise bitstring from type '{type(s)}'.") def _setauto(self, s: BitsType, length: Optional[int], offset: Optional[int], /) -> None: """Set bitstring from a bitstring, file, bool, array, iterable or string.""" # As s can be so many different things it's important to do the checks # in the correct order, as some types are also other allowed types. if offset is None and length is None: return self._setauto_no_length_or_offset(s) if offset is None: offset = 0 if isinstance(s, io.BytesIO): if length is None: length = s.seek(0, 2) * 8 - offset byteoffset, offset = divmod(offset, 8) bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset if length + byteoffset * 8 + offset > s.seek(0, 2) * 8: raise bitstring.CreationError("BytesIO object is not long enough for specified length and offset.") self._bitstore = BitStore.frombytes(s.getvalue()[byteoffset: byteoffset + bytelength]).getslice( offset, offset + length) return if isinstance(s, io.BufferedReader): self._setfile(s.name, length, offset) return if isinstance(s, (str, Bits, bytes, bytearray, memoryview, io.BytesIO, io.BufferedReader, bitarray.bitarray, 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)}'.") def _setfile(self, filename: str, length: Optional[int] = None, offset: Optional[int] = None) -> None: """Use file as source of bits.""" with open(pathlib.Path(filename), 'rb') as source: if offset is None: offset = 0 m = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ) if offset == 0: self._filename = source.name self._bitstore = BitStore.frombuffer(m, length=length) else: # If offset is given then always read into memory. temp = BitStore.frombuffer(m) if length is None: if offset > len(temp): raise bitstring.CreationError(f"The offset of {offset} bits is greater than the file length ({len(temp)} bits).") self._bitstore = temp.getslice(offset, None) else: self._bitstore = temp.getslice(offset, offset + length) 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 = BitStore(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 = BitStore(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 = bitstore_helpers.p3binary2bitstore(f) def _setp4binary(self, f: float) -> None: self._bitstore = bitstore_helpers.p4binary2bitstore(f) def _sete4m3mxfp(self, f: float) -> None: self._bitstore = bitstore_helpers.e4m3mxfp2bitstore(f) def _sete5m2mxfp(self, f: float) -> None: self._bitstore = bitstore_helpers.e5m2mxfp2bitstore(f) def _sete3m2mxfp(self, f: float) -> None: self._bitstore = bitstore_helpers.e3m2mxfp2bitstore(f) def _sete2m3mxfp(self, f: float) -> None: self._bitstore = bitstore_helpers.e2m3mxfp2bitstore(f) def _sete2m1mxfp(self, f: float) -> None: self._bitstore = bitstore_helpers.e2m1mxfp2bitstore(f) def _sete8m0mxfp(self, f: float) -> None: self._bitstore = bitstore_helpers.e8m0mxfp2bitstore(f) def _setmxint(self, f: float) -> None: self._bitstore = 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.""" self._bitstore = BitStore.frombytes(bytes(data)) def _setbytes_with_truncation(self, data: Union[bytearray, bytes], length: Optional[int] = None, offset: Optional[int] = None) -> None: """Set the data from a bytes or bytearray object, with optional offset and length truncations.""" if offset is None and length is None: return self._setbytes(data) data = bytearray(data) if offset is None: offset = 0 if length is None: # Use to the end of the data length = len(data) * 8 - offset 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 = BitStore.frombytes(data).getslice_msb0(offset, offset + length) def _getbytes(self) -> bytes: """Return the data as an ordinary bytes object.""" if len(self) % 8: raise bitstring.InterpretError("Cannot interpret as bytes unambiguously - not multiple of 8 bits.") return self._bitstore.tobytes() _unprintable = list(range(0x00, 0x20)) # ASCII control characters _unprintable.extend(range(0x7f, 0xff)) # DEL char + non-ASCII def _getbytes_printable(self) -> str: """Return an approximation of the data as a string of printable characters.""" bytes_ = self._getbytes() # For everything that isn't printable ASCII, use value from 'Latin Extended-A' unicode block. string = ''.join(chr(0x100 + x) if x in Bits._unprintable else chr(x) for x in bytes_) return string def _setuint(self, uint: int, length: Optional[int] = None) -> None: """Reset the bitstring to have given unsigned int interpretation.""" # If no length given, and we've previously been given a length, use it. if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length == 0: raise bitstring.CreationError("A non-zero length must be specified with a uint initialiser.") self._bitstore = bitstore_helpers.int2bitstore(uint, length, False) def _getuint(self) -> int: """Return data as an unsigned int.""" if len(self) == 0: raise bitstring.InterpretError("Cannot interpret a zero length bitstring as an integer.") return self._bitstore.slice_to_uint() def _setint(self, int_: int, length: Optional[int] = None) -> None: """Reset the bitstring to have given signed int interpretation.""" # If no length given, and we've previously been given a length, use it. if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length == 0: raise bitstring.CreationError("A non-zero length must be specified with an int initialiser.") self._bitstore = bitstore_helpers.int2bitstore(int_, length, True) def _getint(self) -> int: """Return data as a two's complement signed int.""" if len(self) == 0: raise bitstring.InterpretError("Cannot interpret bitstring without a length as an integer.") return self._bitstore.slice_to_int() def _setuintbe(self, uintbe: int, length: Optional[int] = None) -> None: """Set the bitstring to a big-endian unsigned int interpretation.""" if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length == 0: raise bitstring.CreationError("A non-zero length must be specified with a uintbe initialiser.") self._bitstore = bitstore_helpers.int2bitstore(uintbe, length, False) def _getuintbe(self) -> int: """Return data as a big-endian two's complement unsigned int.""" if len(self) % 8: raise bitstring.InterpretError(f"Big-endian integers must be whole-byte. Length = {len(self)} bits.") return self._getuint() def _setintbe(self, intbe: int, length: Optional[int] = None) -> None: """Set bitstring to a big-endian signed int interpretation.""" if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length == 0: raise bitstring.CreationError("A non-zero length must be specified with a intbe initialiser.") self._bitstore = bitstore_helpers.int2bitstore(intbe, length, True) def _getintbe(self) -> int: """Return data as a big-endian two's complement signed int.""" if len(self) % 8: raise bitstring.InterpretError(f"Big-endian integers must be whole-byte. Length = {len(self)} bits.") return self._getint() def _setuintle(self, uintle: int, length: Optional[int] = None) -> None: if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length == 0: raise bitstring.CreationError("A non-zero length must be specified with a uintle initialiser.") self._bitstore = bitstore_helpers.intle2bitstore(uintle, length, False) def _getuintle(self) -> int: """Interpret as a little-endian unsigned int.""" if len(self) % 8: raise bitstring.InterpretError(f"Little-endian integers must be whole-byte. Length = {len(self)} bits.") bs = BitStore.frombytes(self._bitstore.tobytes()[::-1]) return bs.slice_to_uint() def _setintle(self, intle: int, length: Optional[int] = None) -> None: if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length == 0: raise bitstring.CreationError("A non-zero length must be specified with an intle initialiser.") self._bitstore = bitstore_helpers.intle2bitstore(intle, length, True) def _getintle(self) -> int: """Interpret as a little-endian signed int.""" if len(self) % 8: raise bitstring.InterpretError(f"Little-endian integers must be whole-byte. Length = {len(self)} bits.") bs = BitStore.frombytes(self._bitstore.tobytes()[::-1]) return bs.slice_to_int() def _getp4binary(self) -> float: u = self._getuint() return p4binary_fmt.lut_binary8_to_float[u] def _getp3binary(self) -> float: u = self._getuint() return p3binary_fmt.lut_binary8_to_float[u] def _gete4m3mxfp(self) -> float: u = self._getuint() return e4m3mxfp_saturate_fmt.lut_int_to_float[u] def _gete5m2mxfp(self) -> float: u = self._getuint() return e5m2mxfp_saturate_fmt.lut_int_to_float[u] def _gete3m2mxfp(self) -> float: u = self._getuint() return e3m2mxfp_fmt.lut_int_to_float[u] def _gete2m3mxfp(self) -> float: u = self._getuint() return e2m3mxfp_fmt.lut_int_to_float[u] def _gete2m1mxfp(self) -> float: u = self._getuint() return e2m1mxfp_fmt.lut_int_to_float[u] def _gete8m0mxfp(self) -> float: u = self._getuint() - 127 if u == 128: return float('nan') return 2.0 ** u def _getmxint(self) -> float: u = self._getint() return float(u) * 2 ** -6 def _setfloat(self, f: float, length: Optional[int], big_endian: bool) -> None: if length is None and hasattr(self, 'len') and len(self) != 0: length = len(self) if length is None or length not in [16, 32, 64]: raise bitstring.CreationError("A length of 16, 32, or 64 must be specified with a float initialiser.") self._bitstore = bitstore_helpers.float2bitstore(f, length, big_endian) def _setfloatbe(self, f: float, length: Optional[int] = None) -> None: self._setfloat(f, length, True) def _getfloatbe(self) -> float: """Interpret the whole bitstring as a big-endian float.""" fmt = {16: '>e', 32: '>f', 64: '>d'}[len(self)] return struct.unpack(fmt, self._bitstore.tobytes())[0] def _setfloatle(self, f: float, length: Optional[int] = None) -> None: self._setfloat(f, length, False) def _getfloatle(self) -> float: """Interpret the whole bitstring as a little-endian float.""" fmt = {16: ' float: zero_padded = self + Bits(16) return zero_padded._getfloatbe() 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 = bitstore_helpers.bfloat2bitstore(f, True) def _getbfloatle(self) -> float: zero_padded = Bits(16) + self return zero_padded._getfloatle() 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 = bitstore_helpers.bfloat2bitstore(f, False) def _setue(self, i: int) -> None: """Initialise bitstring with unsigned exponential-Golomb code for integer i. Raises CreationError if i < 0. """ if bitstring.options.lsb0: raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") self._bitstore = bitstore_helpers.ue2bitstore(i) def _readue(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as unsigned exponential-Golomb code. Raises ReadError if the end of the bitstring is encountered while reading the code. """ if bitstring.options.lsb0: raise bitstring.ReadError("Exp-Golomb codes cannot be read in lsb0 mode.") oldpos = pos try: while not self[pos]: pos += 1 except IndexError: raise bitstring.ReadError("Read off end of bitstring trying to read code.") leadingzeros = pos - oldpos codenum = (1 << leadingzeros) - 1 if leadingzeros > 0: if pos + leadingzeros + 1 > len(self): raise bitstring.ReadError("Read off end of bitstring trying to read code.") codenum += self[pos + 1:pos + 1 + leadingzeros]._getuint() pos += leadingzeros + 1 else: assert codenum == 0 pos += 1 return codenum, pos def _getue(self) -> Tuple[int, int]: try: return self._readue(0) except bitstring.ReadError: raise bitstring.InterpretError def _getse(self) -> Tuple[int, int]: try: return self._readse(0) except bitstring.ReadError: raise bitstring.InterpretError def _getuie(self) -> Tuple[int, int]: try: return self._readuie(0) except bitstring.ReadError: raise bitstring.InterpretError def _getsie(self) -> Tuple[int, int]: try: return self._readsie(0) except bitstring.ReadError: raise bitstring.InterpretError 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 = bitstore_helpers.se2bitstore(i) def _readse(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as a signed exponential-Golomb code. Advances position to after the read code. Raises ReadError if the end of the bitstring is encountered while reading the code. """ codenum, pos = self._readue(pos) m = (codenum + 1) // 2 return (m, pos) if codenum % 2 else (-m, pos) def _setuie(self, i: int) -> None: """Initialise bitstring with unsigned interleaved exponential-Golomb code for integer i. Raises CreationError if i < 0. """ if bitstring.options.lsb0: raise bitstring.CreationError("Exp-Golomb codes cannot be used in lsb0 mode.") self._bitstore = bitstore_helpers.uie2bitstore(i) def _readuie(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as unsigned interleaved exponential-Golomb code. Raises ReadError if the end of the bitstring is encountered while reading the code. """ if bitstring.options.lsb0: raise bitstring.ReadError("Exp-Golomb codes cannot be read in lsb0 mode.") try: codenum: int = 1 while not self[pos]: pos += 1 codenum <<= 1 codenum += self[pos] pos += 1 pos += 1 except IndexError: raise bitstring.ReadError("Read off end of bitstring trying to read code.") return codenum - 1, pos 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 = bitstore_helpers.sie2bitstore(i) def _readsie(self, pos: int) -> Tuple[int, int]: """Return interpretation of next bits as a signed interleaved exponential-Golomb code. Advances position to after the read code. Raises ReadError if the end of the bitstring is encountered while reading the code. """ codenum, pos = self._readuie(pos) if not codenum: return 0, pos try: return (-codenum, pos + 1) if self[pos] else (codenum, pos + 1) except IndexError: raise bitstring.ReadError("Read off end of bitstring trying to read code.") def _setbool(self, value: Union[bool, str]) -> None: # We deliberately don't want to have implicit conversions to bool here. # If we did then it would be difficult to deal with the 'False' string. if value in (1, 'True', '1'): self._bitstore = BitStore('1') elif value in (0, 'False', '0'): self._bitstore = BitStore('0') else: raise bitstring.CreationError(f"Cannot initialise boolean with {value}.") def _getbool(self) -> bool: return self[0] def _getpad(self) -> None: return None def _setpad(self, value: None, length: int) -> None: self._bitstore = BitStore(length) def _setbin_safe(self, binstring: str, length: None = None) -> None: """Reset the bitstring to the value given in binstring.""" self._bitstore = bitstore_helpers.bin2bitstore(binstring) def _setbin_unsafe(self, binstring: str, length: None = None) -> None: """Same as _setbin_safe, but input isn't sanity checked. binstring mustn't start with '0b'.""" self._bitstore = bitstore_helpers.bin2bitstore_unsafe(binstring) def _getbin(self) -> str: """Return interpretation as a binary string.""" return self._bitstore.slice_to_bin() def _setoct(self, octstring: str, length: None = None) -> None: """Reset the bitstring to have the value given in octstring.""" self._bitstore = bitstore_helpers.oct2bitstore(octstring) def _getoct(self) -> str: """Return interpretation as an octal string.""" return self._bitstore.slice_to_oct() def _sethex(self, hexstring: str, length: None = None) -> None: """Reset the bitstring to have the value given in hexstring.""" self._bitstore = bitstore_helpers.hex2bitstore(hexstring) def _gethex(self) -> str: """Return the hexadecimal representation as a string. Raises an InterpretError if the bitstring's length is not a multiple of 4. """ return self._bitstore.slice_to_hex() def _getlength(self) -> int: """Return the length of the bitstring in bits.""" return len(self._bitstore) def _copy(self: TBits) -> TBits: """Create and return a new copy of the Bits (always in memory).""" # Note that __copy__ may choose to return self if it's immutable. This method always makes a copy. s_copy = self.__class__() s_copy._bitstore = self._bitstore._copy() return s_copy def _slice(self: TBits, start: int, end: int) -> TBits: """Used internally to get a slice, without error checking.""" bs = self.__class__() bs._bitstore = self._bitstore.getslice(start, end) return bs def _absolute_slice(self: TBits, start: int, end: int) -> TBits: """Used internally to get a slice, without error checking. Uses MSB0 bit numbering even if LSB0 is set.""" if end == start: return self.__class__() assert start < end, f"start={start}, end={end}" bs = self.__class__() bs._bitstore = self._bitstore.getslice_msb0(start, end) return bs def _readtoken(self, name: str, pos: int, length: Optional[int]) -> Tuple[Union[float, int, str, None, Bits], int]: """Reads a token from the bitstring and returns the result.""" dtype = dtype_register.get_dtype(name, length) if dtype.bitlength is not None and dtype.bitlength > len(self) - pos: raise bitstring.ReadError("Reading off the end of the data. " f"Tried to read {dtype.bitlength} bits when only {len(self) - pos} available.") try: val = dtype.read_fn(self, pos) if isinstance(val, tuple): return val else: assert length is not None return val, pos + dtype.bitlength except KeyError: raise ValueError(f"Can't parse token {name}:{length}") def _addright(self, bs: Bits, /) -> None: """Add a bitstring to the RHS of the current bitstring.""" self._bitstore += bs._bitstore def _addleft(self, bs: Bits, /) -> None: """Prepend a bitstring to the current bitstring.""" if bs._bitstore.immutable: self._bitstore = bs._bitstore._copy() + self._bitstore else: self._bitstore = bs._bitstore + self._bitstore def _truncateleft(self: TBits, bits: int, /) -> TBits: """Truncate bits from the start of the bitstring. Return the truncated bits.""" assert 0 <= bits <= len(self) if bits == 0: return self.__class__() truncated_bits = self._absolute_slice(0, bits) if bits == len(self): self._clear() return truncated_bits self._bitstore = self._bitstore.getslice_msb0(bits, None) return truncated_bits def _truncateright(self: TBits, bits: int, /) -> TBits: """Truncate bits from the end of the bitstring. Return the truncated bits.""" assert 0 <= bits <= len(self) if bits == 0: return self.__class__() truncated_bits = self._absolute_slice(len(self) - bits, len(self)) if bits == len(self): self._clear() return truncated_bits self._bitstore = self._bitstore.getslice_msb0(None, -bits) return truncated_bits def _insert(self, bs: Bits, pos: int, /) -> None: """Insert bs at pos.""" assert 0 <= pos <= len(self) self._bitstore[pos: pos] = bs._bitstore return def _overwrite(self, bs: Bits, pos: int, /) -> None: """Overwrite with bs at pos.""" assert 0 <= pos <= len(self) if bs is self: # Just overwriting with self, so do nothing. assert pos == 0 return self._bitstore[pos: pos + len(bs)] = bs._bitstore def _delete(self, bits: int, pos: int, /) -> None: """Delete bits at pos.""" assert 0 <= pos <= len(self) assert pos + bits <= len(self), f"pos={pos}, bits={bits}, len={len(self)}" del self._bitstore[pos: pos + bits] return def _reversebytes(self, start: int, end: int) -> None: """Reverse bytes in-place.""" assert (end - start) % 8 == 0 self._bitstore[start:end] = BitStore.frombytes(self._bitstore.getslice(start, end).tobytes()[::-1]) def _invert(self, pos: int, /) -> None: """Flip bit at pos 1<->0.""" assert 0 <= pos < len(self) self._bitstore.invert(pos) def _invert_all(self) -> None: """Invert every bit.""" self._bitstore.invert() def _ilshift(self: TBits, n: int, /) -> TBits: """Shift bits by n to the left in place. Return self.""" assert 0 < n <= len(self) self._addright(Bits(n)) self._truncateleft(n) return self def _irshift(self: TBits, n: int, /) -> TBits: """Shift bits by n to the right in place. Return self.""" assert 0 < n <= len(self) self._addleft(Bits(n)) self._truncateright(n) return self def _imul(self: TBits, n: int, /) -> TBits: """Concatenate n copies of self in place. Return self.""" assert n >= 0 if n == 0: self._clear() else: m = 1 old_len = len(self) while m * 2 < n: self._addright(self) m *= 2 self._addright(self[0:(n - m) * old_len]) return self def _getbits(self: TBits): return self._copy() def _validate_slice(self, start: Optional[int], end: Optional[int]) -> Tuple[int, int]: """Validate start and end and return them as positive bit positions.""" start = 0 if start is None else (start + len(self) if start < 0 else start) end = len(self) if end is None else (end + len(self) if end < 0 else end) if not 0 <= start <= end <= len(self): raise ValueError(f"Invalid slice positions for bitstring length {len(self)}: start={start}, end={end}.") return start, end def unpack(self, fmt: Union[str, List[Union[str, int]]], **kwargs) -> List[Union[int, float, str, Bits, bool, bytes, None]]: """Interpret the whole bitstring using fmt and return list. fmt -- A single string or a list of strings with comma separated tokens describing how to interpret the bits in the bitstring. Items can also be integers, for reading new bitstring of the given length. kwargs -- A dictionary or keyword-value pairs - the keywords used in the format string will be replaced with their given value. Raises ValueError if the format is not understood. If not enough bits are available then all bits to the end of the bitstring will be used. See the docstring for 'read' for token examples. """ return self._readlist(fmt, 0, **kwargs)[0] def _readlist(self, fmt: Union[str, List[Union[str, int, Dtype]]], pos: int, **kwargs) \ -> Tuple[List[Union[int, float, str, Bits, bool, bytes, None]], int]: if isinstance(fmt, str): fmt = [fmt] # Convert to a flat list of Dtypes dtype_list = [] for f_item in fmt: if isinstance(f_item, numbers.Integral): dtype_list.append(Dtype('bits', f_item)) elif isinstance(f_item, Dtype): dtype_list.append(f_item) else: token_list = utils.preprocess_tokens(f_item) for t in token_list: try: name, length = utils.parse_name_length_token(t, **kwargs) except ValueError: dtype_list.append(Dtype('bits', int(t))) else: dtype_list.append(Dtype(name, length)) return self._read_dtype_list(dtype_list, pos) def _read_dtype_list(self, dtypes: List[Dtype], pos: int) -> Tuple[List[Union[int, float, str, Bits, bool, bytes, None]], int]: has_stretchy_token = False bits_after_stretchy_token = 0 for dtype in dtypes: stretchy = dtype.bitlength is None and not dtype.variable_length if stretchy: if has_stretchy_token: raise bitstring.Error("It's not possible to have more than one 'filler' token.") has_stretchy_token = True elif has_stretchy_token: if dtype.variable_length: raise bitstring.Error(f"It's not possible to parse a variable length token '{dtype}' after a 'filler' token.") bits_after_stretchy_token += dtype.bitlength # We should have precisely zero or one stretchy token vals = [] for dtype in dtypes: stretchy = dtype.bitlength is None and not dtype.variable_length if stretchy: bits_remaining = len(self) - pos # Set length to the remaining bits bitlength = max(bits_remaining - bits_after_stretchy_token, 0) items, remainder = divmod(bitlength, dtype.bits_per_item) if remainder != 0: raise ValueError( f"The '{dtype.name}' type must have a bit length that is a multiple of {dtype.bits_per_item}" f" so cannot be created from the {bitlength} bits that are available for this stretchy token.") dtype = Dtype(dtype.name, items) if dtype.bitlength is not None: val = dtype.read_fn(self, pos) pos += dtype.bitlength else: val, pos = dtype.read_fn(self, pos) if val is not None: # Don't append pad tokens vals.append(val) return vals, pos def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: """Find first occurrence of substring bs. Returns a single item tuple with the bit position if found, or an empty tuple if not found. The bit position (pos property) will also be set to the start of the substring if it is found. bs -- The bitstring to find. start -- The bit position to start the search. Defaults to 0. end -- The bit position one past the last bit to search. Defaults to len(self). bytealigned -- If True the bitstring will only be found on byte boundaries. Raises ValueError if bs is empty, if start < 0, if end > len(self) or if end < start. >>> BitArray('0xc3e').find('0b1111') (6,) """ bs = Bits._create_from_bitstype(bs) if len(bs) == 0: raise ValueError("Cannot find an empty bitstring.") start, end = self._validate_slice(start, end) ba = bitstring.options.bytealigned if bytealigned is None else bytealigned p = self._find(bs, start, end, ba) return p def _find_lsb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: # A forward find in lsb0 is very like a reverse find in msb0. assert start <= end assert bitstring.options.lsb0 new_slice = bitstring.bitstore.offset_slice_indices_lsb0(slice(start, end, None), len(self)) msb0_start, msb0_end = self._validate_slice(new_slice.start, new_slice.stop) p = self._rfind_msb0(bs, msb0_start, msb0_end, bytealigned) if p: return (len(self) - p[0] - len(bs),) else: return () def _find_msb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: """Find first occurrence of a binary string.""" p = self._bitstore.find(bs._bitstore, start, end, bytealigned) return () if p == -1 else (p,) def findall(self, bs: BitsType, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None, bytealigned: Optional[bool] = None) -> Iterable[int]: """Find all occurrences of bs. Return generator of bit positions. bs -- The bitstring to find. start -- The bit position to start the search. Defaults to 0. end -- The bit position one past the last bit to search. Defaults to len(self). count -- The maximum number of occurrences to find. bytealigned -- If True the bitstring will only be found on byte boundaries. Raises ValueError if bs is empty, if start < 0, if end > len(self) or if end < start. Note that all occurrences of bs are found, even if they overlap. """ if count is not None and count < 0: raise ValueError("In findall, count must be >= 0.") bs = Bits._create_from_bitstype(bs) start, end = self._validate_slice(start, end) ba = bitstring.options.bytealigned if bytealigned is None else bytealigned return self._findall(bs, start, end, count, ba) def _findall_msb0(self, bs: Bits, start: int, end: int, count: Optional[int], bytealigned: bool) -> Iterable[int]: c = 0 for i in self._bitstore.findall_msb0(bs._bitstore, start, end, bytealigned): if count is not None and c >= count: return c += 1 yield i return def _findall_lsb0(self, bs: Bits, start: int, end: int, count: Optional[int], bytealigned: bool) -> Iterable[int]: assert start <= end assert bitstring.options.lsb0 new_slice = bitstring.bitstore.offset_slice_indices_lsb0(slice(start, end, None), len(self)) msb0_start, msb0_end = self._validate_slice(new_slice.start, new_slice.stop) # Search chunks starting near the end and then moving back. c = 0 increment = max(8192, len(bs) * 80) buffersize = min(increment + len(bs), msb0_end - msb0_start) pos = max(msb0_start, msb0_end - buffersize) while True: found = list(self._findall_msb0(bs, start=pos, end=pos + buffersize, count=None, bytealigned=False)) if not found: if pos == msb0_start: return pos = max(msb0_start, pos - increment) continue while found: if count is not None and c >= count: return c += 1 lsb0_pos = len(self) - found.pop() - len(bs) if not bytealigned or lsb0_pos % 8 == 0: yield lsb0_pos pos = max(msb0_start, pos - increment) if pos == msb0_start: return def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: """Find final occurrence of substring bs. Returns a single item tuple with the bit position if found, or an empty tuple if not found. The bit position (pos property) will also be set to the start of the substring if it is found. bs -- The bitstring to find. start -- The bit position to end the reverse search. Defaults to 0. end -- The bit position one past the first bit to reverse search. Defaults to len(self). bytealigned -- If True the bitstring will only be found on byte boundaries. Raises ValueError if bs is empty, if start < 0, if end > len(self) or if end < start. """ bs = Bits._create_from_bitstype(bs) start, end = self._validate_slice(start, end) ba = bitstring.options.bytealigned if bytealigned is None else bytealigned if len(bs) == 0: raise ValueError("Cannot find an empty bitstring.") p = self._rfind(bs, start, end, ba) return p def _rfind_msb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: """Find final occurrence of a binary string.""" p = self._bitstore.rfind(bs._bitstore, start, end, bytealigned) return () if p == -1 else (p,) def _rfind_lsb0(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]: # A reverse find in lsb0 is very like a forward find in msb0. assert start <= end assert bitstring.options.lsb0 new_slice = bitstring.bitstore.offset_slice_indices_lsb0(slice(start, end, None), len(self)) msb0_start, msb0_end = self._validate_slice(new_slice.start, new_slice.stop) p = self._find_msb0(bs, msb0_start, msb0_end, bytealigned) if p: return (len(self) - p[0] - len(bs),) else: return () def cut(self, bits: int, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None) -> Iterator[Bits]: """Return bitstring generator by cutting into bits sized chunks. bits -- The size in bits of the bitstring chunks to generate. start -- The bit position to start the first cut. Defaults to 0. end -- The bit position one past the last bit to use in the cut. Defaults to len(self). count -- If specified then at most count items are generated. Default is to cut as many times as possible. """ start_, end_ = self._validate_slice(start, end) if count is not None and count < 0: raise ValueError("Cannot cut - count must be >= 0.") if bits <= 0: raise ValueError("Cannot cut - bits must be >= 0.") c = 0 while count is None or c < count: c += 1 nextchunk = self._slice(start_, min(start_ + bits, end_)) if len(nextchunk) == 0: return yield nextchunk if len(nextchunk) != bits: return start_ += bits return def split(self, delimiter: BitsType, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None, bytealigned: Optional[bool] = None) -> Iterable[Bits]: """Return bitstring generator by splitting using a delimiter. The first item returned is the initial bitstring before the delimiter, which may be an empty bitstring. delimiter -- The bitstring used as the divider. start -- The bit position to start the split. Defaults to 0. end -- The bit position one past the last bit to use in the split. Defaults to len(self). count -- If specified then at most count items are generated. Default is to split as many times as possible. bytealigned -- If True splits will only occur on byte boundaries. Raises ValueError if the delimiter is empty. """ delimiter = Bits._create_from_bitstype(delimiter) if len(delimiter) == 0: raise ValueError("split delimiter cannot be empty.") start, end = self._validate_slice(start, end) bytealigned_: bool = bitstring.options.bytealigned if bytealigned is None else bytealigned if count is not None and count < 0: raise ValueError("Cannot split - count must be >= 0.") if count == 0: return f = functools.partial(self._find_msb0, bs=delimiter, bytealigned=bytealigned_) found = f(start=start, end=end) if not found: # Initial bits are the whole bitstring being searched yield self._slice(start, end) return # yield the bytes before the first occurrence of the delimiter, even if empty yield self._slice(start, found[0]) startpos = pos = found[0] c = 1 while count is None or c < count: pos += len(delimiter) found = f(start=pos, end=end) if not found: # No more occurrences, so return the rest of the bitstring yield self._slice(startpos, end) return c += 1 yield self._slice(startpos, found[0]) startpos = pos = found[0] # Have generated count bitstrings, so time to quit. return def join(self: TBits, sequence: Iterable[Any]) -> TBits: """Return concatenation of bitstrings joined by self. sequence -- A sequence of bitstrings. """ s = self.__class__() if len(self) == 0: # Optimised version that doesn't need to add self between every item for item in sequence: s._addright(Bits._create_from_bitstype(item)) return s else: sequence_iter = iter(sequence) try: s._addright(Bits._create_from_bitstype(next(sequence_iter))) except StopIteration: return s for item in sequence_iter: s._addright(self) s._addright(Bits._create_from_bitstype(item)) return s def tobytes(self) -> bytes: """Return the bitstring as bytes, padding with zero bits if needed. Up to seven zero bits will be added at the end to byte align. """ return self._bitstore.tobytes() def tobitarray(self) -> bitarray.bitarray: """Convert the bitstring to a bitarray object.""" if self._bitstore.modified_length is not None: # Removes the offset and truncates to length return self._bitstore.getslice(0, len(self))._bitarray else: return self._bitstore._bitarray def tofile(self, f: BinaryIO) -> None: """Write the bitstring to a file object, padding with zero bits if needed. Up to seven zero bits will be added at the end to byte align. """ # If the bitstring is file based then we don't want to read it all in to memory first. chunk_size = 8 * 100 * 1024 * 1024 # 100 MiB for chunk in self.cut(chunk_size): f.write(chunk.tobytes()) def startswith(self, prefix: BitsType, start: Optional[int] = None, end: Optional[int] = None) -> bool: """Return whether the current bitstring starts with prefix. prefix -- The bitstring to search for. start -- The bit position to start from. Defaults to 0. end -- The bit position to end at. Defaults to len(self). """ prefix = self._create_from_bitstype(prefix) start, end = self._validate_slice(start, end) return self._slice(start, start + len(prefix)) == prefix if end >= start + len(prefix) else False def endswith(self, suffix: BitsType, start: Optional[int] = None, end: Optional[int] = None) -> bool: """Return whether the current bitstring ends with suffix. suffix -- The bitstring to search for. start -- The bit position to start from. Defaults to 0. end -- The bit position to end at. Defaults to len(self). """ suffix = self._create_from_bitstype(suffix) start, end = self._validate_slice(start, end) return self._slice(end - len(suffix), end) == suffix if start + len(suffix) <= end else False def all(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool: """Return True if one or many bits are all set to bool(value). value -- If value is True then checks for bits set to 1, otherwise checks for bits set to 0. pos -- An iterable of bit positions. Negative numbers are treated in the same way as slice indices. Defaults to the whole bitstring. """ value = 1 if bool(value) else 0 if pos is None: return self._bitstore.all_set() if value else not self._bitstore.any_set() for p in pos: if self._bitstore.getindex(p) != value: return False return True def any(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool: """Return True if any of one or many bits are set to bool(value). value -- If value is True then checks for bits set to 1, otherwise checks for bits set to 0. pos -- An iterable of bit positions. Negative numbers are treated in the same way as slice indices. Defaults to the whole bitstring. """ value = 1 if bool(value) else 0 if pos is None: return self._bitstore.any_set() if value else not self._bitstore.all_set() for p in pos: if self._bitstore.getindex(p) == value: return True return False def count(self, value: Any) -> int: """Return count of total number of either zero or one bits. value -- If bool(value) is True then bits set to 1 are counted, otherwise bits set to 0 are counted. >>> Bits('0xef').count(1) 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 @staticmethod def _format_bits(bits: Bits, bits_per_group: int, sep: str, dtype: Dtype, colour_start: str, colour_end: str, width: Optional[int]=None) -> Tuple[str, int]: get_fn = dtype.get_fn if dtype.name == 'bytes': # Special case for bytes to print one character each. get_fn = Bits._getbytes_printable if dtype.name == 'bool': # Special case for bool to print '1' or '0' instead of `True` or `False`. get_fn = dtype_register.get_dtype('uint', bits_per_group).get_fn if bits_per_group == 0: x = str(get_fn(bits)) else: # Left-align for fixed width types when msb0, otherwise right-align. align = '<' if dtype.name in ['bin', 'oct', 'hex', 'bits', 'bytes'] and not bitstring.options.lsb0 else '>' chars_per_group = 0 if dtype_register[dtype.name].bitlength2chars_fn is not None: chars_per_group = dtype_register[dtype.name].bitlength2chars_fn(bits_per_group) x = sep.join(f"{str(get_fn(b)): {align}{chars_per_group}}" for b in bits.cut(bits_per_group)) chars_used = len(x) padding_spaces = 0 if width is None else max(width - len(x), 0) x = colour_start + x + colour_end # Pad final line with spaces to align it if bitstring.options.lsb0: x = ' ' * padding_spaces + x else: x += ' ' * padding_spaces return x, chars_used @staticmethod def _chars_per_group(bits_per_group: int, fmt: Optional[str]): """How many characters are needed to represent a number of bits with a given format.""" if fmt is None or dtype_register[fmt].bitlength2chars_fn is None: return 0 return dtype_register[fmt].bitlength2chars_fn(bits_per_group) @staticmethod def _bits_per_char(fmt: str): """How many bits are represented by each character of a given format.""" if fmt not in ['bin', 'oct', 'hex', 'bytes']: raise ValueError return 24 // dtype_register[fmt].bitlength2chars_fn(24) def _pp(self, dtype1: Dtype, dtype2: Optional[Dtype], bits_per_group: int, width: int, sep: str, format_sep: str, show_offset: bool, stream: TextIO, lsb0: bool, offset_factor: int) -> None: """Internal pretty print method.""" colour = Colour(not bitstring.options.no_color) name1 = dtype1.name name2 = dtype2.name if dtype2 is not None else None if dtype1.variable_length: raise ValueError(f"Can't use Dtype '{dtype1}' in pp() as it has a variable length.") if dtype2 is not None and dtype2.variable_length: raise ValueError(f"Can't use Dtype '{dtype2}' in pp() as it has a variable length.") offset_width = 0 offset_sep = ' :' if lsb0 else ': ' if show_offset: # This could be 1 too large in some circumstances. Slightly recurrent logic needed to fix it... offset_width = len(str(len(self))) + len(offset_sep) if bits_per_group > 0: group_chars1 = Bits._chars_per_group(bits_per_group, name1) group_chars2 = Bits._chars_per_group(bits_per_group, name2) # The number of characters that get added when we add an extra group (after the first one) total_group_chars = group_chars1 + group_chars2 + len(sep) + len(sep) * bool(group_chars2) width_excluding_offset_and_final_group = width - offset_width - group_chars1 - group_chars2 - len( format_sep) * bool(group_chars2) width_excluding_offset_and_final_group = max(width_excluding_offset_and_final_group, 0) groups_per_line = 1 + width_excluding_offset_and_final_group // total_group_chars max_bits_per_line = groups_per_line * bits_per_group # Number of bits represented on each line else: assert bits_per_group == 0 # Don't divide into groups width_available = width - offset_width - len(format_sep) * (name2 is not None) width_available = max(width_available, 1) if name2 is None: max_bits_per_line = width_available * Bits._bits_per_char(name1) else: chars_per_24_bits = dtype_register[name1].bitlength2chars_fn(24) + dtype_register[name2].bitlength2chars_fn(24) max_bits_per_line = 24 * (width_available // chars_per_24_bits) if max_bits_per_line == 0: max_bits_per_line = 24 # We can't fit into the width asked for. Show something small. assert max_bits_per_line > 0 bitpos = 0 first_fb_width = second_fb_width = None for bits in self.cut(max_bits_per_line): offset_str = '' if show_offset: offset = bitpos // offset_factor bitpos += len(bits) if bitstring.options.lsb0: offset_str = colour.green + offset_sep + f'{offset: <{offset_width - len(offset_sep)}}' + colour.off else: offset_str = colour.green + f'{offset: >{offset_width - len(offset_sep)}}' + offset_sep + colour.off fb1, chars_used = Bits._format_bits(bits, bits_per_group, sep, dtype1, colour.purple, colour.off, first_fb_width) if first_fb_width is None: first_fb_width = chars_used fb2 = '' if dtype2 is not None: fb2, chars_used = Bits._format_bits(bits, bits_per_group, sep, dtype2, colour.blue, colour.off, second_fb_width) if second_fb_width is None: second_fb_width = chars_used fb2 = format_sep + fb2 if bitstring.options.lsb0 is True: line_fmt = fb1 + fb2 + offset_str + '\n' else: line_fmt = offset_str + fb1 + fb2 + '\n' stream.write(line_fmt) return @staticmethod def _process_pp_tokens(token_list, fmt): if len(token_list) not in [1, 2]: raise ValueError( f"Only one or two tokens can be used in an pp() format - '{fmt}' has {len(token_list)} tokens.") has_length_in_fmt = True name1, length1 = utils.parse_name_length_token(token_list[0]) dtype1 = Dtype(name1, length1) bits_per_group = dtype1.bitlength dtype2 = None if len(token_list) == 2: name2, length2 = utils.parse_name_length_token(token_list[1]) dtype2 = Dtype(name2, length2) if None not in {dtype1.bitlength, dtype2.bitlength} and dtype1.bitlength != dtype2.bitlength: raise ValueError( f"Differing bit lengths of {dtype1.bitlength} and {dtype2.bitlength} in format string '{fmt}'.") if bits_per_group is None: bits_per_group = dtype2.bitlength if bits_per_group is None: has_length_in_fmt = False if len(token_list) == 1: bits_per_group = {'bin': 8, 'hex': 8, 'oct': 12, 'bytes': 32}.get(dtype1.name) if bits_per_group is None: raise ValueError(f"No length or default length available for pp() format '{fmt}'.") else: try: bits_per_group = 2 * Bits._bits_per_char(dtype1.name) * Bits._bits_per_char(dtype2.name) except ValueError: raise ValueError(f"Can't find a default bitlength to use for pp() format '{fmt}'.") if bits_per_group >= 24: bits_per_group //= 2 return dtype1, dtype2, bits_per_group, has_length_in_fmt def pp(self, fmt: Optional[str] = None, width: int = 120, sep: str = ' ', show_offset: bool = True, stream: TextIO = sys.stdout) -> None: """Pretty print the bitstring's value. fmt -- Printed data format. One or two of 'bin', 'oct', 'hex' or 'bytes'. The number of bits represented in each printed group defaults to 8 for hex and bin, 12 for oct and 32 for bytes. This can be overridden with an explicit length, e.g. 'hex:64'. Use a length of 0 to not split into groups, e.g. `bin:0`. width -- Max width of printed lines. Defaults to 120. A single group will always be printed per line even if it exceeds the max width. sep -- A separator string to insert between groups. Defaults to a single space. show_offset -- If True (the default) shows the bit offset in the first column of each line. stream -- A TextIO object with a write() method. Defaults to sys.stdout. >>> s.pp('hex16') >>> s.pp('b, h', sep='_', show_offset=False) """ colour = Colour(not bitstring.options.no_color) if fmt is None: fmt = 'bin, hex' if len(self) % 8 == 0 and len(self) >= 8 else 'bin' token_list = utils.preprocess_tokens(fmt) dtype1, dtype2, bits_per_group, has_length_in_fmt = Bits._process_pp_tokens(token_list, fmt) trailing_bit_length = len(self) % bits_per_group if has_length_in_fmt and bits_per_group else 0 data = self if trailing_bit_length == 0 else self[0: -trailing_bit_length] format_sep = " : " # String to insert on each line between multiple formats tidy_fmt = colour.purple + str(dtype1) + colour.off if dtype2 is not None: tidy_fmt += ', ' + colour.blue + str(dtype2) + colour.off output_stream = io.StringIO() len_str = colour.green + str(len(self)) + colour.off output_stream.write(f"<{self.__class__.__name__}, fmt='{tidy_fmt}', length={len_str} bits> [\n") data._pp(dtype1, dtype2, bits_per_group, width, sep, format_sep, show_offset, output_stream, bitstring.options.lsb0, 1) output_stream.write("]") if trailing_bit_length != 0: output_stream.write(" + trailing_bits = " + str(self[-trailing_bit_length:])) output_stream.write("\n") stream.write(output_stream.getvalue()) return def copy(self: TBits) -> TBits: """Return a copy of the bitstring.""" # Note that if you want a new copy (different ID), use _copy instead. # The copy can return self as it's immutable. return self @classmethod def fromstring(cls: TBits, s: str, /) -> TBits: """Create a new bitstring from a formatted string.""" x = super().__new__(cls) x._bitstore = bitstore_helpers.str_to_bitstore(s) return x len = length = property(_getlength, doc="The length of the bitstring in bits. Read only.") bitstring-bitstring-4.2.3/bitstring/bitstore.py000066400000000000000000000241411462444752600217620ustar00rootroot00000000000000from __future__ import annotations import bitarray from bitstring.exceptions import CreationError from typing import Union, Iterable, Optional, overload, Iterator, Any def offset_slice_indices_lsb0(key: slice, length: int) -> slice: # First convert slice to all integers # Length already should take account of the offset start, stop, step = key.indices(length) new_start = length - stop new_stop = length - start # For negative step we sometimes get a negative stop, which can't be used correctly in a new slice return slice(new_start, None if new_stop < 0 else new_stop, step) def offset_start_stop_lsb0(start: Optional[int], stop: Optional[int], length: int) -> tuple[int, int]: # First convert slice to all integers # Length already should take account of the offset start, stop, _ = slice(start, stop, None).indices(length) new_start = length - stop new_stop = length - start return new_start, new_stop class BitStore: """A light wrapper around bitarray that does the LSB0 stuff""" __slots__ = ('_bitarray', 'modified_length', 'immutable') def __init__(self, initializer: Union[int, bitarray.bitarray, str, None] = None, immutable: bool = False) -> None: self._bitarray = bitarray.bitarray(initializer) self.immutable = immutable self.modified_length = None @classmethod def frombytes(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 def setall(self, value: int, /) -> None: self._bitarray.setall(value) def tobytes(self) -> bytes: if self.modified_length is not None: return self._bitarray[:self.modified_length].tobytes() return self._bitarray.tobytes() def slice_to_uint(self, start: Optional[int] = None, end: Optional[int] = None) -> int: return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=False) def slice_to_int(self, start: Optional[int] = None, end: Optional[int] = None) -> int: return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=True) def slice_to_hex(self, start: Optional[int] = None, end: Optional[int] = None) -> str: return bitarray.util.ba2hex(self.getslice(start, end)._bitarray) def slice_to_bin(self, start: Optional[int] = None, end: Optional[int] = None) -> str: return self.getslice(start, end)._bitarray.to01() def slice_to_oct(self, start: Optional[int] = None, end: Optional[int] = None) -> str: return bitarray.util.ba2base(8, self.getslice(start, end)._bitarray) def __iadd__(self, other: BitStore, /) -> BitStore: self._bitarray += other._bitarray return self def __add__(self, other: BitStore, /) -> BitStore: bs = self._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 find(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> int: if not bytealigned: return self._bitarray.find(bs._bitarray, start, end) try: return next(self.findall_msb0(bs, start, end, bytealigned)) except StopIteration: return -1 def rfind(self, bs: BitStore, start: int, end: int, bytealigned: bool = False): if not bytealigned: return self._bitarray.find(bs._bitarray, start, end, right=True) try: return next(self.rfindall_msb0(bs, start, end, bytealigned)) except StopIteration: return -1 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.tobytes() # 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.itersearch(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.itersearch(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 _copy(self) -> BitStore: """Always creates a copy, even if instance is immutable.""" return BitStore(self._bitarray) def copy(self) -> BitStore: return self if self.immutable else self._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: start, stop = offset_start_stop_lsb0(start, stop, len(self)) return BitStore(self._bitarray[start: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 any_set(self) -> bool: return self._bitarray.any() def all_set(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) bitstring-bitstring-4.2.3/bitstring/bitstore_helpers.py000066400000000000000000000216231462444752600235060ustar00rootroot00000000000000from __future__ import annotations import struct import math import functools from typing import Union, Optional, Dict, Callable import bitarray from bitstring.bitstore import BitStore 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) # The size of various caches used to improve performance CACHE_SIZE = 256 def tidy_input_string(s: str) -> str: """Return string made lowercase and with all whitespace and underscores removed.""" try: t = s.split() except (AttributeError, TypeError): raise ValueError(f"Expected str object but received a {type(s)} with value {s}.") return ''.join(t).lower().replace('_', '') @functools.lru_cache(CACHE_SIZE) def str_to_bitstore(s: str) -> BitStore: _, tokens = bitstring.utils.tokenparser(s) bs = BitStore() for token in tokens: bs += bitstore_from_token(*token) bs.immutable = True return bs def bin2bitstore(binstring: str) -> BitStore: binstring = tidy_input_string(binstring) binstring = binstring.replace('0b', '') try: return BitStore(binstring) except ValueError: raise bitstring.CreationError(f"Invalid character in bin initialiser {binstring}.") def bin2bitstore_unsafe(binstring: str) -> BitStore: return BitStore(binstring) def hex2bitstore(hexstring: str) -> BitStore: 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 BitStore(ba) def oct2bitstore(octstring: str) -> BitStore: 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 BitStore(ba) def ue2bitstore(i: Union[str, int]) -> BitStore: i = int(i) if i < 0: raise bitstring.CreationError("Cannot use negative initialiser for unsigned exponential-Golomb.") if i == 0: return BitStore('1') tmp = i + 1 leadingzeros = -1 while tmp > 0: tmp >>= 1 leadingzeros += 1 remainingpart = i + 1 - (1 << leadingzeros) return BitStore('0' * leadingzeros + '1') + int2bitstore(remainingpart, leadingzeros, False) def se2bitstore(i: Union[str, int]) -> BitStore: i = int(i) if i > 0: u = (i * 2) - 1 else: u = -2 * i return ue2bitstore(u) def uie2bitstore(i: Union[str, int]) -> BitStore: i = int(i) if i < 0: raise bitstring.CreationError("Cannot use negative initialiser for unsigned interleaved exponential-Golomb.") return BitStore('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1') def sie2bitstore(i: Union[str, int]) -> BitStore: i = int(i) if i == 0: return BitStore('1') else: return uie2bitstore(abs(i)) + (BitStore('1') if i < 0 else BitStore('0')) def bfloat2bitstore(f: Union[str, float], big_endian: bool) -> BitStore: f = float(f) fmt = '>f' if big_endian else ' 0 else float('-inf')) return BitStore.frombytes(b[0:2]) if big_endian else BitStore.frombytes(b[2:4]) def p4binary2bitstore(f: Union[str, float]) -> BitStore: f = float(f) u = p4binary_fmt.float_to_int8(f) return int2bitstore(u, 8, False) def p3binary2bitstore(f: Union[str, float]) -> BitStore: f = float(f) u = p3binary_fmt.float_to_int8(f) return int2bitstore(u, 8, False) def e4m3mxfp2bitstore(f: Union[str, float]) -> BitStore: 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]) -> BitStore: 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]) -> BitStore: 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]) -> BitStore: 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]) -> BitStore: 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]) -> BitStore: f = float(f) if math.isnan(f): return BitStore('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]) -> BitStore: 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 BitStore('01111111') if f <= -128: # -2 return BitStore('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) def int2bitstore(i: int, length: int, signed: bool) -> BitStore: i = int(i) try: x = BitStore(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) -> BitStore: x = int2bitstore(i, length, signed).tobytes() return BitStore.frombytes(x[::-1]) def float2bitstore(f: Union[str, float], length: int, big_endian: bool) -> BitStore: f = float(f) fmt = {16: '>e', 32: '>f', 64: '>d'}[length] if big_endian else {16: ' 0 else float('-inf')) return BitStore.frombytes(b) literal_bit_funcs: Dict[str, Callable[..., BitStore]] = { '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]) -> BitStore: 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 bitstring-bitstring-4.2.3/bitstring/bitstream.py000066400000000000000000000705311462444752600221250ustar00rootroot00000000000000from __future__ import annotations import bitstring from bitstring.bits import Bits, BitsType from bitstring.dtypes import Dtype from typing import Union, List, Any, Optional, overload, TypeVar, Tuple import copy import numbers TConstBitStream = TypeVar("TConstBitStream", bound='ConstBitStream') class ConstBitStream(Bits): """A container or stream holding an immutable sequence of bits. For a mutable container use the BitStream class instead. Methods inherited from Bits: all() -- Check if all specified bits are set to 1 or 0. any() -- Check if any of specified bits are set to 1 or 0. copy() -- Return a copy of the bitstring. count() -- Count the number of bits set to 1 or 0. cut() -- Create generator of constant sized chunks. endswith() -- Return whether the bitstring ends with a sub-string. find() -- Find a sub-bitstring in the current bitstring. findall() -- Find all occurrences of a sub-bitstring in the current bitstring. fromstring() -- Create a bitstring from a formatted string. join() -- Join bitstrings together using current bitstring. pp() -- Pretty print the bitstring. 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. Other methods: bytealign() -- Align to next byte boundary. peek() -- Peek at and interpret next bits as a single item. peeklist() -- Peek at and interpret next bits as a list of items. read() -- Read and interpret next bits as a single item. readlist() -- Read and interpret next bits as a list of items. readto() -- Read up to and including next occurrence of a bitstring. Special methods: Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. Properties: [GENERATED_PROPERTY_DESCRIPTIONS] len -- Length of the bitstring in bits. pos -- The current bit position in the bitstring. """ __slots__ = ('_pos',) def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, offset: Optional[int] = None, pos: int = 0, **kwargs) -> None: """Either specify an 'auto' initialiser: A string of comma separated tokens, an integer, a file object, a bytearray, a boolean iterable or another bitstring. Or initialise via **kwargs with one (and only one) of: bin -- binary string representation, e.g. '0b001010'. hex -- hexadecimal string representation, e.g. '0x2ef' oct -- octal string representation, e.g. '0o777'. bytes -- raw data as a bytes object, for example read from a binary file. int -- a signed integer. uint -- an unsigned integer. float / floatbe -- a big-endian floating point number. bool -- a boolean (True or False). se -- a signed exponential-Golomb code. ue -- an unsigned exponential-Golomb code. sie -- a signed interleaved exponential-Golomb code. uie -- an unsigned interleaved exponential-Golomb code. floatle -- a little-endian floating point number. floatne -- a native-endian floating point number. bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. bfloatle -- a little-endian bfloat format 16-bit floating point number. bfloatne -- a native-endian bfloat format 16-bit floating point number. intbe -- a signed big-endian whole byte integer. intle -- a signed little-endian whole byte integer. intne -- a signed native-endian whole byte integer. uintbe -- an unsigned big-endian whole byte integer. uintle -- an unsigned little-endian whole byte integer. uintne -- an unsigned native-endian whole byte integer. filename -- the path of a file which will be opened in binary read-only mode. Other keyword arguments: length -- length of the bitstring in bits, if needed and appropriate. It must be supplied for all integer and float initialisers. offset -- bit offset to the data. These offset bits are ignored and this is mainly intended for use when initialising using 'bytes' or 'filename'. pos -- Initial bit position, defaults to 0. """ if pos < 0: pos += len(self._bitstore) if pos < 0 or pos > len(self._bitstore): raise bitstring.CreationError(f"Cannot set pos to {pos} when length is {len(self._bitstore)}.") self._pos = pos self._bitstore.immutable = True def _setbytepos(self, bytepos: int) -> None: """Move to absolute byte-aligned position in stream.""" self._setbitpos(bytepos * 8) def _getbytepos(self) -> int: """Return the current position in the stream in bytes. Must be byte aligned.""" if self._pos % 8: raise bitstring.ByteAlignError("Not byte aligned when using bytepos property.") return self._pos // 8 def _setbitpos(self, pos: int) -> None: """Move to absolute position bit in bitstream.""" if pos < 0: raise ValueError("Bit position cannot be negative.") if pos > len(self): raise ValueError("Cannot seek past the end of the data.") self._pos = pos def _getbitpos(self) -> int: """Return the current position in the stream in bits.""" return self._pos def _clear(self) -> None: Bits._clear(self) self._pos = 0 def __copy__(self: TConstBitStream) -> TConstBitStream: """Return a new copy of the ConstBitStream for the copy module.""" # Note that if you want a new copy (different ID), use _copy instead. # The copy can use the same datastore as it's immutable. s = self.__class__() s._bitstore = self._bitstore # Reset the bit position, don't copy it. s._pos = 0 return s def __and__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: """Bit-wise 'and' between two bitstrings. Returns new bitstring. bs -- The bitstring to '&' with. Raises ValueError if the two bitstrings have differing lengths. """ s = Bits.__and__(self, bs) s._pos = 0 return s def __or__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: """Bit-wise 'or' between two bitstrings. Returns new bitstring. bs -- The bitstring to '|' with. Raises ValueError if the two bitstrings have differing lengths. """ s = Bits.__or__(self, bs) s._pos = 0 return s def __xor__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: """Bit-wise 'xor' between two bitstrings. Returns new bitstring. bs -- The bitstring to '^' with. Raises ValueError if the two bitstrings have differing lengths. """ s = Bits.__xor__(self, bs) s._pos = 0 return s def __add__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream: """Concatenate bitstrings and return new bitstring. bs -- the bitstring to append. """ s = Bits.__add__(self, bs) s._pos = 0 return s def append(self, bs: BitsType, /) -> None: """Append a bitstring to the current bitstring. bs -- The bitstring to append. The current bit position will be moved to the end of the BitStream. """ self._append(bs) self._pos = len(self) def __repr__(self) -> str: """Return representation that could be used to recreate the bitstring. If the returned string is too long it will be truncated. See __str__(). """ return self._repr(self.__class__.__name__, len(self), self._pos) def overwrite(self, bs: BitsType, /, pos: Optional[int] = None) -> None: """Overwrite with bitstring at bit position pos. bs -- The bitstring to overwrite with. pos -- The bit position to begin overwriting from. The current bit position will be moved to the end of the overwritten section. Raises ValueError if pos < 0 or pos > len(self). """ bs = Bits._create_from_bitstype(bs) if len(bs) == 0: return if pos is None: pos = self._pos if pos < 0: pos += len(self) if pos < 0 or pos > len(self): raise ValueError("Overwrite starts outside boundary of bitstring.") self._overwrite(bs, pos) self._pos = pos + len(bs) def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: """Find first occurrence of substring bs. Returns a single item tuple with the bit position if found, or an empty tuple if not found. The bit position (pos property) will also be set to the start of the substring if it is found. bs -- The bitstring to find. start -- The bit position to start the search. Defaults to 0. end -- The bit position one past the last bit to search. Defaults to len(self). bytealigned -- If True the bitstring will only be found on byte boundaries. Raises ValueError if bs is empty, if start < 0, if end > len(self) or if end < start. >>> BitStream('0xc3e').find('0b1111') (6,) """ p = super().find(bs, start, end, bytealigned) if p: self._pos = p[0] return p def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None, bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]: """Find final occurrence of substring bs. Returns a single item tuple with the bit position if found, or an empty tuple if not found. The bit position (pos property) will also be set to the start of the substring if it is found. bs -- The bitstring to find. start -- The bit position to end the reverse search. Defaults to 0. end -- The bit position one past the first bit to reverse search. Defaults to len(self). bytealigned -- If True the bitstring will only be found on byte boundaries. Raises ValueError if bs is empty, if start < 0, if end > len(self) or if end < start. """ p = super().rfind(bs, start, end, bytealigned) if p: self._pos = p[0] return p @overload def read(self, fmt: int) -> Bits: ... @overload def read(self, fmt: str) -> Any: ... def read(self, fmt: Union[int, str, Dtype]) -> Union[int, float, str, Bits, bool, bytes, None]: """Interpret next bits according to the format string and return result. fmt -- Token string describing how to interpret the next bits. Token examples: 'int:12' : 12 bits as a signed integer 'uint:8' : 8 bits as an unsigned integer 'float:64' : 8 bytes as a big-endian float 'intbe:16' : 2 bytes as a big-endian signed integer 'uintbe:16' : 2 bytes as a big-endian unsigned integer 'intle:32' : 4 bytes as a little-endian signed integer 'uintle:32' : 4 bytes as a little-endian unsigned integer 'floatle:64': 8 bytes as a little-endian float 'intne:24' : 3 bytes as a native-endian signed integer 'uintne:24' : 3 bytes as a native-endian unsigned integer 'floatne:32': 4 bytes as a native-endian float 'hex:80' : 80 bits as a hex string 'oct:9' : 9 bits as an octal string 'bin:1' : single bit binary string 'ue' : next bits as unsigned exp-Golomb code 'se' : next bits as signed exp-Golomb code 'uie' : next bits as unsigned interleaved exp-Golomb code 'sie' : next bits as signed interleaved exp-Golomb code 'bits:5' : 5 bits as a bitstring 'bytes:10' : 10 bytes as a bytes object 'bool' : 1 bit as a bool 'pad:3' : 3 bits of padding to ignore - returns None fmt may also be an integer, which will be treated like the 'bits' token. The position in the bitstring is advanced to after the read items. Raises ReadError if not enough bits are available. Raises ValueError if the format is not understood. """ p = self._pos if isinstance(fmt, numbers.Integral): if fmt < 0: raise ValueError("Cannot read negative amount.") if fmt > len(self) - self._pos: raise bitstring.ReadError(f"Cannot read {fmt} bits, only {len(self) - self._pos} available.") bs = self._slice(self._pos, self._pos + fmt) self._pos += fmt return bs dtype = bitstring.dtypes.Dtype(fmt) if dtype.bitlength is None and not dtype.variable_length: # No length specified? Try again, but read to end. bitlength = len(self) - self._pos items, remainder = divmod(bitlength, dtype.bits_per_item) if remainder != 0: raise ValueError( f"The '{dtype.name}' type must have a bit length that is a multiple of {dtype.bits_per_item}" f" so cannot be read from the {bitlength} bits that are available.") dtype = bitstring.dtypes.Dtype(fmt, items) if dtype.bitlength is not None: val = dtype.read_fn(self, self._pos) self._pos += dtype.bitlength else: val, self._pos = dtype.read_fn(self, self._pos) if self._pos > len(self): self._pos = p raise bitstring.ReadError(f"Reading off end of bitstring with fmt '{fmt}'. Only {len(self) - p} bits available.") return val def readlist(self, fmt: Union[str, List[Union[int, str, Dtype]]], **kwargs) \ -> List[Union[int, float, str, Bits, bool, bytes, None]]: """Interpret next bits according to format string(s) and return list. fmt -- A single string or list of strings with comma separated tokens describing how to interpret the next bits in the bitstring. Items can also be integers, for reading new bitstring of the given length. kwargs -- A dictionary or keyword-value pairs - the keywords used in the format string will be replaced with their given value. The position in the bitstring is advanced to after the read items. Raises ReadError is not enough bits are available. Raises ValueError if the format is not understood. See the docstring for 'read' for token examples. 'pad' tokens are skipped and not added to the returned list. >>> h, b1, b2 = s.readlist('hex:20, bin:5, bin:3') >>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10]) """ value, self._pos = self._readlist(fmt, self._pos, **kwargs) return value def readto(self: TConstBitStream, bs: BitsType, /, bytealigned: Optional[bool] = None) -> TConstBitStream: """Read up to and including next occurrence of bs and return result. bs -- The bitstring to find. bytealigned -- If True the bitstring will only be found on byte boundaries. Raises ValueError if bs is empty. Raises ReadError if bs is not found. """ if isinstance(bs, numbers.Integral): raise ValueError("Integers cannot be searched for") bs = Bits._create_from_bitstype(bs) oldpos = self._pos p = self.find(bs, self._pos, bytealigned=bytealigned) if not p: raise bitstring.ReadError("Substring not found") self._pos += len(bs) return self._slice(oldpos, self._pos) @overload def peek(self: TConstBitStream, fmt: int) -> TConstBitStream: ... @overload def peek(self, fmt: str) -> Union[int, float, str, TConstBitStream, bool, bytes, None]: ... def peek(self: TConstBitStream, fmt: Union[int, str]) -> Union[int, float, str, TConstBitStream, bool, bytes, None]: """Interpret next bits according to format string and return result. fmt -- Token string describing how to interpret the next bits. The position in the bitstring is not changed. If not enough bits are available then all bits to the end of the bitstring will be used. Raises ReadError if not enough bits are available. Raises ValueError if the format is not understood. See the docstring for 'read' for token examples. """ pos_before = self._pos value = self.read(fmt) self._pos = pos_before return value def peeklist(self, fmt: Union[str, List[Union[int, str]]], **kwargs) \ -> List[Union[int, float, str, Bits, None]]: """Interpret next bits according to format string(s) and return list. fmt -- One or more integers or strings with comma separated tokens describing how to interpret the next bits in the bitstring. kwargs -- A dictionary or keyword-value pairs - the keywords used in the format string will be replaced with their given value. The position in the bitstring is not changed. If not enough bits are available then all bits to the end of the bitstring will be used. Raises ReadError if not enough bits are available. Raises ValueError if the format is not understood. See the docstring for 'read' for token examples. """ pos = self._pos return_values = self.readlist(fmt, **kwargs) self._pos = pos return return_values def bytealign(self) -> int: """Align to next byte and return number of skipped bits. Raises ValueError if the end of the bitstring is reached before aligning to the next byte. """ skipped = (8 - (self._pos % 8)) % 8 self.pos += skipped return skipped @classmethod def fromstring(cls: TBits, s: str, /) -> TBits: x = super().fromstring(s) x._pos = 0 x._bitstore.immutable = True return x @overload def __getitem__(self: TBits, key: slice, /) -> TBits: ... @overload def __getitem__(self: TBits, key: int, /) -> bool: ... def __getitem__(self: TBits, key: Union[slice, int], /) -> Union[TBits, bool]: """Return a new bitstring representing a slice of the current bitstring.""" if isinstance(key, numbers.Integral): return bool(self._bitstore.getindex(key)) bs = super().__new__(self.__class__) bs._bitstore = self._bitstore.getslice_withstep(key) bs._pos = 0 return bs pos = property(_getbitpos, _setbitpos, doc="""The position in the bitstring in bits. Read and write. """) bitpos = property(_getbitpos, _setbitpos, doc="""The position in the bitstring in bits. Read and write. """) bytepos = property(_getbytepos, _setbytepos, doc="""The position in the bitstring in bytes. Read and write. """) class BitStream(ConstBitStream, bitstring.BitArray): """A container or stream holding a mutable sequence of bits Subclass of the ConstBitStream and BitArray classes. Inherits all of their methods. Methods: all() -- Check if all specified bits are set to 1 or 0. any() -- Check if any of specified bits are set to 1 or 0. append() -- Append a bitstring. bytealign() -- Align to next byte boundary. byteswap() -- Change byte endianness in-place. clear() -- Remove all bits from the bitstring. copy() -- Return a copy of the bitstring. count() -- Count the number of bits set to 1 or 0. cut() -- Create generator of constant sized chunks. endswith() -- Return whether the bitstring ends with a sub-string. find() -- Find a sub-bitstring in the current bitstring. findall() -- Find all occurrences of a sub-bitstring in the current bitstring. fromstring() -- Create a bitstring from a formatted string. insert() -- Insert a bitstring. invert() -- Flip bit(s) between one and zero. join() -- Join bitstrings together using current bitstring. overwrite() -- Overwrite a section with a new bitstring. peek() -- Peek at and interpret next bits as a single item. peeklist() -- Peek at and interpret next bits as a list of items. pp() -- Pretty print the bitstring. prepend() -- Prepend a bitstring. read() -- Read and interpret next bits as a single item. readlist() -- Read and interpret next bits as a list of items. readto() -- Read up to and including next occurrence of a bitstring. replace() -- Replace occurrences of one bitstring with another. reverse() -- Reverse bits in-place. rfind() -- Seek backwards to find a sub-bitstring. rol() -- Rotate bits to the left. ror() -- Rotate bits to the right. 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. Special methods: Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= in addition to [], ==, !=, +, *, ~, <<, >>, &, | and ^. Properties: [GENERATED_PROPERTY_DESCRIPTIONS] len -- Length of the bitstring in bits. pos -- The current bit position in the bitstring. """ __slots__ = () def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, offset: Optional[int] = None, pos: int = 0, **kwargs) -> None: """Either specify an 'auto' initialiser: A string of comma separated tokens, an integer, a file object, a bytearray, a boolean iterable or another bitstring. Or initialise via **kwargs with one (and only one) of: bin -- binary string representation, e.g. '0b001010'. hex -- hexadecimal string representation, e.g. '0x2ef' oct -- octal string representation, e.g. '0o777'. bytes -- raw data as a bytes object, for example read from a binary file. int -- a signed integer. uint -- an unsigned integer. float / floatbe -- a big-endian floating point number. bool -- a boolean (True or False). se -- a signed exponential-Golomb code. ue -- an unsigned exponential-Golomb code. sie -- a signed interleaved exponential-Golomb code. uie -- an unsigned interleaved exponential-Golomb code. floatle -- a little-endian floating point number. floatne -- a native-endian floating point number. bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number. bfloatle -- a little-endian bfloat format 16-bit floating point number. bfloatne -- a native-endian bfloat format 16-bit floating point number. intbe -- a signed big-endian whole byte integer. intle -- a signed little-endian whole byte integer. intne -- a signed native-endian whole byte integer. uintbe -- an unsigned big-endian whole byte integer. uintle -- an unsigned little-endian whole byte integer. uintne -- an unsigned native-endian whole byte integer. filename -- the path of a file which will be opened in binary read-only mode. Other keyword arguments: length -- length of the bitstring in bits, if needed and appropriate. It must be supplied for all integer and float initialisers. offset -- bit offset to the data. These offset bits are ignored and this is intended for use when initialising using 'bytes' or 'filename'. pos -- Initial bit position, defaults to 0. """ ConstBitStream.__init__(self, auto, length, offset, pos, **kwargs) if self._bitstore.immutable: self._bitstore = self._bitstore._copy() self._bitstore.immutable = False def __copy__(self) -> BitStream: """Return a new copy of the BitStream.""" s_copy = object.__new__(BitStream) s_copy._pos = 0 s_copy._bitstore = self._bitstore.copy() return s_copy def __iadd__(self, bs: BitsType, /) -> BitStream: """Append to current bitstring. Return self. bs -- the bitstring to append. The current bit position will be moved to the end of the BitStream. """ self._append(bs) self._pos = len(self) return self def prepend(self, bs: BitsType, /) -> None: """Prepend a bitstring to the current bitstring. bs -- The bitstring to prepend. """ bs = Bits._create_from_bitstype(bs) super().prepend(bs) self._pos = 0 def __setitem__(self, /, key: Union[slice, int], value: BitsType) -> None: length_before = len(self) super().__setitem__(key, value) if len(self) != length_before: self._pos = 0 return def __delitem__(self, /, key: Union[slice, int]) -> None: """Delete item or range. >>> a = BitStream('0x001122') >>> del a[8:16] >>> print a 0x0022 """ length_before = len(self) self._bitstore.__delitem__(key) if len(self) != length_before: self._pos = 0 def insert(self, bs: BitsType, /, pos: Optional[int] = None) -> None: """Insert bitstring at bit position pos. bs -- The bitstring to insert. pos -- The bit position to insert at. The current bit position will be moved to the end of the inserted section. Raises ValueError if pos < 0 or pos > len(self). """ bs = Bits._create_from_bitstype(bs) if len(bs) == 0: return if bs is self: bs = self._copy() if pos is None: pos = self._pos if pos < 0: pos += len(self) if not 0 <= pos <= len(self): raise ValueError("Invalid insert position.") self._insert(bs, pos) self._pos = pos + len(bs) def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int: """Replace all occurrences of old with new in place. Returns number of replacements made. old -- The bitstring to replace. new -- The replacement bitstring. start -- Any occurrences that start before this will not be replaced. Defaults to 0. end -- Any occurrences that finish after this will not be replaced. Defaults to len(self). count -- The maximum number of replacements to make. Defaults to replace all occurrences. bytealigned -- If True replacements will only be made on byte boundaries. Raises ValueError if old is empty or if start or end are out of range. """ if count == 0: return 0 if len(old := Bits._create_from_bitstype(old)) == 0: raise ValueError("Empty bitstring cannot be replaced.") start, end = self._validate_slice(start, end) new = Bits._create_from_bitstype(new) if new is self: # Prevent self assignment woes new = copy.copy(self) length_before = len(self) replacement_count = self._replace(old, new, start, end, 0 if count is None else count, bytealigned) if len(self) != length_before: self._pos = 0 return replacement_countbitstring-bitstring-4.2.3/bitstring/bitstring_options.py000066400000000000000000000067501462444752600237150ustar00rootroot00000000000000from __future__ import annotations import bitstring import os class Options: """Internal class to create singleton module options instance.""" _instance = None def __init__(self): self.set_lsb0(False) self._bytealigned = False self.mxfp_overflow = 'saturate' self.no_color = False no_color = os.getenv('NO_COLOR') self.no_color = True if no_color else False @property def mxfp_overflow(self) -> str: return self._mxfp_overflow @mxfp_overflow.setter def mxfp_overflow(self, value: str) -> None: allowed_values = ('saturate', 'overflow') if value not in allowed_values: raise ValueError(f"mxfp_overflow must be one of {allowed_values}, not {value}.") self._mxfp_overflow = value def __repr__(self) -> str: attributes = {attr: getattr(self, attr) for attr in dir(self) if not attr.startswith('_') and not callable(getattr(self, attr))} return '\n'.join(f"{attr}: {value!r}" for attr, value in attributes.items()) @property def lsb0(self) -> bool: return self._lsb0 @lsb0.setter def lsb0(self, value: bool) -> None: self.set_lsb0(value) def set_lsb0(self, value: bool) -> None: self._lsb0 = bool(value) Bits = bitstring.bits.Bits BitArray = bitstring.bitarray_.BitArray BitStore = bitstring.bitstore.BitStore lsb0_methods = { Bits: {'_find': Bits._find_lsb0, '_rfind': Bits._rfind_lsb0, '_findall': Bits._findall_lsb0}, BitArray: {'_ror': BitArray._rol_msb0, '_rol': BitArray._ror_msb0, '_append': BitArray._append_lsb0, '_prepend': BitArray._append_msb0}, BitStore: {'__setitem__': BitStore.setitem_lsb0, '__delitem__': BitStore.delitem_lsb0, 'getindex': BitStore.getindex_lsb0, 'getslice': BitStore.getslice_lsb0, 'getslice_withstep': BitStore.getslice_withstep_lsb0, 'invert': BitStore.invert_lsb0} } msb0_methods = { Bits: {'_find': Bits._find_msb0, '_rfind': Bits._rfind_msb0, '_findall': Bits._findall_msb0}, BitArray: {'_ror': BitArray._ror_msb0, '_rol': BitArray._rol_msb0, '_append': BitArray._append_msb0, '_prepend': BitArray._append_lsb0}, BitStore: {'__setitem__': BitStore.setitem_msb0, '__delitem__': BitStore.delitem_msb0, 'getindex': BitStore.getindex_msb0, 'getslice': BitStore.getslice_msb0, 'getslice_withstep': BitStore.getslice_withstep_msb0, 'invert': BitStore.invert_msb0} } methods = lsb0_methods if self._lsb0 else msb0_methods for cls, method_dict in methods.items(): for attr, method in method_dict.items(): setattr(cls, attr, method) @property def bytealigned(self) -> bool: return self._bytealigned @bytealigned.setter def bytealigned(self, value: bool) -> None: self._bytealigned = bool(value) def __new__(cls): if cls._instance is None: cls._instance = super(Options, cls).__new__(cls) return cls._instance class Colour: def __new__(cls, use_colour: bool) -> Colour: x = super().__new__(cls) if use_colour: cls.blue = '\033[34m' cls.purple = '\033[35m' cls.green = '\033[32m' cls.off = '\033[0m' else: cls.blue = cls.purple = cls.green = cls.off = '' return x bitstring-bitstring-4.2.3/bitstring/dtypes.py000066400000000000000000000410011462444752600214310ustar00rootroot00000000000000from __future__ import annotations import functools from typing import Optional, Dict, Any, Union, Tuple, Callable import inspect import bitstring from bitstring import utils CACHE_SIZE = 256 def scaled_get_fn(get_fn, s: Union[int, float]): def wrapper(*args, scale=s, **kwargs): return get_fn(*args, **kwargs) * scale return wrapper def scaled_set_fn(set_fn, s: Union[int, float]): def wrapper(bs, value, *args, scale=s, **kwargs): return set_fn(bs, value / scale, *args, **kwargs) return wrapper def scaled_read_fn(read_fn, s: Union[int, float]): def wrapper(*args, scale=s, **kwargs): val = read_fn(*args, **kwargs) if isinstance(val, tuple): val, pos = val return val * scale, pos return val * scale return wrapper class Dtype: """A data type class, representing a concrete interpretation of binary data. Dtype instances are immutable. They are often created implicitly elsewhere via a token string. >>> u12 = Dtype('uint', 12) # length separate from token string. >>> float16 = Dtype('float16') # length part of token string. >>> mxfp = Dtype('e3m2mxfp', scale=2 ** 6) # dtype with scaling factor """ _name: str _read_fn: Callable _set_fn: Callable _get_fn: Callable _return_type: Any _is_signed: bool _set_fn_needs_length: bool _variable_length: bool _bitlength: Optional[int] _bits_per_item: int _length: Optional[int] _scale: Union[None, float, int] def __new__(cls, token: Union[str, Dtype], /, length: Optional[int] = None, scale: Union[None, float, int] = None) -> Dtype: if isinstance(token, cls): return token if length is None: x = cls._new_from_token(token, scale) return x else: x = dtype_register.get_dtype(token, length, scale) return x @property def scale(self) -> Union[int, float, None]: """The multiplicative scale applied when interpreting the data.""" return self._scale @property def name(self) -> str: """A string giving the name of the data type.""" return self._name @property def length(self) -> int: """The length of the data type in units of bits_per_item. Set to None for variable length dtypes.""" return self._length @property def bitlength(self) -> Optional[int]: """The number of bits needed to represent a single instance of the data type. Set to None for variable length dtypes.""" return self._bitlength @property def bits_per_item(self) -> int: """The number of bits for each unit of length. Usually 1, but equals 8 for bytes type.""" return self._bits_per_item @property def variable_length(self) -> bool: """If True then the length of the data type depends on the data being interpreted, and must not be specified.""" return self._variable_length @property def return_type(self) -> Any: """The type of the value returned by the parse method, such as int, float or str.""" return self._return_type @property def is_signed(self) -> bool: """If True then the data type represents a signed quantity.""" return self._is_signed @property def set_fn(self) -> Optional[Callable]: """A function to set the value of the data type.""" return self._set_fn @property def get_fn(self) -> Callable: """A function to get the value of the data type.""" return self._get_fn @property def read_fn(self) -> Callable: """A function to read the value of the data type.""" return self._read_fn def _set_scale(self, value: Union[None, float, int]) -> None: self._scale = value if self._scale is None: return if self._scale == 0: raise ValueError("A Dtype's scale factor must not be zero.") if not hasattr(self, 'unscaled_get_fn'): self.unscaled_get_fn = self._get_fn self.unscaled_set_fn = self._set_fn self.unscaled_read_fn = self._read_fn self._get_fn = scaled_get_fn(self.unscaled_get_fn, self._scale) self._set_fn = scaled_set_fn(self.unscaled_set_fn, self._scale) self._read_fn = scaled_read_fn(self.unscaled_read_fn, self._scale) @classmethod @functools.lru_cache(CACHE_SIZE) def _new_from_token(cls, token: str, scale: Union[None, float, int] = None) -> Dtype: token = ''.join(token.split()) return dtype_register.get_dtype(*utils.parse_name_length_token(token), scale=scale) def __hash__(self) -> int: return hash((self._name, self._length)) @classmethod @functools.lru_cache(CACHE_SIZE) def _create(cls, definition: DtypeDefinition, length: Optional[int], scale: Union[None, float, int]) -> Dtype: x = super().__new__(cls) x._name = definition.name x._bitlength = x._length = length x._bits_per_item = definition.multiplier if x._bitlength is not None: x._bitlength *= x._bits_per_item x._set_fn_needs_length = definition.set_fn_needs_length x._variable_length = definition.variable_length if x._variable_length or dtype_register.names[x._name].allowed_lengths.only_one_value(): x._read_fn = definition.read_fn else: x._read_fn = functools.partial(definition.read_fn, length=x._bitlength) if definition.set_fn is None: x._set_fn = None else: if x._set_fn_needs_length: x._set_fn = functools.partial(definition.set_fn, length=x._bitlength) else: x._set_fn = definition.set_fn x._get_fn = definition.get_fn x._return_type = definition.return_type x._is_signed = definition.is_signed x._set_scale(scale) return x def build(self, value: Any, /) -> bitstring.Bits: """Create a bitstring from a value. The value parameter should be of a type appropriate to the dtype. """ b = bitstring.Bits() self._set_fn(b, value) if self.bitlength is not None and len(b) != self.bitlength: raise ValueError(f"Dtype has a length of {self.bitlength} bits, but value '{value}' has {len(b)} bits.") return b def parse(self, b: BitsType, /) -> Any: """Parse a bitstring to find its value. The b parameter should be a bitstring of the appropriate length, or an object that can be converted to a bitstring.""" b = bitstring.Bits._create_from_bitstype(b) return self._get_fn(bitstring.Bits(b)) def __str__(self) -> str: if self._scale is not None: return self.__repr__() hide_length = self._variable_length or dtype_register.names[self._name].allowed_lengths.only_one_value() or self._length is None length_str = '' if hide_length else str(self._length) return f"{self._name}{length_str}" def __repr__(self) -> str: hide_length = self._variable_length or dtype_register.names[self._name].allowed_lengths.only_one_value() or self._length is None length_str = '' if hide_length else ', ' + str(self._length) if self._scale is None: scale_str = '' else: try: # This will only succeed for powers of two from -127 to 127. e8m0 = bitstring.Bits(e8m0mxfp=self._scale) except ValueError: scale_str = f', scale={self._scale}' else: power_of_two = e8m0.uint - 127 if power_of_two in [0, 1]: scale_str = f', scale={self._scale}' else: scale_str = f', scale=2 ** {power_of_two}' return f"{self.__class__.__name__}('{self._name}'{length_str}{scale_str})" def __eq__(self, other: Any) -> bool: if isinstance(other, Dtype): return self._name == other._name and self._length == other._length return False class AllowedLengths: def __init__(self, value: Tuple[int, ...] = tuple()) -> None: if len(value) >= 3 and value[-1] is Ellipsis: step = value[1] - value[0] for i in range(1, len(value) - 1): if value[i] - value[i - 1] != step: raise ValueError(f"Allowed length tuples must be equally spaced when final element is Ellipsis, but got {value}.") self.values = (value[0], value[1], Ellipsis) else: self.values = value def __str__(self) -> str: if self.values and self.values[-1] is Ellipsis: return f"({self.values[0]}, {self.values[1]}, ...)" return str(self.values) def __contains__(self, other: Any) -> bool: if not self.values: return True if self.values[-1] is Ellipsis: return (other - self.values[0]) % (self.values[1] - self.values[0]) == 0 return other in self.values def only_one_value(self) -> bool: return self.values and len(self.values) == 1 class DtypeDefinition: """Represents a class of dtypes, such as uint or float, rather than a concrete dtype such as uint8. Not (yet) part of the public interface.""" def __init__(self, name: str, set_fn, get_fn, return_type: Any = Any, is_signed: bool = False, bitlength2chars_fn=None, variable_length: bool = False, allowed_lengths: Tuple[int, ...] = tuple(), multiplier: int = 1, description: str = ''): # Consistency checks if int(multiplier) != multiplier or multiplier <= 0: raise ValueError("multiplier must be an positive integer") if variable_length and allowed_lengths: raise ValueError("A variable length dtype can't have allowed lengths.") if variable_length and set_fn is not None and 'length' in inspect.signature(set_fn).parameters: raise ValueError("A variable length dtype can't have a set_fn which takes a length.") self.name = name self.description = description self.return_type = return_type self.is_signed = is_signed self.variable_length = variable_length self.allowed_lengths = AllowedLengths(allowed_lengths) self.multiplier = multiplier # Can work out if set_fn needs length based on its signature. self.set_fn_needs_length = set_fn is not None and 'length' in inspect.signature(set_fn).parameters self.set_fn = set_fn if self.allowed_lengths.values: def allowed_length_checked_get_fn(bs): if len(bs) not in self.allowed_lengths: if self.allowed_lengths.only_one_value(): raise bitstring.InterpretError(f"'{self.name}' dtypes must have a length of {self.allowed_lengths.values[0]}, but received a length of {len(bs)}.") else: raise bitstring.InterpretError(f"'{self.name}' dtypes must have a length in {self.allowed_lengths}, but received a length of {len(bs)}.") return get_fn(bs) self.get_fn = allowed_length_checked_get_fn # Interpret everything and check the length else: self.get_fn = get_fn # Interpret everything # Create a reading function from the get_fn. if not self.variable_length: if self.allowed_lengths.only_one_value(): def read_fn(bs, start): return self.get_fn(bs[start:start + self.allowed_lengths.values[0]]) else: def read_fn(bs, start, length): if len(bs) < start + length: raise bitstring.ReadError(f"Needed a length of at least {length} bits, but only {len(bs) - start} bits were available.") return self.get_fn(bs[start:start + length]) self.read_fn = read_fn else: # We only find out the length when we read/get. def length_checked_get_fn(bs): x, length = get_fn(bs) if length != len(bs): raise ValueError return x self.get_fn = length_checked_get_fn def read_fn(bs, start): try: x, length = get_fn(bs[start:]) except bitstring.InterpretError: raise bitstring.ReadError return x, start + length self.read_fn = read_fn self.bitlength2chars_fn = bitlength2chars_fn def get_dtype(self, length: Optional[int] = None, scale: Union[None, float, int] = None) -> Dtype: if self.allowed_lengths: if length is None: if self.allowed_lengths.only_one_value(): length = self.allowed_lengths.values[0] else: if length not in self.allowed_lengths: if self.allowed_lengths.only_one_value(): raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype, but its only allowed length is {self.allowed_lengths.values[0]}.") else: raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype which is not one of its possible lengths (must be one of {self.allowed_lengths}).") if length is None: d = Dtype._create(self, None, scale) return d if self.variable_length: raise ValueError(f"A length ({length}) shouldn't be supplied for the variable length dtype '{self.name}'.") d = Dtype._create(self, length, scale) return d def __repr__(self) -> str: s = f"{self.__class__.__name__}(name='{self.name}', description='{self.description}', return_type={self.return_type.__name__}, " s += f"is_signed={self.is_signed}, set_fn_needs_length={self.set_fn_needs_length}, allowed_lengths={self.allowed_lengths!s}, multiplier={self.multiplier})" return s class Register: """A singleton class that holds all the DtypeDefinitions. Not (yet) part of the public interface.""" _instance: Optional[Register] = None names: Dict[str, DtypeDefinition] = {} def __new__(cls) -> Register: # Singleton. Only one Register instance can ever exist. if cls._instance is None: cls._instance = super(Register, cls).__new__(cls) return cls._instance @classmethod def add_dtype(cls, definition: DtypeDefinition): cls.names[definition.name] = definition if definition.get_fn is not None: setattr(bitstring.bits.Bits, definition.name, property(fget=definition.get_fn, doc=f"The bitstring as {definition.description}. Read only.")) if definition.set_fn is not None: setattr(bitstring.bitarray_.BitArray, definition.name, property(fget=definition.get_fn, fset=definition.set_fn, doc=f"The bitstring as {definition.description}. Read and write.")) @classmethod def add_dtype_alias(cls, name: str, alias: str): cls.names[alias] = cls.names[name] definition = cls.names[alias] if definition.get_fn is not None: setattr(bitstring.bits.Bits, alias, property(fget=definition.get_fn, doc=f"An alias for '{name}'. Read only.")) if definition.set_fn is not None: setattr(bitstring.bitarray_.BitArray, alias, property(fget=definition.get_fn, fset=definition.set_fn, doc=f"An alias for '{name}'. Read and write.")) @classmethod def get_dtype(cls, name: str, length: Optional[int], scale: Union[None, float, int] = None) -> Dtype: try: definition = cls.names[name] except KeyError: raise ValueError(f"Unknown Dtype name '{name}'. Names available: {list(cls.names.keys())}.") else: return definition.get_dtype(length, scale) @classmethod def __getitem__(cls, name: str) -> DtypeDefinition: return cls.names[name] @classmethod def __delitem__(cls, name: str) -> None: del cls.names[name] def __repr__(self) -> str: s = [f"{'key':<12}:{'name':^12}{'signed':^8}{'set_fn_needs_length':^23}{'allowed_lengths':^16}{'multiplier':^12}{'return_type':<13}"] s.append('-' * 85) for key in self.names: m = self.names[key] allowed = '' if not m.allowed_lengths else m.allowed_lengths ret = 'None' if m.return_type is None else m.return_type.__name__ s.append(f"{key:<12}:{m.name:>12}{m.is_signed:^8}{m.set_fn_needs_length:^16}{allowed!s:^16}{m.multiplier:^12}{ret:<13} # {m.description}") return '\n'.join(s) # Create the Register singleton dtype_register = Register() bitstring-bitstring-4.2.3/bitstring/exceptions.py000066400000000000000000000010511462444752600223030ustar00rootroot00000000000000 class Error(Exception): """Base class for errors in the bitstring module.""" def __init__(self, *params: object) -> None: self.msg = params[0] if params else '' self.params = params[1:] class ReadError(Error, IndexError): """Reading or peeking past the end of a bitstring.""" InterpretError = ValueError """Inappropriate interpretation of binary data.""" class ByteAlignError(Error): """Whole-byte position or length needed.""" CreationError = ValueError """Inappropriate argument during bitstring creation.""" bitstring-bitstring-4.2.3/bitstring/fp8.py000066400000000000000000000072701462444752600206300ustar00rootroot00000000000000""" The 8-bit float formats used here are from a proposal supported by Graphcore, AMD and Qualcomm. See https://arxiv.org/abs/2206.02915 """ import struct import zlib import array import bitarray from bitstring.luts import binary8_luts_compressed import math class Binary8Format: """8-bit floating point formats based on draft IEEE binary8""" def __init__(self, exp_bits: int, bias: int): self.exp_bits = exp_bits self.bias = bias self.pos_clamp_value = 0b01111111 self.neg_clamp_value = 0b11111111 def __str__(self): return f"Binary8Format(exp_bits={self.exp_bits}, bias={self.bias})" def decompress_luts(self): binary8_to_float_compressed, float16_to_binary8_compressed = binary8_luts_compressed[(self.exp_bits, self.bias)] self.lut_float16_to_binary8 = zlib.decompress(float16_to_binary8_compressed) dec = zlib.decompress(binary8_to_float_compressed) self.lut_binary8_to_float = struct.unpack(f'<{len(dec) // 4}f', dec) def create_luts(self): self.lut_binary8_to_float = self.createLUT_for_binary8_to_float() self.lut_float16_to_binary8 = self.createLUT_for_float16_to_binary8() def float_to_int8(self, f: float) -> int: """Given a Python float convert to the best float8 (expressed as an integer in 0-255 range).""" # First convert the float to a float16, then a 16 bit uint try: b = struct.pack('>e', f) except (OverflowError, struct.error): # Return the largest representable positive or negative value return self.pos_clamp_value if f > 0 else self.neg_clamp_value f16_int = int.from_bytes(b, byteorder='big') # Then use this as an index to our large LUT return self.lut_float16_to_binary8[f16_int] def createLUT_for_float16_to_binary8(self) -> bytes: # Used to create the LUT that was compressed and stored for the fp8 code import gfloat fi = gfloat.formats.format_info_p3109(8 - self.exp_bits) fp16_to_fp8 = bytearray(1 << 16) for i in range(1 << 16): b = struct.pack('>H', i) f, = struct.unpack('>e', b) fp = gfloat.round_float(fi, f) if math.isnan(fp): fp8_i = 0b10000000 else: fp8_i = self.lut_binary8_to_float.index(fp) fp16_to_fp8[i] = fp8_i return bytes(fp16_to_fp8) 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) sign = b[0] exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits]) significand = b[1 + self.exp_bits:] if exponent == 0: significand = bitarray.bitarray('0') + significand exponent = -self.bias + 1 else: significand = bitarray.bitarray('1') + significand exponent -= self.bias f = float(bitarray.util.ba2int(significand)) / (2.0 ** (7 - self.exp_bits)) f *= 2 ** exponent i2f.append(f if not sign else -f) # One special case for minus zero i2f[0b10000000] = float('nan') # and for plus and minus infinity i2f[0b01111111] = float('inf') i2f[0b11111111] = float('-inf') return array.array('f', i2f) # We create the 1.5.2 and 1.4.3 formats. p4binary_fmt = Binary8Format(exp_bits=4, bias=8) p3binary_fmt = Binary8Format(exp_bits=5, bias=16) def decompress_luts(): p4binary_fmt.decompress_luts() p3binary_fmt.decompress_luts() bitstring-bitstring-4.2.3/bitstring/luts.py000066400000000000000000000647001462444752600211230ustar00rootroot00000000000000# # This file is generated by generate_luts.py. DO NOT EDIT. # mxfp_luts_compressed = \ {(2, 1, 1, 'saturate'): (b'x\x01\x1d\xc9\xc1\r\xc00\x00\xc2@o\xd6\x8c\xc6f\xf5h\xb1\x828\xf1\x00^>X\x0c\xa7f1,\x7f' b'\x13\x83\xfdY\xf4\x027\xf1\x0c\xfb', b"x\x01\xed\xdd\t\r\xc00\x10\x03\xc1\xf4\xff[\xfehS\x1a'y\x96\x81\x87\x80[\x13\x01\x02" b'\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80' b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' b' @\x80\x00\x01\x02e\x05\x06\x11 @\x80@\xa2\xc0(\x02\xc9\x02\x93\x92\x05fe\x0b,J\x16X\x95-' b'\xb0\x89\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04' b'\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00' b'\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x14\x14\xe8"@ U`\x17\x01\x02\x04\x08\x10 ' b'@\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02u\x05\x0e\x11 @' b'\x80@\xa2\xc0)\x02\xc9\x02\x97\x92\x05ne\x0b\xb7\x9b\x00\x81\xfe\x03\\M8\xf2'), (2, 3, 1, 'saturate'): (b'x\x01\x1d\xcdQ\r\xc30\x10\x03\xd0\x830\x08\x85P\x04[ \x14B \x0cB \x04B \x14\xc1v\x10' b'\x02!\x10\x02aO\xb3\xf4\xe4?;\xe2\x9fgD#\x89W\xc4A\xa1\xd2\xe8\x0cn\x92\xc9b\x13%\xe2\xc1' b'\xc1I\xe1\xa2\xf2\xa6\xd1\x19\xdc$\x93\xc5&\x1a\x1fE\x12_[\x14*\x8d\xce\xe0&\x99,6\x91\xfe98' b')\\T\xde4:\x83\x9bd\xb2\xd8\xf9\x03~S=\xdd', b'x\x01\xed\xdd\x85qB\x01\x14DQ\x12\x88\x11#\xeeF\x84\xb8\xbb\xf6_\x15\xa9\x82\xd9\xf9\xf3' b'\xce\xed`O\x03\xdbj\x89\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01' b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' b'\x80\x00\x81\xc6\x08\x8c\x89\x00\x01\x02\x04*\n\x8c\x8b@e\x81\xb6*\x0bt\x94\x15\x98\xa8\xddd' b'\xf5\xa6j7\xdd\xf4f\xb2u\xd3\xcdf\x9bK7\x9fm!\xddb\xb6^\xba\xa5l\xcb\xe9V\xb2\xad\xa6[' b'\xcb\xb6\x9en#\xdbf\xba\xadl\xdb\xe9v\xb2\xed\xa6\xdb\xcb\xb6\x9f\xee@\x04\x08\x10 @\x80' b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' b' @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02' b'\x04\x08\x10\x18\x91\xc0P\x04\x08T\x158\x14\x01\x02\x04\x08\x10 @\x80\x00\x01\x02' b'\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80' b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\xcd\x118\x12\x01\x02\x04\x08T\x14\xe8\x8b@e\x81c' b'U\x168QV\xe0\xb4vg\xd5\x1b\xd4\xee\xbc\xe9]d\xbbLw\x95\xed:\xddM\xb6\xdbtw\xd9\xee\xd3=d{L' b'\xf7\x94\xed9\xddK\xb6\xd7to\xd9\xde\xd3}d\xfbL\xf7\x95\xed;\xddO\xb6\xdft\x7f"@\x80\x00\x01' b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08' b'\x10 @\x80\xc0\x88\x04\xaa\x1e\x9f\xdbM\x80\xc0\xf0\x1f\x9f\x0cK\xfb'), (3, 2, 3, 'saturate'): (b'x\x01\x15\xcbA\x15BQ\x08\x05@\xa2XD\xc5&/\nQ(\xa2\xf2\x9b\x10\xc5\x91\xc3\xc0\xe2B\xc4\xbf' b'\xean<"\x92\xa2\x19\x96xF\xdcH\x0eE3,\x91r\x92C\xd1\x0cK\xbc\xe4$\x87\xa2\x19\x96(\xfd' b'\xb6?n(\x9aa\x89\xaf\x7f\x92C\xd1\x0cK\x8c\x9c\xe4P4\xc3\x12\x97\x9c\xe4P4\xc3^?\xc7\x8a;c', b'x\x01\xed\xdd\xd7U\x15\x00\x00\x04Q$\x89HV@r\x90 Q\xb2\x92\xfb\xaf\xea\xd1\x02\x7f' b'\x1c\xce\xdc\xe9`o\x03;4$\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01' b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\xe0\xd3\t|' b'\x11\x01\x02\x04\x08\x14\x05\x86E\xa0,0\xa2\xb2\xc0\xa8>V`\xac\xddx\xbd\xaf\xed&\xea}k7Y\xef' b'{\xbb\xa9z\xd3\xedf\xea\xcd\xb6\x9b\xab7\xdfn\xa1\xde\x8fv?\xeb-\xb6[\xaa\xb7\xdc\xee' b'W\xbd\x95v\xab\xf5\xd6\xda\xad\xd7\xdbh\xb7YoK\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' b' @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 ' b'@\x80\x00\x01\x02\x04\x08\x10 \xf0.\x81\x81\x08\x10\xa8\nl\x8b\x00\x01\x02\x04\x08\x10 @\x80' b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10' b' @\x80\x00\x01\x02\x04\x08\x10\xf8|\x02;"@\x80\x00\x81\xa2\xc0\xae\x08\x94\x05~\xab,\xb0' b'\xa7\x8f\x15\xd8owP\xef\xb0\xdd\x9fzG\xed\x8e\xeb\x9d\xb4;\xadw\xd6\xee\xbc\xde\xdfv\x17' b'\xf5.\xdb]\xd5\xbbnwS\xef\xb6\xdd]\xbd\x7f\xed\xfe\xd7\xbbo\xf7P\xef\xb1\xddS\xbd\xe7v/\xf5^' b'E\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @' b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\xef\x12' b'\xa8\x1e\x9f\xdbM\x80\xc0\xe0\r\xd9\xf2;{'), (4, 3, 7, 'saturate'): (b'x\x01\x1d\xcd[\xb5\x90!\x10\x80Q"\x18\x81\x08\xbc{C-@\x04"\xd0\xe0\x10\x81\x08D\xe0\xdd\x1bj' b'\x01"\x10\xe1o\xa0\xfb\xc8Z{\xcd\xbc|C\x08\xff\xdf\xdb\x10:\x9b\xf0.\x84H\xa6\xd2\x19' b'L\x16\x9b\xc3\xe5!\xbc\x0f\xe1\r\x91D\xa6Pit\x06\x93\xc5\xe6py\x08\x1f\xf4D\x12\x99' b'B\xa5\xd1\x19L\x16\x9b\xc3\xe5!|\xd4\x13Id\n\x95Fg0Yl\x0e\x97\x87\x90\xf5D\x12\x99B\xa5' b'\xd1\x19L\x16\x9b\xc3\xe5!|\xd2\x13Id\n\x95Fg0Yl\x0e\x97\x87\xf0YO$\x91)T\x1a\x9d\xc1d\xb19' b'\\\x1e\xc2\x17=\x91D\xa6Pit\x06\x93\xc5\xe6p_\xf7\x17}\xe7\xab\xc1&|s\x8bL\xa53\x98,' b'6\x87\xcbC\xf8\xee\x7f"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xf2\x10~\xe8\x89$2\x85J\xa33\x98,6' b"\x87\xcbC\xf8\xa9'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0fa\xeb\x89$2\x85J\xa33\x98,6\x87\xcb" b'C\xf8\xa5\'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0f\xe1\xb7\x9eH"S\xa84:\x83\xc9bs\xb8<\x84' b'?z"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xbe\xee\x7f\xff\x01!\xba\xf7\x9b', b'x\x01\xed\xdd\xd5\x96\x90\x05\x00E\xe1\x01\xe9\x90n\x90\x96F\xbaE\xa4S@\xba\x15\xa4\xbbKX H*' b'\xdd)\xa9\x12Jw*H\x08Jww\x87\xd25\\\xccC\xec5\xf3\xef\xef\r\xce~\x81\x13\x12"\x0bX' b'\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX \xc2\x16\x88$\x0b' b'X\xc0\x02\x16\x08b\x81\xc8\xb2@\x90\x0b|\xa0 \x17\x88"\xb6@\xd4`\x8b\x16t\xd1\x83-Fx\x17\x93' b'\x15\x8b\x16\x9b\x15\x87\x16\x97\xf5!-\x1e+>-\x01+!-\x11+1-\t+)-\x19+9-\x05+%-\x15+5-\r' b'\xeb#ZZV:ZzV\x06ZFV&Zf\xd6\xc7\xb4,\xac\xac\xb4l\xac\xec\xb4\x1c\xac\x9c\xb4\\\xac\xdc\xb4O' b'XyhyY\xf9h\xf9Y\x05h\x05Y\x85h\x85YEhEY\xc5h\xc5Y%h\x9f\xb2J\xd2>c\x95\xa2}\xce*M+\xc3*K+' b'\xc7*O\xab\xc0\xaaH\xab\xc4\xaaL\xab\xc2\xaaJ\xab\xc6\xfa\x82V\x9dU\x83V\x93\xf5%\xad' b'\x16\xab6\xad\x0e\xab.\xad\x1e\xab>\xad\x01\xab!\xad\x11\xab1\xad\t\xab)\xad\x19\xeb+\xda' b'\xd7\xac\xe6\xb4\x16\xacoh-Y\xadh\xadYmhmY\xedh\xedY\x1dh\x1dY\x9dh\x9dY]h]Y\xddh\xddY=h' b'=Y\xbdh\xbdY}h}Y\xfdh\xfdY\xdf\xd2\x06\xb0\x06\xca\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02' b'\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05\xc2\n' b'\x84\xca\x02\x16\x08j\x81A\xb2\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0' b'\x02\x16\xb0\x80\x05"n\x81\xc1\xb2\x80\x05,`\x81 \x16\xf8N\x16\x08r\x81!\nr\x81\xa1' b'b\x0b|\x1fl\xc3\x82nx\xb0\x8d\x08\xefF\xb2F\xd1F\xb3~\xa0\xfd\xc8\x1aC\x1b\xcb\x1a' b'G\x1b\xcf\x9a@\x9b\xc8\x9aD\x9b\xcc\x9aB\x9b\xca\x9aF\x9b\xce\x9aA\x9b\xc9\x9aE\x9b\xcd\x9a' b'C\x9b\xcb\xfa\x896\x8f5\x9f\xb6\x80\xb5\x90\xb6\x88\xb5\x98\xf63\xeb\x17\xda\xaf\xac' b'%\xb4\xa5\xace\xb4\xe5\xac\xdfh\xbf\xb3V\xd0V\xb2V\xd1V\xb3\xd6\xd0\xd6\xb2\xd6\xd1\xd6\xb3' b'6\xd06\xb26\xd16\xb3\xb6\xd0\xb6\xb2\xb6\xd1\xb6\xb3v\xd0v\xb2\xfe\xa0\xfd\xc9\xdaE\xdb\xcd' b'\xfa\x8b\xb6\x87\xb5\x97\xb6\x8f\xb5\x9f\xf67\xeb\x00\xed \xeb\x1f\xda\xbf\xacC\xb4\xc3' b'\xac#\xb4\xa3\xacc\xb4\xe3\xac\x13\xb4\x93\xacS\xb4\xd3\xac3\xb4\xb3\xacs\xb4\xf3' b'\xac\x0b\xb4\x8b\xacK\xb4\xcb\xac+\xb4\xab\xack\xb4\xeb\xac\x1b\xb4\x9b\xac[\xb4\xdb' b'\xac;\xb4\xbb\xac{\xb4\xfb\xac\x07\xb4\x87\xacG\xb4\xc7\xac\xffh\xff\xb3\x9e\xd0\x9e' b'\xb2\x9e\xd1\x9e\xb3^\xd0^\xb2^\xd1^\xb3\xde\xd0\xde\xb2\xde\xc9\x02\x16\xb0\x80\x05' b',`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0' b'\x02\x16\xb0\x80\x05\xc2\n\x04\xf5\xf8\xdc\xdd\x16\xb0@\xe8{\t?\xc8\x90'), (5, 2, 15, 'saturate'): (b'x\x01M\xcb\xd9\x11\x10T\x0c@\xd1\x94B\x17 \x08\x06Dh#\x9d\x90R\xd2\x05\xca\x1a\x17' b'\xa0\x8dt\x02\xc7\x1f\xc7\xcc\x9c\xf7\xf1n\x12\xf1\xef\xf4C\xcf\xa3\x88\xa4\x19\x96#~\x8ax@' b"R4\xc3r\xc4c\x9d\xa4h\x86\xe5\x88':I\xd1\x0c\xcb\x11?\xeb$E3,G<\xd5I\x8afX\x8ex\xa6\x93" b'\x14\xcd\xb0\x1c\xf1\x8bNR4\xc3rD\xea$E3,G<\xd7I\x8afX\x8ex\xa1\x93\x14\xcd\xb0\x1c' b'\xf1\xabNR4\xc3r\xc4K\x9d\xa4h\x86\xe5\x88\xdft\x92\xa2\x19\x96#^\xe9$E3,G\xbc\xd6I' b'\x8a~\xa3\xfdO\xb4\xbf\xb7\xf6~\xb7C3,G\xfc\xe1\x9e\xa4h\x86\xe5\x88w:I\xd1\x0c' b'\xcb\x11\xefu\x92\xa2\x19\x96#>\xe8$E3,G|\xd4I\x8afX\x8e\xf8\xa4\x93\x14\xcd\xb0\x1c\xf1Y' b"')\x9aa9bu\x92\xa2\x19\x96#\xfe\xd4I\x8afX\x8e\xf8K')\x9aa9\xe2o\x9d\xa4h\x86\xe5\x88\x7ft" b'\x92\xa2\x19\x96#\xbe\xe8$E3,G|\xd5I\x8afX\x8e\xf8\xa6\x93\x14\xfd]\xfb\xcf\x0f' b'\xd2\x15\xf0\xcf', b'x\x01\xed\xddC\xa2\x1c\x00\x00D\xc1\xd8\xb6m\xdb\xb6m\xdb66\xb1m\xdb\xb6m\xdb\xb6m' b',\x92c\xfcEM\xdd\xa0\xdf\x05:X\xb0 \x16\xdc\x16B\x17\xd2\x16J\x17\xda\x16F\x17\xd6' b'\x16N\x17\xde\x16A\x17\xd1\x16I\x17\xd9\x16E\x17\xd5\x16M\x17\xdd\x16C\x17\xd3\x16K\x17\xdb' b'\x16G\x17\xd7\x16O\x17\xdf\x96@\x97\xd0\x96H\x97\xd8\x96D\x97\xd4\x96L\x97\xdc\x96B\x97\xd2' b'\x96J\x97\xda\x96F\x97\xd6\x96N\x97\xde\x96A\x97\xd1\x96I\x97\xd9\x96E\x97\xd5\x96M\x97\xdd' b'\x96C\x97\xd3\x96K\x97\xdb\x96G\x97\xd7\x96O\x97\xdfV@W\xd0VHW\xd8VDW\xd4VLW\xdcVBW\xd2' b'VJW\xdaVFW\xd6VNW\xdeVAW\xd1VIW\xd9VEW\xd5VMW\xddVCW\xd3VKW\xdbVGW\xd7VOW\xdf\xd6@\xd7\xd0' b'\xd6H\xd7\xd8\xd6D\xd7\xd4\xd6L\xd7\xdc\xd6B\xd7\xd2\xd6J\xd7\xda\xd6F\xd7\xd6\xd6N\xd7\xde' b'\xd6A\xd7\xd1\xd6I\xd7\xd9\xd6E\xd7\xd5\xd6M\xd7\xdd\xd6C\xd7\xd3\xd6K\xd7\xdb\xd6G\xd7\xd7' b'\xd6O\xd7\xdf6@70 H\x0b\xfc\x0b\x08\x14\x08\x14P\x0b\x0c\nj\x83mCtCm\xc3t\xc3m#t#m' b'\xa3t\xa3mctcm\xe3t\xe3m\x13t\x13m\x93t\x93mStSm\xd3t\xd3m3t3m\xb3t\xb3mstsm\xf3t\xf3m' b'\x0bt\x0bm\x8bt\x8bmKtKm\xcbt\xcbm+t+m\xabt\xabmktkm\xebt\xebm\x1bt\x1bm\x9bt\x9bm[t[m' b"\xdbt\xdbm;t;m\xbbt\xbbm{t{m\xfbt\xfbm\x07t\x07m\x87t\x87mGtGm\xc7t\xc7m't'm\xa7t\xa7mgtgm" b'\xe7t\xe7m\x17t\x17m\x97t\x97mWtWm\xd7t\xd7m7t7m\xb7t\xb7mwtwm\xf7t\xf7m\x0ft\x0fm' b'\x8ft\x8fmOtOm\xcft\xcfm/t/m\xaft\xafmotom\xeft\xefm\x1ft\x1fm\x9ft\x9fm_t_m\xdft\xdfm?t?m' b'\xbft\xbfm\x7ft\x7f\x03\x82\xb4\x80z|\x1e\xd8\x1d(\x10(\xf0\xef?\xe6\xfc\r\x9b'), (4, 3, 7, 'overflow'): (b'x\x01\x1d\xcd[\xb5\x90!\x10\x80Q"\x18\x81\x08\xbc{C-@\x04"\xd0\xe0\x10\x81\x08D\xe0\xdd\x1bj' b'\x01"\x10\xe1o\xa0\xfb\xc8Z{\xcd\xbc|C\x08\xff\xdf\xdb\x10:\x9b\xf0.\x84H\xa6\xd2\x19' b'L\x16\x9b\xc3\xe5!\xbc\x0f\xe1\r\x91D\xa6Pit\x06\x93\xc5\xe6py\x08\x1f\xf4D\x12\x99' b'B\xa5\xd1\x19L\x16\x9b\xc3\xe5!|\xd4\x13Id\n\x95Fg0Yl\x0e\x97\x87\x90\xf5D\x12\x99B\xa5' b'\xd1\x19L\x16\x9b\xc3\xe5!|\xd2\x13Id\n\x95Fg0Yl\x0e\x97\x87\xf0YO$\x91)T\x1a\x9d\xc1d\xb19' b'\\\x1e\xc2\x17=\x91D\xa6Pit\x06\x93\xc5\xe6p_\xf7\x17}\xe7\xab\xc1&|s\x8bL\xa53\x98,' b'6\x87\xcbC\xf8\xee\x7f"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xf2\x10~\xe8\x89$2\x85J\xa33\x98,6' b"\x87\xcbC\xf8\xa9'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0fa\xeb\x89$2\x85J\xa33\x98,6\x87\xcb" b'C\xf8\xa5\'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0f\xe1\xb7\x9eH"S\xa84:\x83\xc9bs\xb8<\x84' b'?z"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xbe\xee\x7f\xff\x01!\xba\xf7\x9b', b'x\x01\xed\xdc\x05\xb2\x90\x05\x00E\xe1\x87\x80\xa4t\x83tw\x97Jw\x0bHKw\x83\x80\x92Cww7' b'\x92\xd2\x1dRJww\x97\xa4\x92J\xba\x8c3\x8f\xff|\x1b\xb83g\x017$D\x16\xb0\x80\x05,`\x01' b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\xc0g[ \x8c,`\x01\x0bX ' b'\x88\x05\xbe\x90\x05\x82\\ \xac\x82\\ \x9c\xd8\x02\xe1\x83\xed\xcb\xa0\x8b\x10l\x11' b'C\xbbH\xac\xc8\xb4(\xac\xa8\xb4\xafX\xd1h\xd1Y1h1Y\xb1h\xb1YqhqY\xf1h\xf1Y\th\tY\x89h\x89Y' b'Ih_\xb3\x92\xd2\x92\xb1\x92\xd3R\xb0R\xd2R\xb1R\xd3\xd2\xb0\xd2\xd2\xd2\xb1\xd2\xd32\xb0' b'2\xd22\xb12\xd3\xb2\xb0\xb2\xd2\xb2\xb1\xb2\xd3r\xb0r\xd2r\xb1r\xd3\xf2\xb0\xf2\xd2\xf2\xb1' b'\xf2\xd3\n\xb0\n\xd2\xbea}K\xfb\x8eU\x88V\x98U\x84V\x94U\x8cV\x9cU\x82V\x92U\x8aV\x9a' b'U\x86V\x96U\x8eV\x9eU\x81V\x91U\x89V\x99U\x85\xf6=\xab*\xad\x1a\xab:\xed\x07V\rZMV-ZmV\x1dZ]' b'V=\xda\x8f\xac\xfa\xb4\x06\xac\x86\xb4F\xac\xc6\xb4&\xac\xa6\xb4f\xac\xe6\xb4\x16' b'\xac\x96\xb4V\xac\xd6\xb46\xac\xb6\xb4v\xac\xf6\xb4\x0e\xac\x8e\xb4N\xac\xce\xb4\x9fX]h]' b'Y\xddh?\xb3~\xa1ug\xf5\xa0\xf5d\xf5\xa2\xf5f\xf5\xa1}\x92\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80' b'\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX' b'\xc0\x02\x16`\x0b\xf4\x95\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0' b"\x80\x05,\xf0\xf9\x16\xe8'\x0bX\xc0\x02\x16\x08b\x81\xfe\xb2@\x90\x0b\x0cP\x90\x0b\x0c\x14[" b'`P\xb0\r\x0e\xba!\xc164\xb4\x1b\xc6\x1aN\x1b\xc1\x1aI\x1b\xc5\x1aM\x1b\xc3\x1aK\x1b' b'\xc7\x1aO\x9b\xc0\x9aH\x9b\xc4\x9aL\x9b\xc2\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b' b'\xc3\x9aK\x9b\xc7\x9aO[\xc0ZH[\xc4\xfa\x95\xb6\x98\xb5\x84\xb6\x94\xb5\x8c\xb6' b'\x9c\xb5\x82\xf6\x1bk%m\x15k5m\rk-m\x1dk=m\x03k#m\x13k3m\x0bk+m\x1bk;m\x07\xebw\xda' b"N\xd6.\xdan\xd6\x1e\xda^\xd6\x1f\xb4?Y\xfbh\xfbY\x07h\x07Y\x87h\x87YGhGY\xc7h\xc7Y'h'Y\xa7h" b'\xa7YghgY\xe7h\xe7Y\x17h\x17Y\x97h\x97YWhWY\xd7h\xd7Y7h7Y\xb7h\xb7YwhwY\xf7h\xf7Y\x0fh' b'\x7f\xb1\x1e\xd2\x1e\xb1\x1e\xd3\x9e\xb0\x9e\xd2\x9e\xb1\xfe\xa6\xfd\xc3zN{\xc1zI{\xc5zM' b'{\xc3\xfa\x97\xf6\x1f\xeb-\xed\x1d\xeb=\xed\x03\xeb#\x8d\xbd\xefw\xdd\x02\x16\xb0\x80\x05,`' b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16' b'\xb0\x80\x05,`\x01\x0b|\xfa\x1f\xb2\xf6b\xf1'), (5, 2, 15, 'overflow'): (b'x\x01M\xcb\xd9\x11\x10T\x0c@\xd1\x94B\x17 \x08\x06Dh#\x9d\x90R\xd2\x05\xca\x1a\x17' b'\xa0\x8dt\x02\xc7\x1f\xc7\xcc\x9c\xf7\xf1n\x12\xf1\xef\xf4C\xcf\xa3\x88\xa4\x19\x96#~\x8ax@' b"R4\xc3r\xc4c\x9d\xa4h\x86\xe5\x88':I\xd1\x0c\xcb\x11?\xeb$E3,G<\xd5I\x8afX\x8ex\xa6\x93" b'\x14\xcd\xb0\x1c\xf1\x8bNR4\xc3rD\xea$E3,G<\xd7I\x8afX\x8ex\xa1\x93\x14\xcd\xb0\x1c' b'\xf1\xabNR4\xc3r\xc4K\x9d\xa4h\x86\xe5\x88\xdft\x92\xa2\x19\x96#^\xe9$E3,G\xbc\xd6I' b'\x8a~\xa3\xfdO\xb4\xbf\xb7\xf6~\xb7C3,G\xfc\xe1\x9e\xa4h\x86\xe5\x88w:I\xd1\x0c' b'\xcb\x11\xefu\x92\xa2\x19\x96#>\xe8$E3,G|\xd4I\x8afX\x8e\xf8\xa4\x93\x14\xcd\xb0\x1c\xf1Y' b"')\x9aa9bu\x92\xa2\x19\x96#\xfe\xd4I\x8afX\x8e\xf8K')\x9aa9\xe2o\x9d\xa4h\x86\xe5\x88\x7ft" b'\x92\xa2\x19\x96#\xbe\xe8$E3,G|\xd5I\x8afX\x8e\xf8\xa6\x93\x14\xfd]\xfb\xcf\x0f' b'\xd2\x15\xf0\xcf', b'x\x01\xed\xddS\xb6\x1e\x06\x00\x85\xd1\x1b\xdb\xb6\x1b\xdb\xb6\x9b\x06\x8dm\xdbz\x88' b"m\xabI\x1b\xa3\xb1m\xdb\xb6mg\xad\x9ba\xdc\x87\xfd\xef\x19\x9co\x02'((\x84\x85\xb2" b'\x85\xd6\x85\xb1\x85\xd5\x85\xb3\x85\xd7E\xb0E\xd4E\xb2E\xd6E\xb1E\xd5E\xb3E\xd7\xc5\xb0' b'\xc5\xd4\xc5\xb2\xc5\xd6\xc5\xb1\xc5\xd5\xc5\xb3\xc5\xd7%\xb0%\xd4%\xb2%\xd6%\xb1%\xd5%\xb3' b'%\xd7\xa5\xb0\xa5\xd4\xa5\xb2\xa5\xd6\xa5\xb1\xa5\xd5\xa5\xb3\xfd\xa1Ko\xcb\xa0\xcbh' b'\xcb\xa4\xcbl\xcb\xa2\xcbj\xcb\xa6\xcbn\xcb\xa1\xcbi\xcb\xa5\xcbm\xcb\xa3\xcbk\xcb\xa7\xcbo' b'+\xa0+h+\xa4+l+\xa2+j+\xa6+n+\xa1+i+\xa5+m+\xa3+k+\xa7+o\xab\xa0\xabh\xab\xa4\xabl' b'\xfbSW\xc5\xf6\x97\xae\xaa\xad\x9a\xae\xba\xad\x86\xeeo[M]-[m]\x1d[]]=[}]\x03[C]#[c]\x13' b"[S]3[s]\x0b[K]+[k]\x1b[[];[{]\x07[G]'[g]\x17[W]7[w]\x0f[O]/[o]\x1f[_]?[\x7f\xdd\x00" b'\xdb\xc0\x90\x16\x1c\x10(\x10(\xa0\x16\x18\x14\xd2\x06\xdb\x86\xe8\x86\xda\x86\xe9\x86\xdb' b'F\xe8F\xdaF\xe9F\xdb\xc6\xe8\xc6\xda\xc6\xe9\xc6\xdb&\xe8&\xda&\xe9&\xdb\xa6\xe8\xa6\xda' b'\xa6\xe9\xa6\xdbf\xe8f\xdaf\xe9f\xdb\xe6\xe8\xe6\xda\xe6\xe9\xfe\xb1\xcd\xd7-\xb0' b'\xfd\xab\xfb\xcf\xb6P\xb7\xc8\xb6X\xb7\xc4\xb6T\xb7\xcc\xb6\\\xb7\xc2\xb6R\xb7\xca' b'\xf6\xbfn\xb5m\x8dn\xadm\x9dn\xbdm\x83n\xa3m\x93n\xb3m\x8bn\xabm\x9bn\xbbm\x87n\xa7' b'm\x97n\xb7m\x8fn\xafm\x9fn\xbf\xed\x80\xee\xa0\xed\x90\xee\xb0\xed\x88\xee\xa8' b'\xed\x98\xee\xb8\xed\x84\xee\xa4\xed\x94\xee\xb4\xed\x8c\xee\xac\xed\x9c\xee\xbc' b'\xed\x82\xee\xa2\xed\x92\xee\xb2\xed\x8a\xee\xaa\xed\x9a\xee\xba\xed\x86\xee\xa6' b'\xed\x96\xee\xb6\xed\x8e\xee\xae\xed\x9e\xee\xbe\xed\x81\xee\xa1\xed\x91\xee\xb1' b'\xed\x89\xee\xa9\xed\x99\xee\xb9\xed\x85\xee\xa5\xed\x95\xee\xb5\xed\x8d\xee\xad' b'\xed\x9d\xee\xbd\xed\x83\xee\xa3\xed\x93\xee\xb3\xed\x8b\xee\xab\xed\x9b\xee\xbb' b'\xed\x87\xee\xa7\xedWHS\x8f\xcf\x03\xbb\x03\x05\x02\x05\x82\x7f\x03\xb3\x87\x0e\x9d')} binary8_luts_compressed = \ {(4, 8): (b'x\x01\x15\xcb[\xd5P!\x10\x80Q"\x18\x81\x08<{E-@\x04"\xd0@"\x10\x81\x08<{E\xff\x02\'\x02\x11h\xa0\xdbY' b'k\xcf\xcb\xcc\x17\xc2\xff\xe9\xaf\xad7!d:\x93\xcd!\xbc\r\xe1\x15\x91D\xa6Pit\x06\x93\xc5\xe6\xe1p\t\xef\xf4' b'D\x12\x99B\xa5\xd1\x19L\x16\x9b\x87\xc3%\xbc\xd7\x13Id\n\x95Fg0Yl\x1e\x0e\x97\xf0AO$\x91)T\x1a\x9d\xc1d\xb1' b"y8\\B\xd6\x13Id\n\x95Fg0Yl\x1e\x0e\x97\xf0QO$\x91)T\x1a\x9d\xc1d\xb1y8\\\xc2'=\x91D\xa6Pit\x06\x93" b'\xc5\xe6\xe1p\t\x9f\xf5D\x12\x99B\xa5\x7f\xf1O\xff\xea\xef\x9b\x1b\x9d\xc9\xe6\x10\xbe\xeb\x89$2\x85J\xa3' b'3\x98,6\x0f\x87K\xf8\xa1\'\x92\xc8\x14*\x8d\xce`\xb2\xd8<\x1c.\xe1\xa7\x9eH"S\xa84:\x83\xc9b\xf3p\xb8\x84_z' b'"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xc3\xe1\x12\xb6\x9eH"S\xa84:\x83\xc9b\xf3p\xb8\x84\xdfz"\x89L\xa1\xd2\xe8' b'\x0c&\x8b\xcd\xc3\xe1\x12\xfe\xe8\x89$2\x85J\xa33\x98,6\x0f\x87Kx\xd1\x13Id\n\x95\xfe\xf7\x1f[)\xf3`', b'x\x01\xed\xdd\x05\xba\x96\x05\x00\x05\xe1\x9f\xee\x06\xe9FZA\xa4\xbb\xbb;\xa4SB\xba\xeb\xd2\xdd\x8dt\x97' b'\x92J(\xa14\xa2\x84\x92\x8a\xa4\x82\xd2\x1d\x12.c\x9e\xcb7\xef\x0e\xcel\xe0\x84B\xb2\x80\x05,`\x01' b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xe0\xbd/\x10A\x16\xb0\x80\x05,\x10\xc4\x02\x11e\x81 \x17\x88' b'\xa4 \x17\x88,\xb6@\x94`\x8b\x1at\xd1\x82-zx\x17\x83\x15\x93\x16\x8b\x15\x9b\x16\x87\x15\x97\x16\x8f\x15' b'\x9f\x96\x80\x95\x90\x96\x88\x95\x98\x96\x84\xf5\x01-)+\x19-9+\x05-%+\x15-5+\r--+\x1d-=+\x03-#+\x13\xedCV' b'fZ\x16VVZ6VvZ\x0eVN\xdaG\xac\x8fi\xb9X\xb9i\x9f\xb0\xf2\xd0>e\xe5\xa5\xe5c\xe5\xa7\x15`\x15\xa4\x15b' b'\x15\xa6\x15a\x15\xa5\x15c\x15\xa7\x95`\x95\xa4\x95b\x95\xa6\x95a\x95\xa5\x95c\x95\xa7U`U\xa4UbU\xa6Ua' b'U\xa5UcU\xa7\xd5`\xd5\xa4\xd5b\xd5\xa6\xd5a\xd5\xa5\xd5c\xd5\xa75`5\xa45b5\xa65a}Fk\xcajFk\xcejAk\xc9' b"jEk\xcdjCk\xcbjGk\xcf\xea@\xfb\x9c\xd5\x91\xd6\x89\xd5\x99\xd6\x85\xf5\x05\xad+\xab\x1b\xad;\xab\x07\xad'" b'\xab\x17\xad7\xab\x0f\xad/\xab\x1f\xad?k\x00m k\x10m0k\x08m\xa8,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,' b'`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x00\\' b' L\x16\xb0@P\x0b\xbc\xf7\xff\x86\x0e\xb4\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX ' b'\x14\x1a&\x0bX\xc0\x02\x16\x08b\x81\xe1\xb2@\x90\x0b\x8cP\x90\x0b\x8c\x14[`T\xb0\x8d\x0e\xba1\xc16' b'6\xbc\x1b\xc7\x1aO\x9b\xc0\x9aH\x9b\xc4\x9aL\x9b\xc2\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b\xc3' b'\x9aK\x9b\xc7\x9aO\xfb\x92\xb5\x80\xb6\x90\xb5\x88\xb6\x98\xb5\x84\xb6\x94\xb5\x8c\xb6\x9c\xb5\x82\xb6\x92' b'\xb5\x8a\xb6\x9a\xb5\x86\xb6\x96\xb5\x8e\xb6\x9e\xb5\x81\xf6\x15\xebk\xdaF\xd6&\xdaf\xd6\x16\xdaV\xd66\xda7' b'\xacoi\xdbY;h;Y\xbbh\xdf\xb1\xbe\xa7\xedf\xed\xa1\xede\xed\xa3\xfd\xc0\xfa\x91\xb6\x9fu\x80v\x90u\x88v' b'\x98u\x84v\x94u\x8c\xf6\x13\xeb8\xedg\xd6/\xb4\x13\xac\x93\xb4S\xac\xd3\xb4_Y\xbf\xd1\xce\xb0\xce\xd2' b'\xce\xb1\xce\xd3.\xb0.\xd2~g\xfdA\xbb\xc4\xfa\x93v\x99u\x85v\x95u\x8dv\x9du\x83\xf6\x17\xebo\xdaM\xd6-' b'\xda?\xac\x7fi\xb7YwhwY\xf7h\xf7Y\x0fh\x0fY\x8fh\x8fYOhOY\xcfh\xcfY/h/Y\xafh\xff\xb1^\xd3\xde\xb0\xde' b'\xd2\xde\xc9\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`' b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0b\xc0\x05\x82z|\xeen\x0bX \xec\x7f\xc6\xe4\x02%'), (5, 16): (b'x\x01\x1d\xc9\x89\r\x10\x04\x0c\x86\xd1\x8e\xc2\x16 \x97\x059\xd6\xe8&t\x94n\xa1\\RQ`\x8dn\x02/6yI' b"\xf3\x7f\x11\xff\xdf\xc3\x88f\x89G\x11\x0fH\x8afX\x8e\xf8M')\x9aa9\xe2\xb1NR4\xc3r\xc4\x13\x9d\xa4h" b'\x86\xe5\x88\xa7:I\xd1\x0c\xcb\x11\xcft\x92\xa2\x19\x96#\x9e\xeb$E3,G\xfc\xae\x93\x14\xcd\xb0\x1c\x91' b":I\xd1\x0c\xcb\x11/t\x92\xa2\x19\x96#^\xea$E3,G\xfc\xa1\x93\x14\xcd\xb0\x1c\xf1J')\x9aa9\xe2\xb5NR4\xc3" b'r\xc4\x1b\x9d\xa4h\x86\xe5\x88\xb7:I\xbf\xb3\x13\x7f\xfaY\xe2/\x9d\xa4h\x86\xe5\x88\xf7:I\xd1\x0c\xcb' b'\x11\x1ft\x92\xa2\x19\x96#>\xea$E3,G|\xd2I\x8afX\x8e\xf8\xac\x93\x14\xcd\xb0\x1c\xf1\xb7NR4\xc3r' b"\xc4\x17\x9d\xa4h\x86\xe5\x88\xd5I\x8afX\x8e\xf8G')\x9aa9\xe2\xabNR4\xc3r\xc4\xbf:I\xd1\x0c\xcb\x11" b"\xff\xe9$E3,G|\xd3I\x8afX\x8e\xf8\xae\x93\x14\xcd\xb0\x1c\xf1C'\xe9\x9f\xbf\x00Gi\xed\x02", b'x\x01\xed\xddU\xd6\x96\x05\x00\x85\xd1\x9f\x16\xa4\x14\x04\xe9P\xa4K\x1a\xe9\x14\x10\t\xe9\x0eQ' b'\xba\xc1\xa0\xbb\xbb\xa5\x1b)%\xa5;%\xa5\xbb;%\x94\x8eAp\xf1.\xd6\xfe\xf6\x0c\xce3\x81\x13\x16\xf6' b'\x8e\xc2\x05+|\xd0"\x04+b\xd0"\x05+r\xd0\xa2\x04\xeb\x83\xf7]T[4\xdd\x87\xb6\xe8\xba\x18\xb6\x98\xbaX\xb6' b'\xd8\xba\x8fl\x1f\xeb\xe2\xd8\xe2\xea>\xb1\xc5\xd3\xc5\xb7}\xaaK`K\xa8KdK\xacKbK\xaaKfK\xaeKaK\xa9\xfb\xcc' b'\xf6\xb9.\x95\xed\x0b]j[\x1a]Z[:]z[\x06]F[&]f[\x16]V\xdb\x97\xbal\xb6\xec\xba\x1c\xb6\x9c\xba\\' b'\xb6\xdc\xba<\xb6\xbc\xba\xafl\xf9t\xf9m\x05t\x05m\x85t\x85mEtEm\xc5t\xc5m%t%m_\xebJ\xd9J\xeb\xca' b'\xd8\xbe\xd1\x95\xb5}\xab+g+\xaf\xab`\xab\xa8\xfb\xceVIW\xd9VEW\xd5VMW\xddVCW\xd3VKW\xdbVGW\xd7VOW' b'\xdf\xd6@\xd7\xd0\xf6\xbd\xae\x91\xed\x07\xdd\x8f\xb6\xc6\xba&\xb6\xa6\xbaf\xb6\xe6\xba\x16\xb6\x96\xba' b'V\xb6\xd6\xba6\xb6\xb6\xbav\xb6\xf6\xba\x0e\xb6\x8e\xba\x9fl?\xeb~\xb1\xfd\xaa\xebd\xeb\xac\xebb\xeb\xaa' b"\xeb\x16\x12h\x81\xee!\xa1\x02\xa1\x02j\x81\xb0w\xd5#X=\x83\xd6+X\xbd\x83\xd6'X}\x83\xd6/X\xfd\xdfw" b'\x03l\x03u\x83l\x83uClCu\xc3l\xc3u#l#u\xa3l\xa3uclcu\xe3l\xe3u\x13l\xbf\xe9&\xda&\xe9&\xdb\xa6\xe8' b'\xa6\xda\xa6\xe9\xa6\xdbf\xe8f\xdaf\xe9f\xdb\xe6\xe8\xe6\xda\xe6\xe9~\xb7\xcd\xd7-\xb0-\xd4-\xb2-\xd6' b'\xfda\xfbS\xb7\xc4\xb6T\xb7\xcc\xb6\\\xb7\xc2\xb6R\xf7\x97m\x95n\xb5m\x8dn\xadm\x9dn\xbdm\x83n\xa3m\x93' b'n\xb3m\x8bn\xabm\x9bn\xbbm\x87n\xa7m\x97n\xb7\xedo\xdd\x1e\xdb^\xdd>\xdb~\xdd\x01\xdbA\xdd?\xb6C' b'\xba\xc3\xb6#\xba\xa3\xb6c\xba\xe3\xb6\x13\xba\x93\xb6S\xba\xd3\xb63\xba\xb3\xb6s\xba\xf3\xb6\x0b' b'\xba\x8b\xb6K\xba\xcb\xb6+\xba\xab\xb6k\xba\xeb\xb6\x1b\xba\x9b\xb6[\xba\xdb\xb6;\xba\xbb\xb6{' b'\xba\xfb\xb6\x7fu\x0fl\x0fu\x8fl\x8fu\xff\xd9\xfe\xd7=\xb1=\xd5=\xb3=\xd7\xbd\xb0\xbd\xd4\xbd\xb2\xbd' b'\xd6\xbd\t\t\xb4\x80z|\x1e\xda\x1d*\x10*\xd0\xfd-\x8c\x93\xc6\x0e')} bitstring-bitstring-4.2.3/bitstring/methods.py000066400000000000000000000103771462444752600216000ustar00rootroot00000000000000from __future__ import annotations import bitstring from bitstring.bitstream import BitStream from bitstring.utils import tokenparser from bitstring.exceptions import CreationError from typing import Union, List from bitstring.bitstore import BitStore from bitstring.bitstore_helpers import bitstore_from_token def pack(fmt: Union[str, List[str]], *values, **kwargs) -> BitStream: """Pack the values according to the format string and return a new BitStream. fmt -- A single string or a list of strings with comma separated tokens describing how to create the BitStream. values -- Zero or more values to pack according to the format. kwargs -- A dictionary or keyword-value pairs - the keywords used in the format string will be replaced with their given value. Token examples: 'int:12' : 12 bits as a signed integer 'uint:8' : 8 bits as an unsigned integer 'float:64' : 8 bytes as a big-endian float 'intbe:16' : 2 bytes as a big-endian signed integer 'uintbe:16' : 2 bytes as a big-endian unsigned integer 'intle:32' : 4 bytes as a little-endian signed integer 'uintle:32' : 4 bytes as a little-endian unsigned integer 'floatle:64': 8 bytes as a little-endian float 'intne:24' : 3 bytes as a native-endian signed integer 'uintne:24' : 3 bytes as a native-endian unsigned integer 'floatne:32': 4 bytes as a native-endian float 'hex:80' : 80 bits as a hex string 'oct:9' : 9 bits as an octal string 'bin:1' : single bit binary string 'ue' / 'uie': next bits as unsigned exp-Golomb code 'se' / 'sie': next bits as signed exp-Golomb code 'bits:5' : 5 bits as a bitstring object 'bytes:10' : 10 bytes as a bytes object 'bool' : 1 bit as a bool 'pad:3' : 3 zero bits as padding >>> s = pack('uint:12, bits', 100, '0xffe') >>> t = pack(['bits', 'bin:3'], s, '111') >>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44) """ tokens = [] if isinstance(fmt, str): fmt = [fmt] try: for f_item in fmt: _, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys()))) tokens.extend(tkns) except ValueError as e: raise CreationError(*e.args) value_iter = iter(values) bsl: List[BitStore] = [] try: for name, length, value in tokens: # If the value is in the kwd dictionary then it takes precedence. value = kwargs.get(value, value) # If the length is in the kwd dictionary then use that too. length = kwargs.get(length, length) # Also if we just have a dictionary name then we want to use it if name in kwargs and length is None and value is None: bsl.append(BitStream(kwargs[name])._bitstore) continue if length is not None: length = int(length) if value is None and name != 'pad': # Take the next value from the ones provided value = next(value_iter) if name == 'bits': value = bitstring.bits.Bits(value) if length is not None and length != len(value): raise CreationError(f"Token with length {length} packed with value of length {len(value)}.") bsl.append(value._bitstore) continue bsl.append(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.") try: next(value_iter) except StopIteration: # Good, we've used up all the *values. s = BitStream() if bitstring.options.lsb0: bsl.reverse() for b in bsl: s._bitstore += b return s raise CreationError(f"Too many parameters present to pack according to the format. Only {len(tokens)} values were expected.") bitstring-bitstring-4.2.3/bitstring/mxfp.py000066400000000000000000000215421462444752600211030ustar00rootroot00000000000000import array import math import struct import bitarray from bitstring.luts import mxfp_luts_compressed import zlib from typing import Optional def round_to_nearest_ties_to_even(lut_int_to_float, lower: int, f: float) -> Optional[int]: upper = lower + 1 # Special case for LUTs without a negative zero. lower_float = 0.0 if lower == 128 else lut_int_to_float[lower] upper_float = lut_int_to_float[upper] if upper_float < lower_float: lower, upper = upper, lower lower_float, upper_float = upper_float, lower_float if f == lower_float: return lower if f == upper_float: return upper if lower_float < f < upper_float: d1 = f - lower_float d2 = upper_float - f if d1 < d2: return lower if d2 < d1: return upper return lower if lower % 2 == 0 else upper return None class MXFPFormat: """Defining an MXFP micro-scaling floating point format""" def __init__(self, exp_bits: int, mantissa_bits: int, bias: int, mxfp_overflow: str): self.exp_bits = exp_bits self.mantissa_bits = mantissa_bits self.bias = bias self.mxfp_overflow = mxfp_overflow self.pos_clamp_value = (1 << (self.exp_bits + self.mantissa_bits)) - 1 self.neg_clamp_value = (1 << (1 + self.exp_bits + self.mantissa_bits)) - 1 # Special cases for e4m3 and e5m2 if self.exp_bits == 4 and self.mantissa_bits == 3: if self.mxfp_overflow == 'saturate': self.pos_clamp_value = 0b01111110 # 448 self.neg_clamp_value = 0b11111110 # -448 else: self.pos_clamp_value = self.neg_clamp_value = 0b11111111 # NaN if self.exp_bits == 5 and self.mantissa_bits == 2: if self.mxfp_overflow == 'saturate': self.pos_clamp_value = 0b01111011 # 57344 self.neg_clamp_value = 0b11111011 # -57344 else: self.pos_clamp_value = 0b01111100 # +inf self.neg_clamp_value = 0b11111100 # -inf # If we calculate these LUTs now it creates a bootstrap problem in generate_luts.py. self.lut_float16_to_mxfp = None self.lut_int_to_float = None def __str__(self): return f"MXFPFormat(exp_bits={self.exp_bits}, mantissa_bits={self.mantissa_bits}, bias={self.bias}, mxfp_overflow='{self.mxfp_overflow}')" def decompress_luts(self): int_to_float_compressed, float16_to_mxfp_compressed = mxfp_luts_compressed[(self.exp_bits, self.mantissa_bits, self.bias, self.mxfp_overflow)] self.lut_float16_to_mxfp = zlib.decompress(float16_to_mxfp_compressed) dec = zlib.decompress(int_to_float_compressed) self.lut_int_to_float = struct.unpack(f'<{len(dec) // 4}f', dec) def create_luts(self): self.lut_int_to_float = self.createLUT_for_int_to_float() self.lut_float16_to_mxfp = self.createLUT_for_float16_to_mxfp() def float_to_int(self, f: float) -> int: """Given a Python float convert to the best mxfp float (expressed as an int) that represents it.""" # First convert the float to a float16, then a 16 bit uint try: b = struct.pack('>e', f) except (OverflowError, struct.error): # Return the largest representable positive or negative value return self.pos_clamp_value if f > 0 else self.neg_clamp_value f16_int = int.from_bytes(b, byteorder='big') # Then use this as an index to our large LUT return self.lut_float16_to_mxfp[f16_int] def slow_float_to_int(self, f: float) -> int: # Slow, but easier to follow than the faster version. # The output int has the binary sequence needed for the float. length = 1 + self.exp_bits + self.mantissa_bits values = 1 << length # First get the NaN case out of the way if math.isnan(f): if length == 8: return 0xff # Works for both e5m2 and e4m3 # For smaller lengths, NaN isn't supported so we instead return an invalid value to detect later return 0xff # This is so we can distinguish between 0.0 and -0.0 is_positive = math.copysign(1.0, f) == 1.0 if is_positive: # Positive, so top bit is not set for i in range(values // 2 - 1): upper = self.lut_int_to_float[i + 1] if upper == float('inf'): break x = round_to_nearest_ties_to_even(self.lut_int_to_float, i, f) if x is not None: return x return self.pos_clamp_value else: # Negative, so top bit is set for i in range(values // 2, values - 1): lower = self.lut_int_to_float[i + 1] if lower == float('-inf'): break x = round_to_nearest_ties_to_even(self.lut_int_to_float, i, f) if x is not None: return x # Clip to negative max return self.neg_clamp_value def createLUT_for_int_to_float(self) -> array.array: """Create a LUT to convert an int in representing a MXFP float into a Python float""" 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) sign = b[0] exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits]) significand = b[1 + self.exp_bits:] if exponent == 0: significand = bitarray.bitarray('0') + significand exponent = -self.bias + 1 else: significand = bitarray.bitarray('1') + significand exponent -= self.bias f = float(bitarray.util.ba2int(significand)) / (2.0 ** self.mantissa_bits) f *= 2 ** exponent if length == 8: # Some special cases if self.exp_bits == 5: if i in [0b01111100, 0b11111100]: f = float('inf') if i in [0b01111101, 0b11111101, 0b01111110, 0b11111110, 0b01111111, 0b11111111]: f = float('nan') if self.exp_bits == 4: if i in [0b01111111, 0b11111111]: f = float('nan') i2f.append(f if not sign else -f) return array.array('f', i2f) def createLUT_for_float16_to_mxfp(self) -> bytes: """Create a LUT to convert a float16 into a MXFP format""" # Used to create the LUT that was compressed and stored for the fp8 code length = 1 + self.exp_bits + self.mantissa_bits if length == 8: import gfloat from gfloat.formats import format_info_ocp_e5m2, format_info_ocp_e4m3 fi = format_info_ocp_e5m2 if self.exp_bits == 5 else format_info_ocp_e4m3 fp16_to_fp8 = bytearray(1 << 16) for i in range(1 << 16): b = struct.pack('>H', i) f, = struct.unpack('>e', b) fp = gfloat.round_float(fi, f, sat=self.mxfp_overflow == 'saturate') if math.isnan(fp): fp8_i = 0b11111111 else: # Special case for negative zero if fp == 0.0 and math.copysign(1.0, fp) == -1.0: fp8_i = 0b10000000 else: fp8_i = self.lut_int_to_float.index(fp) fp16_to_fp8[i] = fp8_i return bytes(fp16_to_fp8) else: assert length in [4, 6] fp16_to_fp8 = bytearray(1 << 16) for i in range(1 << 16): b = struct.pack('>H', i) f, = struct.unpack('>e', b) fp8_i = self.slow_float_to_int(f) fp16_to_fp8[i] = fp8_i return bytes(fp16_to_fp8) e2m1mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=1, bias=1, mxfp_overflow='saturate') e2m3mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=3, bias=1, mxfp_overflow='saturate') e3m2mxfp_fmt = MXFPFormat(exp_bits=3, mantissa_bits=2, bias=3, mxfp_overflow='saturate') e4m3mxfp_saturate_fmt = MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='saturate') e5m2mxfp_saturate_fmt = MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='saturate') e4m3mxfp_overflow_fmt = MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='overflow') e5m2mxfp_overflow_fmt = MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='overflow') def decompress_luts(): e2m1mxfp_fmt.decompress_luts() e2m3mxfp_fmt.decompress_luts() e3m2mxfp_fmt.decompress_luts() e4m3mxfp_saturate_fmt.decompress_luts() e5m2mxfp_saturate_fmt.decompress_luts() e4m3mxfp_overflow_fmt.decompress_luts() e5m2mxfp_overflow_fmt.decompress_luts() bitstring-bitstring-4.2.3/bitstring/py.typed000066400000000000000000000000001462444752600212400ustar00rootroot00000000000000bitstring-bitstring-4.2.3/bitstring/utils.py000066400000000000000000000220051462444752600212640ustar00rootroot00000000000000from __future__ import annotations import functools import re from typing import Tuple, List, Optional, Pattern, Dict, Union, Match # A token name followed by optional : then an integer number NAME_INT_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?(\d*)$') # A token name followed by optional : then an arbitrary keyword NAME_KWARG_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?([a-zA-Z0-9_]+)$') CACHE_SIZE = 256 DEFAULT_BITS: Pattern[str] = re.compile(r'^(?P[^=]+)?(=(?P.*))?$', re.IGNORECASE) MULTIPLICATIVE_RE: Pattern[str] = re.compile(r'^(?P.*)\*(?P.+)') # Hex, oct or binary literals LITERAL_RE: Pattern[str] = re.compile(r'^(?P0([xob]))(?P.+)', re.IGNORECASE) # An endianness indicator followed by one or more struct.pack codes STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P[<>@=])(?P(?:\d*[bBhHlLqQefd])+)$') # The same as above, but it doesn't insist on an endianness as it's byteswapping anyway. BYTESWAP_STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P[<>@=])?(?P(?:\d*[bBhHlLqQefd])+)$') # An endianness indicator followed by exactly one struct.pack codes SINGLE_STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P[<>@=])(?P[bBhHlLqQefd])$') # A number followed by a single character struct.pack code STRUCT_SPLIT_RE: Pattern[str] = re.compile(r'\d*[bBhHlLqQefd]') # These replicate the struct.pack codes # Big-endian REPLACEMENTS_BE: Dict[str, str] = {'b': 'int8', 'B': 'uint8', 'h': 'intbe16', 'H': 'uintbe16', 'l': 'intbe32', 'L': 'uintbe32', 'q': 'intbe64', 'Q': 'uintbe64', 'e': 'floatbe16', 'f': 'floatbe32', 'd': 'floatbe64'} # Little-endian REPLACEMENTS_LE: Dict[str, str] = {'b': 'int8', 'B': 'uint8', 'h': 'intle16', 'H': 'uintle16', 'l': 'intle32', 'L': 'uintle32', 'q': 'intle64', 'Q': 'uintle64', 'e': 'floatle16', 'f': 'floatle32', 'd': 'floatle64'} # Native-endian REPLACEMENTS_NE: Dict[str, str] = {'b': 'int8', 'B': 'uint8', 'h': 'intne16', 'H': 'uintne16', 'l': 'intne32', 'L': 'uintne32', 'q': 'intne64', 'Q': 'uintne64', 'e': 'floatne16', 'f': 'floatne32', 'd': 'floatne64'} # Size in bytes of all the pack codes. PACK_CODE_SIZE: Dict[str, int] = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4, 'L': 4, 'q': 8, 'Q': 8, 'e': 2, 'f': 4, 'd': 8} def structparser(m: Match[str]) -> List[str]: """Parse struct-like format string token into sub-token list.""" endian = m.group('endian') # Split the format string into a list of 'q', '4h' etc. formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt')) # Now deal with multiplicative factors, 4h -> hhhh etc. fmt = ''.join([f[-1] * int(f[:-1]) if len(f) != 1 else f for f in formatlist]) if endian in '@=': # Native endianness tokens = [REPLACEMENTS_NE[c] for c in fmt] elif endian == '<': tokens = [REPLACEMENTS_LE[c] for c in fmt] else: assert endian == '>' tokens = [REPLACEMENTS_BE[c] for c in fmt] return tokens @functools.lru_cache(CACHE_SIZE) def parse_name_length_token(fmt: str, **kwargs) -> Tuple[str, Optional[int]]: # Any single token with just a name and length if m2 := NAME_INT_RE.match(fmt): name = m2.group(1) length_str = m2.group(2) length = None if length_str == '' else int(length_str) else: # Maybe the length is in the kwargs? if m := NAME_KWARG_RE.match(fmt): name = m.group(1) try: length_str = kwargs[m.group(2)] except KeyError: raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.") length = int(length_str) else: raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.") return name, length @functools.lru_cache(CACHE_SIZE) def parse_single_struct_token(fmt: str) -> Optional[Tuple[str, Optional[int]]]: if m := SINGLE_STRUCT_PACK_RE.match(fmt): endian = m.group('endian') f = m.group('fmt') if endian == '>': fmt = REPLACEMENTS_BE[f] elif endian == '<': fmt = REPLACEMENTS_LE[f] else: assert endian in '=@' fmt = REPLACEMENTS_NE[f] return parse_name_length_token(fmt) else: return None @functools.lru_cache(CACHE_SIZE) def parse_single_token(token: str) -> Tuple[str, str, Optional[str]]: if (equals_pos := token.find('=')) == -1: value = None else: value = token[equals_pos + 1:] token = token[:equals_pos] if m2 := NAME_INT_RE.match(token): name = m2.group(1) length_str = m2.group(2) length = None if length_str == '' else length_str elif m3 := NAME_KWARG_RE.match(token): # name then a keyword for a length name = m3.group(1) length = m3.group(2) else: # If you don't specify a 'name' then the default is 'bits' name = 'bits' length = token return name, length, value @functools.lru_cache(CACHE_SIZE) def preprocess_tokens(fmt: str) -> List[str]: # Remove whitespace and expand brackets fmt = expand_brackets(''.join(fmt.split())) # Split tokens by ',' and remove whitespace # The meta_tokens can either be ordinary single tokens or multiple struct-format token strings. meta_tokens = [f.strip() for f in fmt.split(',')] final_tokens = [] for meta_token in meta_tokens: if meta_token == '': continue # Extract factor and actual token if a multiplicative factor exists factor = 1 if m := MULTIPLICATIVE_RE.match(meta_token): factor = int(m.group('factor')) meta_token = m.group('token') # Parse struct-like format into sub-tokens or treat as single token tokens = structparser(m) if (m := STRUCT_PACK_RE.match(meta_token)) else [meta_token] # Extend final tokens list with parsed tokens, repeated by the factor final_tokens.extend(tokens * factor) return final_tokens @functools.lru_cache(CACHE_SIZE) def tokenparser(fmt: str, keys: Tuple[str, ...] = ()) -> \ Tuple[bool, List[Tuple[str, Union[int, str, None], Optional[str]]]]: """Divide the format string into tokens and parse them. Return stretchy token and list of [initialiser, length, value] initialiser is one of: hex, oct, bin, uint, int, se, ue, 0x, 0o, 0b etc. length is None if not known, as is value. If the token is in the keyword dictionary (keys) then it counts as a special case and isn't messed with. tokens must be of the form: [factor*][initialiser][:][length][=value] """ tokens = preprocess_tokens(fmt) stretchy_token = False ret_vals: List[Tuple[str, Union[str, int, None], Optional[str]]] = [] for token in tokens: if keys and token in keys: # Don't bother parsing it, it's a keyword argument ret_vals.append((token, None, None)) continue if token == '': continue # Match literal tokens of the form 0x... 0o... and 0b... if m := LITERAL_RE.match(token): ret_vals.append((m.group('name'), None, m.group('value'))) continue name, length, value = parse_single_token(token) if length is None: stretchy_token = True if length is not None: # Try converting length to int, otherwise check it's a key. try: length = int(length) except ValueError: if not keys or length not in keys: raise ValueError(f"Don't understand length '{length}' of token.") ret_vals.append((name, length, value)) return stretchy_token, ret_vals BRACKET_RE = re.compile(r'(?P\d+)\*\(') def expand_brackets(s: str) -> str: """Expand all brackets.""" while True: start = s.find('(') if start == -1: break count = 1 # Number of hanging open brackets p = start + 1 while p < len(s): count += (s[p] == '(') - (s[p] == ')') if count == 0: break p += 1 if count != 0: raise ValueError(f"Unbalanced parenthesis in '{s}'.") if start == 0 or s[start - 1] != '*': s = s[0:start] + s[start + 1:p] + s[p + 1:] else: # Looks for first number*( m = BRACKET_RE.search(s) if m: factor = int(m.group('factor')) matchstart = m.start('factor') s = s[0:matchstart] + (factor - 1) * (s[start + 1:p] + ',') + s[start + 1:p] + s[p + 1:] else: raise ValueError(f"Failed to parse '{s}'.") return s bitstring-bitstring-4.2.3/doc/000077500000000000000000000000001462444752600163135ustar00rootroot00000000000000bitstring-bitstring-4.2.3/doc/_static/000077500000000000000000000000001462444752600177415ustar00rootroot00000000000000bitstring-bitstring-4.2.3/doc/_static/custom.css000066400000000000000000000034211462444752600217650ustar00rootroot00000000000000@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;600&display=swap'); :root { --mainNavColor: #348ad1; --sidebarColor: #f8f8ff; --inlineCodeBackgroundColor: rgb(0 0 0 / 0%); } div#top_nav nav { background-image: linear-gradient(#348ad1, #046ab1); padding: 0.2rem 1rem; } div#top_nav nav h1 img{ height: 3rem; } .highlight { background: #f8f8f8; } div.document { margin-bottom: 1rem; } div.document div.documentwrapper { max-width: 55rem; } div.document div.highlight { border-radius: 0.0rem; border-left: 3px dotted rgba(128, 128, 128, 40%); background-color: var(--mainBackgroundColor); } h1, h2, h3, h4 { font-family: 'Lato', sans-serif; } h1 { color: #2980b9; } h3, h4 { text-decoration: underline; } div.document div.admonition.attention, div.document div.admonition.caution, div.document div.admonition.warning { border: 2px solid #d77732; } div.document div.admonition.attention p.admonition-title, div.document div.admonition.caution p.admonition-title, div.document div.admonition.warning p.admonition-title { color: #ffffff; background-color: #d77732; } div.document div.admonition.attention, div.document div.admonition.caution, div.document div.admonition.note { border: 2px solid #587f9f; } div.document div.admonition.attention p.admonition-title, div.document div.admonition.caution p.admonition-title, div.document div.admonition.note p.admonition-title { color: #ffffff; background-color: #587f9f; } div.document li p { margin: 0 0 0.3rem 0; } div.document table.docutils td { border: none; padding: 0.1rem 1rem; } div.document table.docutils th { border: none; padding: 0.1rem 1rem; } div.document hr { height: 0.25rem; background-color: #d03434; }bitstring-bitstring-4.2.3/doc/appendices.rst000066400000000000000000000004441462444752600211620ustar00rootroot00000000000000.. currentmodule:: bitstring ########### Appendices ########### This section contains supplementary information about the library, including details of some of the more specialist data types that are supported. .. toctree:: :maxdepth: 2 exotic_floats exp-golomb optimisation bitstring-bitstring-4.2.3/doc/array.rst000066400000000000000000000500421462444752600201640ustar00rootroot00000000000000.. currentmodule:: bitstring Array ===== .. class:: Array(dtype: str | Dtype, initializer: Iterable | int | Array | array.array | Bits | bytes | bytearray | memoryview | BinaryIO | None = None, trailing_bits: BitsType | None = None) Create a new ``Array`` whose elements are set by the `dtype` (data-type) string or :class:`Dtype`. This can be any format which has a fixed length. See :ref:`format_tokens` and :ref:`compact_format` for details on allowed dtype strings, noting that only formats with well defined bit lengths are allowed. The `inititalizer` will typically be an iterable such as a list, but can also be many other things including an open binary file, a bytes or bytearray object, another ``bitstring.Array`` or an ``array.array``. It can also be an integer, in which case the ``Array`` will be zero-initialised with that many items. :: >>> bitstring.Array('i4', 8) Array('int4', [0, 0, 0, 0, 0, 0, 0, 0]) The `trailing_bits` typically isn't used in construction, and specifies bits left over after interpreting the stored binary data according to the data type `dtype`. The ``Array`` class is a way to efficiently store data that has a single type with a set length. The ``bitstring.Array`` type is meant as a more flexible version of the standard ``array.array``, and can be used the same way. :: import array import bitstring x = array.array('f', [1.0, 2.0, 3.14]) y = bitstring.Array('=f', [1.0, 2.0, 3.14]) assert x.tobytes() == y.tobytes() This example packs three 32-bit floats into objects using both libraries. The only difference is the explicit native endianness for the format string of the bitstring version. The bitstring Array's advantage lies in the way that any fixed-length bitstring format can be used instead of just the dozen or so typecodes supported by the ``array`` module. For example ``'uint4'``, ``'bfloat'`` or ``'hex12'`` can be used, and the endianness of multi-byte dtypes can be properly specified. Each element in the ``Array`` must then be something that makes sense for the ``dtype``. Some examples will help illustrate:: from bitstring import Array # Each unsigned int is stored in 4 bits a = Array('uint4', [0, 5, 5, 3, 2]) # Convert and store floats in 8 bits each b = Array('p3binary', [-56.0, 0.123, 99.6]) # Each element is a 7 bit signed integer c = Array('int7', [-3, 0, 120]) You can then access and modify the ``Array`` with the usual notation:: a[1:4] # Array('uint4', [5, 5, 3]) b[0] # -56.0 c[-1] # 120 a[0] = 2 b.extend([0.0, -1.5]) Conversion between ``Array`` types can be done using the :meth:`astype` method. If elements of the old array don't fit or don't make sense in the new array then the relevant exceptions will be raised. :: >>> x = Array('float64', [89.3, 1e34, -0.00000001, 34]) >>> y = x.astype('float16') >>> y Array('float16', [89.3125, inf, -0.0, 34.0]) >>> y = y.astype('p4binary') >>> y Array('p4binary', [88.0, 240.0, 0.0, 32.0]) >>> y.astype('uint8') Array('uint8', [88, 240, 0, 32]) >>> y.astype('uint7') bitstring.CreationError: 240 is too large an unsigned integer for a bitstring of length 7. The allowed range is [0, 127]. You can also reinterpret the data by changing the :attr:`dtype` property directly. This will not copy any data but will cause the current data to be shown differently. :: >>> x = Array('int16', [-5, 100, -4]) >>> x Array('int16', [-5, 100, -4]) >>> x.dtype = 'int8' >>> x Array('int8', [-1, -5, 0, 100, -1, -4]) The data for the array is stored internally as a :class:`BitArray` object. It can be directly accessed using the :attr:`data` property. You can freely manipulate the internal data using all of the methods available for the :class:`BitArray` class. The :class:`Array` object also has a :attr:`trailing_bits` read-only data member, which consists of the end bits of the :attr:`data` that are left over when the :class:`Array` is interpreted using the :attr:`dtype`. Typically :attr:`trailing_bits` will be an empty :class:`BitArray` but if you change the length of the :attr:`data` or change the :attr:`dtype` specification there may be some bits left over. Some methods, such as :meth:`~Array.append` and :meth:`~Array.extend` will raise an exception if used when :attr:`trailing_bits` is not empty, as it not clear how these should behave in this case. You can however still use :meth:`~Array.insert` which will always leave the :attr:`trailing_bits` unchanged. The :attr:`dtype` string can be a type code such as ``'>H'`` or ``'=d'`` but it can also be a string defining any format which has a fixed-length in bits, for example ``'int12'``, ``'bfloat'``, ``'bytes5'`` or ``'bool'``. Note that the typecodes must include an endianness character to give the byte ordering. This is more like the ``struct`` module typecodes, and is different to the ``array.array`` typecodes which are always native-endian. The correspondence between the big-endian type codes and bitstring dtype strings is given in the table below. ========= =================== Type code bitstring dtype ========= =================== ``'>b'`` ``'int8'`` ``'>B'`` ``'uint8'`` ``'>h'`` ``'int16'`` ``'>H'`` ``'uint16'`` ``'>l'`` ``'int32'`` ``'>L'`` ``'uint32'`` ``'>q'`` ``'int64'`` ``'>Q'`` ``'uint64'`` ``'>e'`` ``'float16'`` ``'>f'`` ``'float32'`` ``'>d'`` ``'float64'`` ========= =================== The endianness character can be ``'>'`` for big-endian, ``'<'`` for little-endian or ``'='`` for native-endian (``'@'`` can also be used for native-endian). In the bitstring dtypes the default is big-endian, but you can specify little or native endian using ``'le'`` or ``'ne'`` modifiers, for example: ============ ============================= Type code bitstring dtype ============ ============================= ``'>H'`` ``'uint16'`` / ``'uintbe16'`` ``'=H'`` ``'uintne16'`` ``' None Add a new element with value `x` to the end of the Array. The type of `x` should be appropriate for the type of the Array. Raises a ``ValueError`` if the Array's bit length is not a multiple of its dtype length (see :attr:`~Array.trailing_bits`). .. method:: Array.astype(dtype: Dtype | str) -> Array Cast the ``Array`` to the new `dtype` and return the result. :: >>> a = Array('float64', [-990, 34, 1, 0.25]) >>> a.data BitArray('0xc08ef0000000000040410000000000003ff00000000000003fd0000000000000') >>> b = a.astype('float16') >>> b.data BitArray('0xe3bc50403c003400') >>> a == b Array('bool', [True, True, True, True]) .. method:: Array.byteswap() -> None Change the byte endianness of each element. Raises a ``ValueError`` if the format is not an integer number of bytes long. :: >>> a = Array('uint32', [100, 1, 999]) >>> a.byteswap() >>> a Array('uint32', [1677721600, 16777216, 3875733504]) >>> a.dtype = 'uintle32' >>> a Array('uintle32', [100, 1, 999]) .. method:: Array.count(value: float | int | str | bytes) -> int Returns the number of elements set to *value*. :: >>> a = Array('hex4') >>> a.data += '0xdeadbeef' >>> a Array('hex4', ['d', 'e', 'a', 'd', 'b', 'e', 'e', 'f']) >>> a.count('e') 3 For floating point types, using a `value` of ``float('nan')`` will count the number of elements for which ``math.isnan()`` returns ``True``. .. method:: Array.equals(other: Any) -> bool Equality test - `other` can be either another bitstring Array or an ``array``. Returns ``True`` if the dtypes are equivalent and the underlying bit data is the same, otherwise returns ``False``. :: >>> a = Array('u8', [1, 2, 3, 2, 1]) >>> a[0:3].equals(a[-1:-4:-1]) True >>> b = Array('i8', [1, 2, 3, 2, 1]) >>> a.equals(b) False To compare only the values contained in the Array, extract them using :meth:`~Array.tolist` first:: >>> a.tolist() == b.tolist() True Note that the ``==`` operator will perform an element-wise equality check and return a new ``Array`` of dtype ``'bool'`` (or raise an exception). >>> a == b Array('bool', [True, True, True, True, True]) .. method:: Array.extend(iterable: Iterable | Array) -> None Extend the Array by constructing new elements from the values in a list or other iterable. The `iterable` can be another ``Array`` or an ``array.array``, but only if the dtype is the same. :: >>> a = Array('int5', [-5, 0, 10]) >>> a.extend([3, 2, 1]) >>> a.extend(a[0:3] // 5) >>> a Array('int5', [-5, 0, 10, 3, 2, 1, -1, 0, 2]) .. method:: Array.fromfile(f: BinaryIO, n: int | None = None) -> None Append items read from a file object. .. method:: Array.insert(i: int, x: float | int | str | bytes) -> None Insert an item at a given position. :: >>> a = Array('p3binary', [-10, -5, -0.5, 5, 10]) >>> a.insert(3, 0.5) >>> a Array('p3binary', [-10.0, -5.0, -0.5, 0.5, 5.0, 10.0]) .. method:: Array.pop(i: int | None = None) -> float | int | str | bytes Remove and return the item at position i. If a position isn't specified the final item is returned and removed. :: >>> Array('bytes3', [b'ABC', b'DEF', b'ZZZ']) >>> a.pop(0) b'ABC' >>> a.pop() b'ZZZ' >>> a.pop() b'DEF' .. method:: Array.pp(fmt: str | None = None, width: int = 120, show_offset: bool = True, stream: TextIO = sys.stdout) -> None Pretty print the Array. The format string `fmt` defaults to the Array's current :attr:`dtype`, but any other valid Array format string can be used. If a `fmt` doesn't have an explicit length, the Array's :attr:`itemsize` will be used. A pair of comma-separated format strings can also be used - if both formats specify a length they must be the same. For example ``'float, hex16'`` or ``'u4, b4'``. The output will try to stay within `width` characters per line, but will always output at least one element value. Setting `show_offset` to ``False`` will hide the element index on each line of the output. An output `stream` can be specified. This should be an object with a ``write`` method and the default is ``sys.stdout``. >>> a = Array('u20', bytearray(range(100))) >>> a.pp(width=70, show_offset=False) [ 16 131844 20576 460809 41136 789774 61697 70163 82257 399128 102817 728093 123378 8482 143938 337447 164498 666412 185058 995377 205619 275766 226179 604731 246739 933696 267300 214085 287860 543050 308420 872015 328981 152404 349541 481369 370101 810334 390662 90723 ] >>> a.pp('hex32', width=70) [ 0: 00010203 04050607 08090a0b 0c0d0e0f 10111213 14151617 18191a1b 7: 1c1d1e1f 20212223 24252627 28292a2b 2c2d2e2f 30313233 34353637 14: 38393a3b 3c3d3e3f 40414243 44454647 48494a4b 4c4d4e4f 50515253 21: 54555657 58595a5b 5c5d5e5f 60616263 ] >>> a.pp('i12, hex', show_offset=False, width=70) [ 0 258 48 1029 96 1800 : 000 102 030 405 060 708 144 -1525 192 -754 241 17 : 090 a0b 0c0 d0e 0f1 011 289 788 337 1559 385 -1766 : 121 314 151 617 181 91a 433 -995 481 -224 530 547 : 1b1 c1d 1e1 f20 212 223 578 1318 626 -2007 674 -1236 : 242 526 272 829 2a2 b2c 722 -465 771 306 819 1077 : 2d2 e2f 303 132 333 435 867 1848 915 -1477 963 -706 : 363 738 393 a3b 3c3 d3e 1012 65 1060 836 1108 1607 : 3f4 041 424 344 454 647 1156 -1718 1204 -947 1252 -176 : 484 94a 4b4 c4d 4e4 f50 1301 595 1349 1366 1397 -1959 : 515 253 545 556 575 859 1445 -1188 1493 -417 1542 354 : 5a5 b5c 5d5 e5f 606 162 ] + trailing_bits = 0x63 By default the output will have colours added in the terminal. This can be disabled - see :data:`bitstring.options.no_color` for more information. .. method:: Array.reverse() -> None Reverse the order of all items in the Array. :: >>> a = Array('>L', [100, 200, 300]) >>> a.reverse() >>> a Array('>L', [300, 200, 100]) .. method:: Array.tobytes() -> bytes Return Array data as bytes object, padding with zero bits at the end if needed. :: >>> a = Array('i4', [3, -6, 2, -3, 2, -7]) >>> a.tobytes() b':-)' .. method:: Array.tofile(f: BinaryIO) -> None Write Array data to a file, padding with zero bits at the end if needed. .. method:: Array.tolist() -> List[float | int | str | bytes] Return Array items as a list. Each packed element of the Array is converted to an ordinary Python object such as a ``float`` or an ``int`` depending on the Array's format, and returned in a Python list. ---- Special Methods --------------- Type promotion """""""""""""" Many operations can be performed between two ``Array`` objects. For these to be valid the dtypes of the ``Array`` objects must be numerical, that is they must represent an integer or floating point value. Some operations have tighter restrictions, such as the shift operators ``<<`` and ``>>`` requiring integers only. The dtype of the resulting ``Array`` is calculated by applying these rules: **Rule 0**: For comparison operators (``<``, ``>=``, ``==``, ``!=`` etc.) the result is always an ``Array`` of dtype ``'bool'``. For other operators, one of the two input ``Array`` dtypes is used as the output dtype by applying the remaining rules in order until a winner is found: * **Rule 1**: Floating point types always win against integer types. * **Rule 2**: Signed integer types always win against unsigned integer types. * **Rule 3**: Longer types win against shorter types. * **Rule 4**: In a tie the first type wins. Some examples should help illustrate: =========== ================ ============ ================ === ================== **Rule 0** ``'uint8'`` ``<=`` ``'float64'`` → ``'bool'`` **Rule 1** ``'int32'`` ``+`` ``'float16'`` → ``'float16'`` **Rule 2** ``'uint20'`` ``//`` ``'int10'`` → ``'int10'`` **Rule 3** ``'int8'`` ``*`` ``'int16'`` → ``'int16'`` **Rule 4** ``'float16'`` ``-=`` ``'bfloat'`` → ``'float16'`` =========== ================ ============ ================ === ================== Comparison operators """""""""""""""""""" Comparison operators can operate between two ``Array`` objects, or between an ``Array`` and a scalar quantity (usually a number). Note that they always produce an ``Array`` of :attr:`~Array.dtype` ``'bool'``, including the equality and inequality operators. To test the boolean equality of two Arrays use the :meth:`~Array.equals` method instead. .. method:: Array.__eq__(self, other: int | float | str | BitsType | Array) -> Array ``a1 == a2`` .. method:: Array.__ne__(self, other: int | float | str | BitsType | Array) -> Array ``a1 != a2`` .. method:: Array.__lt__(self, other: int | float | Array) -> Array ``a1 < a2`` .. method:: Array.__le__(self, other: int | float | Array) -> Array ``a1 <= a2`` .. method:: Array.__gt__(self, other: int | float | Array) -> Array ``a1 > a2`` .. method:: Array.__ge__(self, other: int | float | Array) -> Array ``a1 >= a2`` Numerical operators """"""""""""""""""" .. method:: Array.__add__(other: int | float | Array) -> Array ``a + x`` .. method:: Array.__sub__(self, other: int | float | Array) -> Array ``a - x`` .. method:: Array.__mul__(self, other: int | float | Array) -> Array ``a * x`` .. method:: Array.__truediv__(self, other: int | float | Array) -> Array ``a / x`` .. method:: Array.__floordiv__(self, other: int | float | Array) -> Array ``a // x`` .. method:: Array.__rshift__(self, other: int | Array) -> Array ``a >> i`` .. method:: Array.__lshift__(self, other: int | Array) -> Array ``a << i`` .. method:: Array.__mod__(self, other: int | Array) -> Array ``a % i`` .. method:: Array.__neg__(self) -> Array ``-a`` .. method:: Array.__abs__(self) -> Array ``abs(a)`` Bitwise operators """"""""""""""""" .. method:: Array.__and__(self, other: Bits) -> Array ``a & bs`` >>> a &= '0b1110' .. method:: Array.__or__(self, other: Bits) -> Array ``a | bs`` >>> a |= '0x7fff' .. method:: Array.__xor__(self, other: Bits) -> Array ``a ^ bs`` >>> a ^= bytearray([56, 23]) Python language operators """"""""""""""""""""""""" .. method:: Array.__len__(self) -> int ``len(a)`` Return the number of elements in the Array. :: >>> a = Array('uint20', [1, 2, 3]) >>> len(a) 3 >>> a.dtype = 'uint1' >>> len(a) 60 .. method:: Array.__getitem__(self, key: int | slice) -> float | int | str | bytes | Array ``a[i]`` ``a[start:end:step]`` .. method:: Array.__setitem__(self, key: int | slice, value) -> None ``a[i] = x`` ``a[start:end:step] = x`` .. method:: Array.__delitem__(self, key: int | slice) -> None ``del a[i]`` ``del[start:end:step]`` ---- Properties ---------- .. attribute:: Array.data :type: BitArray The bit data of the ``Array``, as a ``BitArray``. Read and write, and can be freely manipulated with all ``BitArray`` methods. Note that some ``Array`` methods such as :meth:`~Array.append` and :meth:`~Array.extend` require the :attr:`~Array.data` to have a length that is a multiple of the ``Array``'s :attr:`~Array.itemsize`. .. attribute:: Array.dtype :type: Dtype The data type used to initialise the ``Array`` type. Read and write. Changing the ``dtype`` for an already formed ``Array`` will cause all of the bit data to be reinterpreted and can change the length of the ``Array``. However, changing the ``dtype`` won't change the underlying bit data in any way. Note that some ``Array`` methods such as :meth:`~Array.append` and :meth:`~Array.extend` require the bit data to have a length that is a multiple of the ``Array``'s :attr:`~Array.itemsize`. .. attribute:: Array.itemsize :type: int The size *in bits* of each item in the ``Array``. Read-only. Note that this gives a value in bits, unlike the equivalent in the ``array`` module which gives a value in bytes. :: >>> a = Array('>h') >>> b = Array('bool') >>> a.itemsize 16 >>> b.itemsize 1 .. attribute:: Array.trailing_bits :type: BitArray A ``BitArray`` object equal to the end of the ``data`` that is not a multiple of the ``itemsize``. Read only. This will typically be an empty ``BitArray``, but if the ``dtype`` or the ``data`` of an ``Array`` object has been altered after its creation then there may be left-over bits at the end of the data. Note that any methods that append items to the ``Array`` will fail with a ``ValueError`` if there are any trailing bits. bitstring-bitstring-4.2.3/doc/bitarray.rst000066400000000000000000000254011462444752600206640ustar00rootroot00000000000000.. currentmodule:: bitstring BitArray ======== .. class:: BitArray(auto: BitsType | int | None, /, length: int | None = None, offset: int | None = None, **kwargs) The :class:`Bits` class is the base class for :class:`BitArray` and so (with the exception of :meth:`~Bits.__hash__`) all of its methods are also available for :class:`BitArray` objects. The initialiser is the same as for :class:`Bits`. A :class:`BitArray` is a mutable :class:`Bits`, and so the one thing all of the methods listed here have in common is that they can modify the contents of the bitstring. ---- Methods ------- .. method:: BitArray.append(bs: BitsType) -> None Join a :class:`BitArray` to the end of the current :class:`BitArray`. :: >>> s = BitArray('0xbad') >>> s.append('0xf00d') >>> s BitArray('0xbadf00d') .. method:: BitArray.byteswap(fmt: str | int | Iterable[int] | None = None, start: int | None = None, end: int | None = None, repeat: bool = True) -> int Change the endianness of the :class:`BitArray` in-place according to *fmt*. Return the number of swaps done. The *fmt* can be an integer, an iterable of integers or a compact format string similar to those used in :func:`pack` (described in :ref:`compact_format`). It defaults to 0, which means reverse as many bytes as possible. The *fmt* gives a pattern of byte sizes to use to swap the endianness of the :class:`BitArray`. Note that if you use a compact format string then the endianness identifier (``<``, ``>`` or ``=``) is not needed, and if present it will be ignored. *start* and *end* optionally give a slice to apply the transformation to (it defaults to the whole :class:`BitArray`). If *repeat* is ``True`` then the byte swapping pattern given by the *fmt* is repeated in its entirety as many times as possible. >>> s = BitArray('0x00112233445566') >>> s.byteswap(2) 3 >>> s BitArray('0x11003322554466') >>> s.byteswap('h') 3 >>> s BitArray('0x00112233445566') >>> s.byteswap([2, 5]) 1 >>> s BitArray('0x11006655443322') It can also be used to swap the endianness of the whole :class:`BitArray`. :: >>> s = BitArray('uintle32=1234') >>> s.byteswap() >>> print(s.uintbe) 1234 .. method:: BitArray.clear() -> None Removes all bits from the bitstring. ``s.clear()`` is equivalent to ``del s[:]`` and simply makes the bitstring empty. .. method:: BitArray.insert(bs: BitsType, pos: int) -> None Inserts *bs* at *pos*. When used with the :class:`BitStream` class the *pos* is optional, and if not present the current bit position will be used. After insertion the property :attr:`~ConstBitStream.pos` will be immediately after the inserted bitstring. :: >>> s = BitStream('0xccee') >>> s.insert('0xd', 8) >>> s BitStream('0xccdee') >>> s.insert('0x00') >>> s BitStream('0xccd00ee') .. method:: BitArray.invert(pos: int | Iterable[int] | None = None) -> None Inverts one or many bits from ``1`` to ``0`` or vice versa. *pos* can be either a single bit position or an iterable of bit positions. Negative numbers are treated in the same way as slice indices and it will raise :exc:`IndexError` if ``pos < -len(s)`` or ``pos > len(s)``. The default is to invert the entire :class:`BitArray`. :: >>> s = BitArray('0b111001') >>> s.invert(0) >>> s.bin '011001' >>> s.invert([-2, -1]) >>> s.bin '011010' >>> s.invert() >>> s.bin '100101' .. method:: BitArray.overwrite(bs: BitsType, pos: int) -> None Replaces the contents of the current :class:`BitArray` with *bs* at *pos*. When used with the :class:`BitStream` class the *pos* is optional, and if not present the current bit position will be used. After insertion the property :attr:`~ConstBitStream.pos` will be immediately after the overwritten bitstring. :: >>> s = BitArray(length=10) >>> s.overwrite('0b111', 3) >>> s BitArray('0b0001110000') >>> s.pos 6 .. method:: BitArray.prepend(bs: BitsType) -> None Inserts *bs* at the beginning of the current :class:`BitArray`. :: >>> s = BitArray('0b0') >>> s.prepend('0xf') >>> s BitArray('0b11110') .. method:: BitArray.replace(old: BitsType, new: BitsType, start: int | None = None, end: int | None = None, count: int | None = None, bytealigned: bool | None = None) -> int Finds occurrences of *old* and replaces them with *new*. Returns the number of replacements made. If *bytealigned* is ``True`` then replacements will only be made on byte boundaries. *start* and *end* give the search range and default to the start and end of the bitstring. If *count* is specified then no more than this many replacements will be made. :: >>> s = BitArray('0b0011001') >>> s.replace('0b1', '0xf') 3 >>> print(s.bin) 0011111111001111 >>> s.replace('0b1', '', count=6) 6 >>> print(s.bin) 0011001111 .. method:: BitArray.reverse(start: int | None = None, end: int | None = None) -> None Reverses bits in the :class:`BitArray` in-place. *start* and *end* give the range of bits to reverse and default to the start and end of the bitstring. :: >>> a = BitArray('0b000001101') >>> a.reverse() >>> a.bin '101100000' >>> a.reverse(0, 4) >>> a.bin '110100000' .. method:: BitArray.rol(bits: int, start: int | None = None, end: int | None = None) -> None Rotates the contents of the :class:`BitArray` in-place by *bits* bits to the left. *start* and *end* define the slice to use and default to the start and end of the bitstring. Raises :exc:`ValueError` if ``bits < 0``. :: >>> s = BitArray('0b01000001') >>> s.rol(2) >>> s.bin '00000101' .. method:: BitArray.ror(bits: int, start: int | None = None, end: int | None = None) -> None Rotates the contents of the :class:`BitArray` in-place by *bits* bits to the right. *start* and *end* define the slice to use and default to the start and end of the bitstring. Raises :exc:`ValueError` if ``bits < 0``. .. method:: BitArray.set(value: bool, pos: int | Iterable[int] | None = None) -> None Sets one or many bits to either ``1`` (if *value* is ``True``) or ``0`` (if *value* isn't ``True``). *pos* can be either a single bit position or an iterable of bit positions. Negative numbers are treated in the same way as slice indices and it will raise :exc:`IndexError` if ``pos < -len(s)`` or ``pos > len(s)``. The default is to set every bit in the :class:`BitArray`. Using ``s.set(True, x)`` can be more efficient than other equivalent methods such as ``s[x] = 1``, ``s[x] = "0b1"`` or ``s.overwrite('0b1', x)``, especially if many bits are being set. In particular using a ``range`` object as an iterable is treated as a special case and is done efficiently. :: >>> s = BitArray('0x0000') >>> s.set(True, -1) >>> print(s) 0x0001 >>> s.set(1, (0, 4, 5, 7, 9)) >>> s.bin '1000110101000001' >>> s.set(0) >>> s.bin '0000000000000000' >>> s.set(1, range(0, len(s), 2)) >>> s.bin '1010101010101010' ---- Properties ---------- Note that the ``bin``, ``oct``, ``hex``, ``int``, ``uint`` and ``float`` properties can all be shortened to their initial letter. Properties can also have a length in bits appended to them to make properties such as ``u8`` or ``floatle64`` (with the exception of the ``bytes`` property which uses a unit of bytes instead of bits, so ``bytes4`` is 32 bits long). These properties with lengths can be used to quickly create a new bitstring. :: >>> a = BitArray() >>> a.f32 = 17.6 >>> a.h '418ccccd' >>> a.i7 = -1 >>> a.b '1111111' The binary interpretation properties of the :class:`Bits` class all become writable in the :class:`BitArray` class. For integer types, the properties can have a bit length appended to it such as ``u32`` or ``int5`` to specify the new length of the bitstring. Using a length too small to contain the value given will raise a :exc:`CreationError`. When used as a setter without a new length the value must fit into the current length of the :class:`BitArray`, else a :exc:`ValueError` will be raised. :: >>> s = BitArray('0xf3') >>> s.int -13 >>> s.int = 1232 ValueError: int 1232 is too large for a BitArray of length 8. Other types also have restrictions on their lengths, and using an invalid length will raise a :exc:`CreationError`. For example trying to create a 20 bit floating point number or a two bit bool will raise this exception. ---- Special Methods --------------- .. method:: BitArray.__delitem__(key) ``del s[start:end:step]`` Deletes the slice specified. .. method:: BitArray.__iadd__(bs) ``s1 += s2`` Appends *bs* to the current bitstring. Note that for :class:`BitArray` objects this will be an in-place change, whereas for :class:`Bits` objects using ``+=`` will not call this method - instead a new object will be created (it is equivalent to a copy and an :meth:`~Bits.__add__`). :: >>> s = BitArray(ue=423) >>> s += BitArray(ue=12) >>> s.read('ue') 423 >>> s.read('ue') 12 .. method:: BitArray.__iand__(bs) ``s &= bs`` In-place bit-wise AND between two bitstrings. If the two bitstrings are not the same length then a :exc:`ValueError` is raised. .. method:: BitArray.__ilshift__(n) ``s <<= n`` Shifts the bits in-place *n* bits to the left. The *n* right-most bits will become zeros and bits shifted off the left will be lost. .. method:: BitArray.__imul__(n) ``s *= n`` In-place concatenation of *n* copies of the current bitstring. >>> s = BitArray('0xbad') >>> s *= 3 >>> s.hex 'badbadbad' .. method:: BitArray.__ior__(bs) ``s |= bs`` In-place bit-wise OR between two bitstrings. If the two bitstrings are not the same length then a :exc:`ValueError` is raised. .. method:: BitArray.__irshift__(n) ``s >>= n`` Shifts the bits in-place *n* bits to the right. The *n* left-most bits will become zeros and bits shifted off the right will be lost. .. method:: BitArray.__ixor__(bs) ``s ^= bs`` In-place bit-wise XOR between two bitstrings. If the two bitstrings are not the same length then a :exc:`ValueError` is raised. .. method:: BitArray.__setitem__(key, value) ``s1[start:end:step] = s2`` Replaces the slice specified with a new value. :: >>> s = BitArray('0x00000000') >>> s[::8] = '0xf' >>> print(s) 0x80808080 >>> s[-12:] = '0xf' >>> print(s) 0x80808f bitstring-bitstring-4.2.3/doc/bits.rst000066400000000000000000000644171462444752600200220ustar00rootroot00000000000000.. currentmodule:: bitstring Bits ==== The ``Bits`` class is the simplest type in the bitstring module, and represents an immutable sequence of bits. This is the best class to use if you will not need to modify the data after creation and don't need streaming methods. .. class:: Bits(auto: BitsType | int | None, /, length: int | None = None, offset: int | None = None, **kwargs) Creates a new bitstring. You must specify either no initialiser, just an 'auto' value as the first parameter, or a keyword argument such as ``bytes``, ``bin``, ``hex``, ``oct``, ``uint``, ``int``, ``float``, ``bool`` or ``filename`` (for example) to indicate the data type. If no initialiser is given then a zeroed bitstring of ``length`` bits is created. The initialiser for the :class:`Bits` class is precisely the same as for :class:`BitArray`, :class:`BitStream` and :class:`ConstBitStream`. ``offset`` is available when using the ``bytes`` or ``filename`` initialisers. It gives a number of bits to ignore at the start of the bitstring. Specifying ``length`` is mandatory when using the various integer initialisers. It must be large enough that a bitstring can contain the integer in ``length`` bits. It must also be specified for the float initialisers (the only valid values are 16, 32 and 64). It is optional for the ``bytes`` and ``filename`` initialisers and can be used to truncate data from the end of the input value. :: >>> s1 = Bits(hex='0x934') >>> s2 = Bits(oct='0o4464') >>> s3 = Bits(bin='0b001000110100') >>> s4 = Bits(int=-1740, length=12) >>> s5 = Bits(uint=2356, length=12) >>> s6 = Bits(bytes=b'\x93@', length=12) >>> s1 == s2 == s3 == s4 == s5 == s6 True See also :ref:`auto_init`, which allows many different types to be used to initialise a bitstring. :: >>> s = Bits('uint12=32, 0b110') >>> t = Bits('0o755, ue=12, int:3=-1') In the methods below we use ``BitsType`` to indicate that any of the types that can auto initialise can be used. ---- Methods ------- .. method:: Bits.all(value: bool, pos: Iterable[int] | None = None) -> bool Returns ``True`` if all of the specified bits are all set to *value*, otherwise returns ``False``. If *value* is ``True`` then ``1`` bits are checked for, otherwise ``0`` bits are checked for. *pos* should be an iterable of bit positions. Negative numbers are treated in the same way as slice indices and it will raise an :exc:`IndexError` if ``pos < -len(s)`` or ``pos > len(s)``. It defaults to the whole bitstring. >>> s = Bits('int15=-1') >>> s.all(True, [3, 4, 12, 13]) True >>> s.all(1) True .. method:: Bits.any(value: bool, pos: Iterable[int] | None = None) -> bool Returns ``True`` if any of the specified bits are set to *value*, otherwise returns ``False``. If *value* is ``True`` then ``1`` bits are checked for, otherwise ``0`` bits are checked for. *pos* should be an iterable of bit positions. Negative numbers are treated in the same way as slice indices and it will raise an :exc:`IndexError` if ``pos < -len(s)`` or ``pos > len(s)``. It defaults to the whole bitstring. >>> s = Bits('0b11011100') >>> s.any(False, range(6)) True >>> s.any(1) True .. method:: Bits.copy() -> Bits Returns a copy of the bitstring. ``s.copy()`` is equivalent to the shallow copy ``s[:]`` and creates a new copy of the bitstring in memory. .. method:: Bits.count(value: bool) -> int Returns the number of bits set to *value*. *value* can be ``True`` or ``False`` or anything that can be cast to a bool, so you could equally use ``1`` or ``0``. >>> s = BitArray(1000000) >>> s.set(1, [4, 44, 444444]) >>> s.count(1) 3 >>> s.count(False) 999997 If you need to count more than just single bits you can use :meth:`~Bits.findall`, for example ``len(list(s.findall('0xabc')))``. Note that if the bitstring is very sparse, as in the example here, it could be quicker to find and count all the set bits with something like ``len(list(s.findall('0b1')))``. For bitstrings with more entropy the ``count`` method will be much quicker than finding. .. method:: Bits.cut(bits: int, start: int | None = None, end: int | None = None, count: int | None = None) -> Iterator[Bits] Returns a generator for slices of the bitstring of length *bits*. At most *count* items are returned and the range is given by the slice *[start:end]*, which defaults to the whole bitstring. :: >>> s = BitArray('0x1234') >>> for nibble in s.cut(4): ... s.prepend(nibble) >>> print(s) 0x43211234 .. method:: Bits.endswith(bs: BitsType, start: int | None = None, end: int | None = None) -> bool Returns ``True`` if the bitstring ends with the sub-string *bs*, otherwise returns ``False``. A slice can be given using the *start* and *end* bit positions and defaults to the whole bitstring. :: >>> s = Bits('0x35e22') >>> s.endswith('0b10, 0x22') True >>> s.endswith('0x22', start=13) False .. method:: Bits.find(bs: BitsType, start: int | None = None, end: int | None = None, bytealigned: bool | None = None) -> Tuple[int] | Tuple[()] Searches for *bs* in the current bitstring and sets :attr:`~ConstBitStream.pos` to the start of *bs* and returns it in a tuple if found, otherwise it returns an empty tuple. The reason for returning the bit position in a tuple is so that it evaluates as True even if the bit position is zero. This allows constructs such as ``if s.find('0xb3'):`` to work as expected. If *bytealigned* is ``True`` then it will look for *bs* only at byte aligned positions (which is generally much faster than searching for it in every possible bit position). *start* and *end* give the search range and default to the whole bitstring. :: >>> s = Bits('0x0023122') >>> s.find('0b000100', bytealigned=True) (16,) .. method:: Bits.findall(bs: BitsType, start: int | None = None, end: int | None = None, count: int | None = None, bytealigned: bool | None = None) -> Iterable[int] Searches for all occurrences of *bs* (even overlapping ones) and returns a generator of their bit positions. If *bytealigned* is ``True`` then *bs* will only be looked for at byte aligned positions. *start* and *end* optionally define a search range and default to the whole bitstring. The *count* parameter limits the number of items that will be found - the default is to find all occurrences. :: >>> s = Bits('0xab220101')*5 >>> list(s.findall('0x22', bytealigned=True)) [8, 40, 72, 104, 136] .. classmethod:: Bits.fromstring(s: str, /) -> Bits Creates a new bitstring from the formatted string *s*. It is equivalent to creating a new bitstring using *s* as the first parameters, but can be clearer to write and will be slightly faster. >>> b1 = Bits('int16=91') >>> b2 = Bits.fromstring('int16=91') >>> b1 == b2 True .. method:: Bits.join(sequence: Iterable) -> Bits Returns the concatenation of the bitstrings in the iterable *sequence* joined with ``self`` as a separator. :: >>> s = Bits().join(['0x0001ee', 'uint:24=13', '0b0111']) >>> print(s) 0x0001ee00000d7 >>> s = Bits('0b1').join(['0b0']*5) >>> print(s.bin) 010101010 .. method:: Bits.pp(fmt: str | None = None, width: int = 120, sep: str = ' ', show_offset: bool = True, stream: TextIO = sys.stdout) -> None Pretty print the bitstring's value according to the *fmt*. Either a single, or two comma separated formats can be specified, together with options for setting the maximum display *width*, the number of bits to display in each group, and the separator to print between groups. >>> s = Bits('0b10111100101101001')*20 >>> s.pp(width=80) [ 0: 10111100 10110100 11011110 01011010 01101111 00101101 : bc b4 de 5a 6f 2d 48: 00110111 10010110 10011011 11001011 01001101 11100101 : 37 96 9b cb 4d e5 96: 10100110 11110010 11010011 01111001 01101001 10111100 : a6 f2 d3 79 69 bc 144: 10110100 11011110 01011010 01101111 00101101 00110111 : b4 de 5a 6f 2d 37 192: 10010110 10011011 11001011 01001101 11100101 10100110 : 96 9b cb 4d e5 a6 240: 11110010 11010011 01111001 01101001 10111100 10110100 : f2 d3 79 69 bc b4 288: 11011110 01011010 01101111 00101101 00110111 10010110 : de 5a 6f 2d 37 96 ] + trailing_bits = 0x9 >>> s.pp('int20, hex', width=80, show_offset=False, sep=' / ') [ -275635 / -107921 / 185209 / 433099 : bcb4d / e5a6f / 2d379 / 69bcb 319066 / 455379 / 497307 / -215842 : 4de5a / 6f2d3 / 7969b / cb4de 370418 / -182378 / -410444 / -137818 : 5a6f2 / d3796 / 9bcb4 / de5a6 -53961 / -431684 / -307739 / -364755 : f2d37 / 969bc / b4de5 / a6f2d 227689 : 37969 ] The available formats are any fixed-length dtypes, for example ``'bin'``, ``'oct'``, ``'hex'`` and ``'bytes'`` together with types with explicit lengths such as ``'uint5'`` and ``'float16'``. A bit length can be specified after the format (with an optional `:`) to give the number of bits represented by each group, otherwise the default is based on the format or formats selected. For the ``'bytes'`` format, characters from the 'Latin Extended-A' unicode block are used for non-ASCII and unprintable characters. If the bitstring cannot be represented in a format due to its length not being a multiple of the number of bits represented by each character then an :exc:`InterpretError` will be raised. An output *stream* can be specified. This should be an object with a ``write`` method and the default is ``sys.stdout``. By default the output will have colours added in the terminal. This can be disabled - see :data:`bitstring.options.no_color` for more information. .. method:: Bits.rfind(bs: BitsType, start: int | None = None, end: int | None = None, bytealigned: bool | None = None) -> Tuple[int] | Tuple[()] Searches backwards for *bs* in the current bitstring and sets :attr:`~ConstBitStream.pos` to the start of *bs* and returns it in a tuple if found, otherwise it returns an empty tuple. The reason for returning the bit position in a tuple is so that it evaluates as True even if the bit position is zero. This allows constructs such as ``if s.rfind('0xb3'):`` to work as expected. If *bytealigned* is ``True`` then it will look for *bs* only at byte aligned positions. *start* and *end* give the search range and default to ``0`` and :attr:`len` respectively. Note that as it's a reverse search it will start at *end* and finish at *start*. :: >>> s = Bits('0o031544') >>> s.rfind('0b100') (15,) >>> s.rfind('0b100', end=17) (12,) .. method:: Bits.split(delimiter: BitsType, start: int | None = None, end: int | None = None, count: int | None = None, bytealigned: bool | None = None) -> Iterable[Bits] Splits the bitstring into sections that start with *delimiter*. Returns a generator for bitstring objects. The first item generated is always the bits before the first occurrence of delimiter (even if empty). A slice can be optionally specified with *start* and *end*, while *count* specifies the maximum number of items generated. If *bytealigned* is ``True`` then the delimiter will only be found if it starts at a byte aligned position. :: >>> s = Bits('0x42423') >>> [bs.bin for bs in s.split('0x4')] ['', '01000', '01001000', '0100011'] .. method:: Bits.startswith(bs: BitsType, start: int | None = None, end: int | None = None) -> bool Returns ``True`` if the bitstring starts with the sub-string *bs*, otherwise returns ``False``. A slice can be given using the *start* and *end* bit positions and defaults to the whole bitstring. :: >>> s = BitArray('0xef133') >>> 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 Returns the bitstring as a ``bytes`` object. The returned value will be padded at the end with between zero and seven ``0`` bits to make it byte aligned. This differs from using the plain :attr:`~Bits.bytes` property which will not pad with zero bits and instead raises an exception if the bitstring is not a whole number of bytes long. This method can also be used to output your bitstring to a file - just open a file in binary write mode and write the function's output. :: >>> s = Bits(bytes=b'hello') >>> s += '0b01' >>> s.tobytes() b'hello@' This is equivalent to casting to a bytes object directly: :: >>> bytes(s) b'hello@' .. method:: Bits.tofile(f: BinaryIO) -> None Writes the bitstring to the file object *f*, which should have been opened in binary write mode. The data written will be padded at the end with between zero and seven ``0`` bits to make it byte aligned. :: >>> f = open('newfile', 'wb') >>> Bits('0x1234').tofile(f) .. method:: Bits.unpack(fmt: str | list[str | int], **kwargs) -> list[float | int | str | None | Bits] Interprets the whole bitstring according to the *fmt* string or iterable and returns a list of bitstring objects. A dictionary or keyword arguments can also be provided. These will replace length identifiers in the format string. *fmt* is an iterable or a string with comma separated tokens that describe how to interpret the next bits in the bitstring. See the :ref:`format_tokens` for details. :: >>> s = Bits('int4=-1, 0b1110') >>> i, b = s.unpack('int:4, bin') If a token doesn't supply a length (as with ``bin`` above) then it will try to consume the rest of the bitstring. Only one such token is allowed. The ``unpack`` method is a natural complement of the :func:`pack` function. :: s = bitstring.pack('uint10, hex, int13, 0b11', 130, '3d', -23) a, b, c, d = s.unpack('uint10, hex, int13, bin2') ---- Properties ---------- The many ways to interpret bitstrings can be accessed via properties. These properties will be read-only for a ``Bits`` object, but are also writable for derived mutable types such as ``BitArray`` and ``BitStream``. Properties can also have a length in bits appended to them to such as ``u8`` or ``f64`` (for the ``bytes`` property the length is interpreted in bytes instead of bits). These properties with lengths will cause an :exc:`InterpretError` to be raised if the bitstring is not of the specified length. This list isn't exhaustive - see for example :ref:`Exotic floats` for information on bfloats and many 8-bit and smaller floating point formats. Also see :ref:`exp-golomb` for some interesting variable length integer formats. Note that the ``bin``, ``oct``, ``hex``, ``int``, ``uint`` and ``float`` properties can all be shortened to their initial letter. .. attribute:: Bits.bin :type: str Property for the representation of the bitstring as a binary string. Can be shortened to just ``b``. .. attribute:: Bits.bool :type: bool Property for representing the bitstring as a boolean (``True`` or ``False``). If the bitstring is not a single bit then the getter will raise an :exc:`InterpretError`. .. attribute:: Bits.bytes :type: bytes Property representing the underlying byte data that contains the bitstring. When used as a getter the bitstring must be a whole number of byte long or a :exc:`InterpretError` will be raised. An alternative is to use the :meth:`tobytes` method, which will pad with between zero and seven ``0`` bits to make it byte aligned if needed. :: >>> s = Bits('0x12345678') >>> s.bytes b'\x124Vx' .. attribute:: Bits.hex :type: str Property representing the hexadecimal value of the bitstring. Can be shortened to just ``h``. If the bitstring is not a multiple of four bits long then getting its hex value will raise an :exc:`InterpretError`. :: >>> s = Bits(bin='1111 0000') >>> s.hex 'f0' .. attribute:: Bits.int :type: int Property for the signed two’s complement integer representation of the bitstring. Can be shortened to just ``i``. .. attribute:: Bits.intbe :type: int Property for the byte-wise big-endian signed two's complement integer representation of the bitstring. Only valid for whole-byte bitstrings, in which case it is equal to ``s.int``, otherwise an :exc:`InterpretError` is raised. .. attribute:: Bits.intle :type: int Property for the byte-wise little-endian signed two's complement integer representation of the bitstring. Only valid for whole-byte bitstring, in which case it is equal to ``s[::-8].int``, i.e. the integer representation of the byte-reversed bitstring. .. attribute:: Bits.intne :type: int Property for the byte-wise native-endian signed two's complement integer representation of the bitstring. Only valid for whole-byte bitstrings, and will equal either the big-endian or the little-endian integer representation depending on the platform being used. .. attribute:: Bits.float :type: float .. attribute:: Bits.floatbe :type: float Property for the floating point representation of the bitstring. Can be shortened to just ``f``. The bitstring must be 16, 32 or 64 bits long to support the floating point interpretations, otherwise an :exc:`InterpretError` will be raised. If the underlying floating point methods on your machine are not IEEE 754 compliant then using the float interpretations is undefined (this is unlikely unless you're on some very unusual hardware). The :attr:`float` property is bit-wise big-endian, which as all floats must be whole-byte is exactly equivalent to the byte-wise big-endian :attr:`floatbe`. .. attribute:: Bits.floatle :type: float Property for the byte-wise little-endian floating point representation of the bitstring. .. attribute:: Bits.floatne :type: float Property for the byte-wise native-endian floating point representation of the bitstring. .. attribute:: Bits.len :type: int .. attribute:: Bits.length :type: int :noindex: Read-only property that give the length of the bitstring in bits (:attr:`len` and ``length`` are equivalent). Using the ``len()`` built-in function is preferred in almost all cases, but these properties are available for backward compatibility. The only occasion where the properties are needed is if a 32-bit build of Python is being used and you have a bitstring whose length doesn't fit in a 32-bit unsigned integer. In that case ``len(s)`` may fail with an :exc:`OverflowError`, whereas ``s.len`` will still work. With 64-bit Python the problem shouldn't occur unless you have more than a couple of exabytes of data! .. attribute:: Bits.oct :type: str Property for the octal representation of the bitstring. Can be shortened to just ``o``. If the bitstring is not a multiple of three bits long then getting its octal value will raise a :exc:`InterpretError`. :: >>> s = Bits('0b111101101') >>> s.oct '755' >>> s.oct = '01234567' >>> s.oct '01234567' .. attribute:: Bits.uint :type: int Property for the unsigned base-2 integer representation of the bitstring. Can be shortened to just ``u``. .. attribute:: Bits.uintbe :type: int Property for the byte-wise big-endian unsigned base-2 integer representation of the bitstring. .. attribute:: Bits.uintle :type: int Property for the byte-wise little-endian unsigned base-2 integer representation of the bitstring. .. attribute:: Bits.uintne :type: int Property for the byte-wise native-endian unsigned base-2 integer representation of the bitstring. ---- Special Methods --------------- .. method:: Bits.__add__(bs) .. method:: Bits.__radd__(bs) ``s1 + s2`` Concatenate two bitstring objects and return the result. Either bitstring can be 'auto' initialised. :: s = Bits(ue=132) + '0xff' s2 = '0b101' + s .. method:: Bits.__and__(bs) .. method:: Bits.__rand__(bs) ``s1 & s2`` Returns the bit-wise AND between two bitstrings, which must have the same length otherwise a :exc:`ValueError` is raised. :: >>> print(Bits('0x33') & '0x0f') 0x03 .. method:: Bits.__bool__() ``if s:`` Returns ``False`` if the bitstring is empty (has zero length), otherwise returns ``True``. >>> bool(Bits()) False >>> bool(Bits('0b0000010000')) True >>> bool(Bits('0b0000000000')) True .. method:: Bits.__contains__(bs) ``bs in s`` Returns ``True`` if *bs* can be found in the bitstring, otherwise returns ``False``. Similar to using :meth:`~Bits.find`, except that you are only told if it is found, and not where it was found. :: >>> '0b11' in Bits('0x06') True >>> '0b111' in Bits('0x06') False .. method:: Bits.__copy__() ``s2 = copy.copy(s1)`` This allows the ``copy`` module to correctly copy bitstrings. Other equivalent methods are to initialise a new bitstring with the old one or to take a complete slice. :: >>> import copy >>> s = Bits('0o775') >>> s_copy1 = copy.copy(s) >>> s_copy2 = Bits(s) >>> s_copy3 = s[:] >>> s == s_copy1 == s_copy2 == s_copy3 True .. method:: Bits.__eq__(bs) ``s1 == s2`` Compares two bitstring objects for equality, returning ``True`` if they have the same binary representation, otherwise returning ``False``. :: >>> Bits('0o7777') == '0xfff' True >>> a = Bits(uint=13, length=8) >>> b = Bits(uint=13, length=10) >>> a == b False If you have a different criterion you wish to use then code it explicitly, for example ``a.int == b.int`` could be true even if ``a == b`` wasn't (as they could be different lengths). .. method:: Bits.__getitem__(key) ``s[start:end:step]`` Returns a slice of the bitstring. The usual slice behaviour applies. :: >>> s = Bits('0x0123456') >>> s[4:8] Bits('0x1') >>> s[1::8] # 1st, 9th, 17th and 25th bits Bits('0x3') If a single element is asked for then either ``True`` or ``False`` will be returned. :: >>> s[0] False >>> s[-1] True .. method:: Bits.__hash__() ``hash(s)`` Returns an integer hash of the :class:`Bits`. This method is not available for the :class:`BitArray` or :class:`BitStream` classes, as only immutable objects should be hashed. You typically won't need to call it directly, instead it is used for dictionary keys and in sets. .. method:: Bits.__invert__() ``~s`` Returns the bitstring with every bit inverted, that is all zeros replaced with ones, and all ones replaced with zeros. If the bitstring is empty then an :exc:`Error` will be raised. :: >>> s = ConstBitStream(‘0b1110010’) >>> print(~s) 0b0001101 >>> print(~s & s) 0b0000000 >>> ~~s == s True .. method:: Bits.__len__() ``len(s)`` Returns the length of the bitstring in bits. If you are using a 32-bit Python build (which is quite unlikely these days) it's recommended that you use the :attr:`len` property rather than the :func:`len` function because of the function will raise a :exc:`OverflowError` if the length is greater than ``sys.maxsize``. .. method:: Bits.__lshift__(n) ``s << n`` Returns the bitstring with its bits shifted *n* places to the left. The *n* right-most bits will become zeros. :: >>> s = Bits('0xff') >>> s << 4 Bits('0xf0') .. method:: Bits.__mul__(n) .. method:: Bits.__rmul__(n) ``s * n / n * s`` Return bitstring consisting of *n* concatenations of another. :: >>> a = Bits('0x34') >>> b = a*5 >>> print(b) 0x3434343434 .. method:: Bits.__ne__(bs) ``s1 != s2`` Compares two bitstring objects for inequality, returning ``False`` if they have the same binary representation, otherwise returning ``True``. .. method:: Bits.__nonzero__() See :meth:`__bool__`. .. method:: Bits.__or__(bs) .. method:: Bits.__ror__(bs) ``s1 | s2`` Returns the bit-wise OR between two bitstring, which must have the same length otherwise a :exc:`ValueError` is raised. :: >>> print(Bits('0x33') | '0x0f') 0x3f .. method:: Bits.__repr__() ``repr(s)`` A representation of the bitstring that could be used to create it (which will often not be the form used to create it). If the result is too long then it will be truncated with ``...`` and the length of the whole will be given. :: >>> Bits(‘0b11100011’) Bits(‘0xe3’) .. method:: Bits.__rshift__(n) ``s >> n`` Returns the bitstring with its bits shifted *n* places to the right. The *n* left-most bits will become zeros. :: >>> s = Bits(‘0xff’) >>> s >> 4 Bits(‘0x0f’) .. method:: Bits.__str__() ``print(s)`` Used to print a representation of the bitstring, trying to be as brief as possible. If the bitstring is a multiple of 4 bits long then hex will be used, otherwise either binary or a mix of hex and binary will be used. Very long strings will be truncated with ``...``. :: >>> s = Bits('0b1')*7 >>> print(s) 0b1111111 >>> print(s + '0b1') 0xff See also the :meth:`pp` method for ways to pretty-print the bitstring. .. method:: Bits.__xor__(bs) .. method:: Bits.__rxor__(bs) ``s1 ^ s2`` Returns the bit-wise XOR between two bitstrings, which must have the same length otherwise a :exc:`ValueError` is raised. :: >>> print(Bits('0x33') ^ '0x0f') 0x3c bitstring-bitstring-4.2.3/doc/bitstream.rst000066400000000000000000000024111462444752600210350ustar00rootroot00000000000000.. currentmodule:: bitstring BitStream ========= .. class:: BitStream(auto: BitsType | int | None, /, length: int | None = None, offset: int | None = None, pos: int = 0, **kwargs) Both the :class:`BitArray` and the :class:`ConstBitStream` classes are base classes for :class:`BitStream` and so all of their methods are also available for :class:`BitStream` objects. The initialiser is the same as for :class:`ConstBitStream`. A :class:`BitStream` is a mutable container of bits with methods and properties that allow it to be parsed as a stream of bits. There are no additional methods or properties in this class - see its base classes (:class:`Bits`, :class:`BitArray` and :class:`ConstBitStream`) for details. The ``pos`` will also used as a default for the :meth:`BitArray.overwrite` and :meth:`BitArray.insert` methods. The bit position is modified by methods that read bits, as described in :attr:`~ConstBitStream.pos`, but for the mutable ``BitStream`` it is also modified by other methods: * If a methods extends the bitstring (``+=``, ``append``) the ``pos`` will move to the end of the bitstring. * If a method otherwise changes the length of the bitstring (``prepend``, ``insert``, sometimes ``replace``) the ``pos`` becomes invalid and will be reset to ``0``. bitstring-bitstring-4.2.3/doc/bitstring_logo.png000066400000000000000000000754141462444752600220610ustar00rootroot00000000000000PNG  IHDR|>zTXtRaw profile type exifxڭiv#9s{}9|%jSO"E#ws[_].W// t7~_^w+F^_>Yt_`ؿ}E}_׶|>oa2.l$ܨ.\'xRH1K'!U,ZUkl .:8݃Ƙqgu٧ͱʫn;]w}'t)v3.P[nkwZpw-|t->ҁWxkkѱo!:4JdEAXnջwK\osNotΩuC׶fi UT?P-;ҷ61N`)B#rǓO$vW{[BBJhkjv>nur2TSRsS6wuyO]=5q :vZ[O۟ڙ4;xf`ۺ9!1oH$c,?74z:oKiW cMkt vM3xrg41Upjҗg,4Xd̳&**@qb9gվCa\w4pYJn'L ;vcQe5qzJZFdk07cmvW9 jqԼEKNSdOh+2]&„:z[qRsWmmp.8~oJ]ﮫ3Ga|lyڞBW(=^i::\`Hhmrk1wtcc cs2bА2ӈ\FީGu\9vl`y5UŦ `Y^mQ.p¤ksŶ uR 3v 6Tl^gCAI1X8iՓF‹rlml bmǂ2&Rس՞\i(2zt.P_+~c\m9Zq^\^dPCN l9l ӳƂTy2iJÞQE݋ھ.VyogY+;#*u zt\21TR԰C e4x%vA93e0ZQN-K5α2pt49oN |^UI[&N.s4%PLrgAI~fpEg8Eyb/Ƥܡg+eĮA=Nfݹ"P ܬL:' X8SfLJ[@iEM1L+<J3z>9GNͬ2$ځL)UȜ hh˜pJN}໲$xD4oɃGas7a3 2u:p4+·g mp F@1&DUR[m*g"*)ˉ5Ol2F! <-SׯXSU2 ^bp̌Bv@x`Sl@95B>&S )0)3RH)nz*'/[wY `ދ#|Z&!Wa2/2ؐC0fNF`XvYDP) B-FozG)`j=Q!xOaKR2[wlwK<5Yv+ZX|CCpY)s.c\2"u2lU{-V &-eƲAn %=KbLfF >3q儖lbTTo_2Mu*lK1Œ.6 |?#4F6S apN+q.1q%I6]χ驗m7?淈]FfXɢ]UeJlayQwrj2dե͸ J,nfClI /-x>UiA~  [!e+UfuZ 3f |h=ƷF%u(6(*RP( Vn1D T/sFP,gQDB~1Nj(SXC /ߘ<3/o!yCv5YI _/ZDgJy3pD!Y:U2\L "%,^QmRYl6߯1mP"n%D6H0F5=amjz 4W@l{CàGa&Nf޺?9I1˻DI):E$ )|m1'Q7oD JvB讠<ލ&b p CI4Pw_=z /^OPʖ+*{oڅxY M!=_MZicKdҬaOpq,*boҔӐ *\~yHd@FXg1j'Trdȉ ǷCBoF\ ܫy<ZHA ~CkeY^yǯ"]G8ȋ$x^$MFq.c%8枪7LnFVu !#p,ׁ@6dmJ]IJ [N-*aKTIr#k M wȄ&ÕAԍvb&59nƐK<BmQJ3*d\EKa= #0>EkyIe3dgtSV1`,i/ԩi@9L+jYD0]Ƌ҄gk6jqj^iHhA\}vD|A3][0#RC +V.b8BaPx{W$P]hgK{ƸgN|aLQ9 IK8f'kȵLY=Q!$m ݤe] ˇCd68a8VOn -zSLNXD}p[no_œ KC ]/R2QRP|ת NXV!P2^G#3_{J}beE>74/j)ehsS bȸnܤ|g !%#ǎnE!JPy}} ='Lϧ<TNnYM3MIÄ Ž1H`tXy59 Һ' 3na+=1Ua6%Pw͜D;MF)0YY$~w'-VLrǭwL*u<Ӯ_NʥP\GU tt9:_?G$*Tg'6?|RKHd{#X@gtup?GǻN .n0OypNYğH*񳆯 Sm0oA\'4|'$~;  ?"_0B{b{~O =1U^ \T:~  19ҭIɤ^C_|zWYeAyJ 3xP)8&6 My=JiD!hS^U&[uG8=+᧍ ?G=ʣZBجuu+:'ݺ珷ArdMkbs~no%ønrc}7K&W!S*t3 SyOb +\cˌq>ıߏos#` >iCCPICC profilex};H@ƿT:4CdA|U(BP+`}A$Qp-8X:8*W'E)IEYU4+4hmfRI!_¯!xLIẻb%X?GZ *ioOo}b^)K*9I$~dlf'J],wR65I⸪A/K&W 9PnĸI=/1 wVqu+5O-~D&;!+ir"~Fߔb@ߚ[{@JH}ۿgr iTXtXML:com.adobe.xmp Screenshot % pHYs%%IR$tIME*8 * IDATx}|U՝IvrDIDTd( :Ѷ8 V,&&4X{8Xm#L5(*`xOb}F$'ɹ$(" 9gzkf|i>ߵk}kH!x2˳sUW-#FD]J$`$# $I8 H FH0p@$`$# $I8 H fKk}d͛tc]̬rF\Q @c %-{I˟wvj?B&D92J'7:Cct86|qxh˱Y.5vukRj';DuDgu-ٙJmD c'7:߳Ox-iߵP)0CrKveDZtkHX {ru{2 8 k,Q 8'zEmk'm  ?D%DD9k}yH$tZs;Dq#iu̱H~ue5q@pTftY27z {o?R^&q! 3ivb?EَE Xu{^nT߿D PHLyӁ?s~=wW[$z՚ @Ppw։SuMbjñ ks @puF26_ Ϧ\[8I8;٨@mB*qn1Z-V$ޭt={3@ˌU H8t٦]Dk= 2k%yy@$COl0Hi@pmr7 S6?`H@H0:kHڋN% $X2wTH7l@giac-h&cE/LɈm k`#3p|t{MD~܋sD$CyavH'pF^X_p$R}SPORC?# P~jrHW{!)T H8RʹkOx-T@>zȴKZ֛ߑp;kSQԾjPJm[g!;?f~G݊kkMszZ yNvr-v ,B.xֿ Sk=IDŲ /u?PJJ+ޜ"" 7]7&"-J)/'}N_I8GV[59ߤ?a5w.qb &UGhQUw-u5$_QwDkLl쬍8| 3U](UKƷ*έAg&}ӂkrK][8Q"PJVtʷsYUTFD▤EDd` &rEOB}-DK)H~R$[,+W"q_yP?٢z2Dd 2^:XהR}Do\x'&""Ra`*"ْ.l^VȀ+.C}M$0N,I*+\"En,>?Giu/gwJ@lHlJ6"_dYbeΔpnQ't'퓓3[$34IOReHFʘnڤ+7vxY.oJ#La\òfHZ\ e}7kf9')%~3'KfTB%٧^Liq<tȤ#D}Ɠ]fSUk}q{~>γ _kh/zR)"-~rQ~w>>RIϹxr3iXw?ʹy\jzLӌQn %-gB /ldL(t͝K$=K YUz7k%o,>Ozj]sewJ&jEoηAU7;Ϋmg%ܦ[ @׹1hgH昈D8Iߋr#If3g^TaƉ,M"?|(Ȑ'w?(v&wM̂p3R%%}!Z%Ⴋ7ڧdD4Jq~̶.%I),:VIa bNk=I~؎:o*j}{2h~eÓGZ dߛVt=S%h!ygf}G<oW/0a,?`ܦɿvZ'N攦\2qkiuٛCPk%I,L#pI:KD|ӵKbIl烟jNk=EK^qKGj4d'*s$3"2I&][9w>J]:n`~.5}I{'Lؐ:q^o1")ٹM9mK5鞳yMq>rvShf'G'5~=^DukH!auPߠ78OZ28 3wgz1tϭ=约/%ޛUw~B+5nulwQ7Koǿ0?M-i :~긑םɥ۝謕u|HgORf\縑tڟk'*0i09;8O.k( ]yaSpRZZϦѷɸF^p槩$\tp U ':9k\B "oc+7oZ "Qk`77ñ~+JtK7kߣs#>Nkhܥ}烏;n%9x~Z|$7Nς DS]켇qDH>=w)8B?5Ǎ6p]/v$5M> 2ShSFJg/t| &C.x4q-Zwm6c{ot{^(mǿzc\k=hI`O.:n3wq#/b}d9= &2(#BAk}vius<Փ!yIqh|⸑iE._mKHQLJ6i1h8$`GZs_>?=14ip]K754נxH$lnʋɕ O$xHor9iptvndoDlY⸑^1(SH`$R]I/4v±O[tYxtiT& M<]ߠy|[%?8qС6GZ q#ZD#8q#}ϼam[ @Ǎ OP4zA!J+>>nF?,3Ҁe鵏)퉎q趭$F ơ61r#څD"H[:5>R_N3(D$VņM>X;`{7ƝO9y@iu3tyF{ճ`[I ttu78sD:ڻ+>H%T )=-0{V1'""=$4`Jg; agM$;,Ǎt}HĘ{NDH78s99%̹5nω>B'vD"dy;c }Ī5CٟD~V⛢0 dʆ'IkF^" suV7p>V}:xEg\Y}€Z~9$p,熲O>ba8P|-@wG|UI#HK躆紿;8= 6$q|S$]@$ѺE$ PH+QHr̻;9 Gzjsax|pfyP_ֳ(<ۢ3k] yiN켇DeWz]|C$?kgOq:Hbywo&G<}ՠ0vCZD%HR^R@3J8Cyqlm!Uw-uf:ymj ħṷ!y ZD_HgkD;g}sLSUw-eC`'+ZIDGn2{K]O_# g(׃2q:Qྴ N@5]n%A/$|.tn!{/G!8aaNpD={Y\t,*0 3'D{\?0 AMa9c_ľB/,  |jwG|+u |9:uhPfLKg lȠyC:ҺjzYD,BNOuĭʖ)xWv omo!gwNՉ8u|߉.9Ms^DD$-T>;P+=M} : Xk^(v){h$?:z#pi}8V}VDVJŎaHXV\M. LtͼA׬nƝthS>z@IDsG)MDW֛鶝L]O'}:Éή`4?A)eLޝ9>pLk)=fP $-K{~{t>~,[JZk=q#[.+n]ˤ/:CDNowhjhycVt`19%Gxħǎ}9 "m":CNς};eT38PB%Ň_x-L`i -5${"^+Dd3Tz#I=e3'˟izwvi/垺3Oz]`p˿~B#Ib9Vqm/'z՚kI|qgsR/f[=\v@s};aB 3P:])ɣRDdpB7_b>fF~si;Y#Y}Kζ_Qߧbp3uj1V+։:ꮥ^TU:燝qz.|~sVe+I^.|I:[+#zGHJ`}aBUXהNDB=t/> kVi3|fR*/" \>a?G`Jy^qZ,N{k]j_Az+>)z( g6[1 e~Muyߏ[Us'oE|rϾx+KƷJURzg':떠#}wdJBrhw}ƪ59sꍌPr2S7m5oG:C2)>j‘^6x SҐ 8q BxtZOeY.w&'DDүLsj%fl]TUj* -TU:'"_Ԅ [@$'-U՘n!yJ>UVcm5=Lj# Wqm5+5+m* V4WWha@ն'gժlIbSUa( IDATIoƵ'&g/WeWֈc̉vMU%!UZiYb$d0nՙzJ Xu|)7wVc*涃4|LSeKl1bIU$R:VjRJ˶՘i4ѫ\Gkko]N&u_-U<QK 00O!MU%!?HL{]W{*vf13vj8g3{k~vg۞n$Ic[U+|?VJ..&^=^k=V$^նȷf5YcBնו_Ⱦ8~_]}كO]|7L߷ \e62hxض3 J%YvNRM!$Rm5)^RJ5VcN$:HٛUQ7%#*C^R̬~3 ' gx**so6gLRI`W51eUuQ1~ݶsp)>᝷t7*l1sf~k"Z f|zq$8{K7Ruw}mxX}|'֛p-/O4(m‰]qj᝷2>CUTVJ59[x~!ׁ 5jP.R*vr֬ˊ~7HqИ2U&>V|KƷs=~1hw`үLp N = ?{ 8}[gGחDg\i  j);h޷=~ PHM} pT](r79pl w¤l@QHű\H {KU_(޷s4 uL^9yJF`I(]6p4}1;jgP#>7zۚJBMU%-H$~U[pzގ -lZ SVViJCgsyD"8H,V-겭3MR#  Q3dzHo. 2 +}opv_VJ;3'` xvqs ZOFV^(AKem\D}R*7//-dNgUezԣg[l/ Cu"9W?0B2Oj8ndX$)8e);yY/ՠ1Zin0I8!"H jG&>?NkX<_kkRqٟӳId6X a#/z::aZ6 EztXnj i عV{y Zjy] 7/_;E)H 7wz !yepVaAɨ3!p>stcEvo7d;S ч,q#;RLKɁ ?_!89Z5 &{Yն4`tgv+ QJyiIH뼾{^Ïy*B>qA@))b=i GןQw?J$$?~CDh˽,_kK-q⸑]֓`)QQJ U̠X 185:]`6$?5q/ *`.w1 ;]ӗ8nu7/0WiƮDAf.Bp(_Z}J>mWDHem"KT5rp#k]):ndGius\Zsֺ 2j[o[&.NH>A'mKV%ΥY[z:Ÿg܀z0p%%gOq:5=}mh8Y+7q#oUkZ3E A ɞMb(yz X giTcʯZ趭K;x|uv;$JmPlXJlp@zbgJPIWc%z;2$C>3C۝78ndkius\'̎|ʀ kMZNR՟C F%!Hik{U:Fm5L8s8uU5ܭ>T6)(%<1 Y$pMDc> FRjm+Dbq#/V7uZB'*$H<;0~gH[㸑V}o^Z{C@($s F|ː\zXv6Fy Rb#sZ'NuȎEmڅD@*tۛM@ҵs3x0aW[s uux8%Nς C 02=+9!O:B4I8M Se 1R/6U9= 6,mkTH8HUm{KO3Y!&Ha":8ndkY ae$}BO3Z?o R Şp@oS?6q#z]Kpn|$tX)u7Z8vDy ?穸:iYh<')LH2U$t;}8qHhcJgk $xkJ0!j,N#G,xZ{#WbnHO3Y<[T?I(PSUI6謕_ s !P9 B*qr`- 5UL4tMw='cYمs hDd 0I8?(xiye?5EUo*Lem$"xc|oFq׫:NJ̲HE`!ܳ Fskk?Nȅl,F1Rmzj_\钞{)a0I8?DH˚ae0[b 9qN*SV๴IU "As/tV>r !TUsnM8WUw-5`)Qx˟THK!Q0u|'Z%Wd[y Zk}@ckxBCp!0aTjEM=ܫ@6x?ukDg\>]y ݺVdr'eg-JH B`6K^`|^)d|kP]Jjunhwo(oCÑq0\{ԇB:-HEJ:ur&FjtzUED?9%l$ q74 ^Ä\AHm`/ rC l$|r3$|׳/SJׄ:qֺZqEǗ0ZH8>@Ⱦph L 8\EOGs37R3/8HĎ(|N#BU^ <'iu:mK֓1piN N(baIBe_! &gOq:up5PJuQo5T?:.w:#/2sW) Wp"0`(  .WEfuE}dQ/US;cg F I8ղ|7q5}W |+`4oն3MM|Z3&zK9%5!2vgEO %#c=@vQfO`~/Ljaԍ' >A_&.2 W޷} ;=Px!!^~`洣Gk!}q#/ zD]J$*Oܭwxy ep?yY|@H!1jr7 ?c / zr T257óc;Lx/'V#BNk5uO/"6j@Օ_8CعM P$ {_H6TD Ĩ(3 0Mpl_Yᓩ >/aoΌY\2rZ s5O|O{[8C/˿J4eޕ*0dLn቎q""DgW! CCX{yz O&(}!LɈzSp($& &wajcsٰꉄ?93nĝV;PJ5SH> 0x4-=' 0@o=F\GFMe s.+2难hI8Z{яSNoPm.I8A_{T2 rH${MtkZHBǍB9nd`aǍuXm;Q##-2"8Ҁ{'BF~j62Y 0q {e%aHϾĐTF(\#xY9ɿ7_ `UQ Dc4i9'zRc y{}&loyT3ĻM<4;wFkJIJ2=8bǕ!o~}Q~(G%`09DgW;Yf$pxYրWKF0R.|ɔq6MDN0aOspNցD0[LmS)(4Syg)V`[ H힔WB0FǛ_6r7'qpOC pD]*ٗաNpVJVR@7fKS_uRǍl=b5:]NFmʜG0hfӨm '$~.N 4`H;a Hĉh?Kx-4EE=?;햔op5OZOr܈q\ DxM.ZO _[{FvwV/ l[&!;k25&J.c"u!gcuƎeO[YN긑hq$\h8e:70K %\KǍl6үLADd|z6P* g5謕+&NZOqJ(;St{s.ZO5Q};Vщθq#{+brW8nuTZ[Ajqf͝Z"RjʰLt.qͼF;M49hxjw欗D$p=wi)m5QJmsFSݑZNgMPO:)wf{ J:m5Z*"qVKFeNzSHV¥ uTǍZ.'=^N,?O <(eo{M縑ͺn9bTwAݿ7vm5fF6;n}h{:_jK=q/xn,C[UfzoEژ{gnMiuscZ*"9 i"R _uJ>޴HqGmc{J.f<[Rl**NULV73I8ӹ]ovn1n20 c tu*_ƊcIjr o[Ei/]HD>9IYZ+C/.=r`ߑU–+nzd&|cܡ c]{暔5Ӭd"XVpm|$:n|;Y03]5ˮ%;gڡϽSl?pC6N<^Ξtz9KcȶJW}{oupw pʲZq/xh0~3GS"B9d +_,E9#M)?{9-li}ʯ? SBi wϼggm\bfD?JHP3wG}x\2Z== >?j/ĞpϬ9D£DsTEœD_HF$ #}{ sCbvwY*i=>6U*Ǵ4r>Gl|rY]ݵD‡S"BNnq> 83wǿFU\[k ZA͝@=g _KUB| 8g'sd!nU 'p~**a|i}$0rҲԚξS:6pow:+FI8qk* MɈ[ʮHxE|:Aa#Y2cxzGxƶт g7<;wyƥ3Hӵj&74߬KVI׍xX%AlFuv/hrذ7kI7Uv?ks`-Qk.GRa*:vߧOW" m9rw\I4u_O|fb0uxW'Ran:*-׹vwﷲ==5;!\ X4C=qu?6p^DžZU}JbݳuW%`i'R!zKNU˥_ՅC:ᦈWf➮gUc-AU- Ѳ?*xC\*ukwO\*D߾%Ç)盧_:^~d*33nq+l"mczE&Qk gGNQRth^{1.8O !x`~R4O&j-iskݯ&U$YTu~/;P =qHo]I\+~֩nC篊_?Nv;I\Zupddy،hJigOx띙 r#' G?0ISV E7¯j%g:%xyw6ռ_cq:baz{g&Jx08_WMUkqwgIrKaՏ~P6ͮ]G\ Z?psvavGdǻq6Ph ÓZwg}/ 5MY>3\0w3=mʷf'֌`naqmWƞI*W]󪳛P"Nq-{vgUN\floQ@X1ʼp',sT,}\\W9|ђfl8ޣLakCw.Yt#a C_JڪB(u @SDo+apݴoXf }'Ngnt6VNܒ.xp8*H3ym^f̻2=.~+ e&)9^x%&JWb"03?y '8Vm #ⲰO;| xyM߮>ECsC\nv!wP XwhyGßy\ӔzVU <PV2*ټVl7aկ |!pU9|U!l0B8i߽O'ȠnɆ~ J=|bӷ[-I`&@"cp$VlwQۀ⏓ k-ۊͮh_V/@Ƭ7ڊZn̄Ȑ$IB2ّ^kvgş!@ikv?Qx #/=rض&L8hU 2 gZhUg`bpP9׵lv&ƻ)wP RYn̄H״2:<,L Vm͂fԪ޽B8J< 8}˝a:!@$\WVWQ'p)$ɥiࢎ:G I|Rܟ}1 nr IEhXyયê|fPO)ft2-$I[92 1̄hG61}KƐl4I$K*Ҹo l\-+jh$I?ujbjkUTHd|RK`QWpLdijs(0%I ^LkuT:%IB+B-{x 9$#IeawUqE;o{6< 7NX1 ej+zD|.R晦SB-O0JeK~i+-2`6mO-J-g˟b'>li'R1<[^:҇zcFO7z-zu7ٖͭ[y|}I~iF [u;`LigyYl/'V^eVnJ;.N8O9߫S_~y[.,U㣥 NjF|tT4Wlu1zҩfs!yL*rt{~TU[J7*gT/*k~S%6[!jwZuED= 9-~g.Z;3߿d?6D(A+D1 չW$2mHG6U]mɉwg !;Eïwo{߈F3v\`Vk?@e0|1yktfkϩp.=cZ=Zڱ9V{RsNzxiIO |蘠mè۔C z9a /?r0|Raκt^>aᲜH:$[\)=sgS1kǘ-^'.t 7S3*j" "R!i[t)4vK)xx7(dg8ZKi*AY``obLulҝױ@O1#_S6`  %oPEv8UO&]y-YL.VN ix^H6B>Zʳy ~&"\9f(a #Ar#&/ ڶP!5ӗ₪#K{vZcރ(@V׉^N"K@5N324{v롣{#{(!*ɉۻ[_, /F?c""e9 k__Cew0n?mgZ*2,khQ.KH 0ޝlR(f2褌/}nU/ A]F )c'3Əz F5)EzO:q$ ʲ90 r ݁?48j"}U4fw3M @AY68@5BZͅԄ|p $Pdq@tV I&ցOKBR2v"m_P@q#]mDLcF+&qQ̖jY'`Ƥ=7nkb,%>z/jc4p@JAB4R 7D6=ɑ+Ƞ\ٝ\Yl2|ů@Ms)]Nꌡ釹К #Z*{sPish?U%>Y@]p[m&z/>=iy7)^Zzy$;1[ǎ 3=R Lb=T*_l>]L |;&xSSu r*09d54V9yӥcbqP?|FrlM .xporeDDş#rdžUfba8Aj*"\IKe\>GK?DZbYBy1H)|]C>QS!C!H# R`XDۛNPg+>D{$53r11 t5yf/N75JV!) E=.rT|u=ߙ>BKf {-hя5GQ[,<ͫiS1b6~'-T: #ģZ :HP slg^ gs(@\p!ca4ʋa%4U^Cv\YxlHh<)nqUY 8$gpUyanjCwe欭 >cNC<&cvZcpdCϧ^..S(+ +-Xlڏ7vE1y#6܈@\b-MۑQ!vK!Ch:It5(9́RʩiFRPfrpجL!L06RSR<9!cɆZdjS}$ !m9J+^$śz$]{2Alt]#t '6 E2?D@ UŘӰ녩M BWR֑j'kVǮALAH3\}P7&ݎDҁme}|4KޖLfF Ʋ]lw lqUc eQB4u:T("Fjჸ "5taQ=u%4tƳz 1ijl6`?oq:h-?Lk樨+Df9T"ٖ6hAHjB?z 㷡 ҏʝN 1z08\G5z$`qҁǭ=Rj4:F 2Z D7ʓ}H"D@PaLHnvIIeKiNUFjFtS3 yDПGDNج'j "j7K:h)u. /݁R+ZÔkAF>E ړPY@[4t]b2s2~L&u3[_hD6r&#V'r(dsEaSF2:6ȣ;r|1Gr&_&D Z[33ـFئKTW{+Egy YM (ĀCXmB 5|wmc|;MMKPO_U>t܊]. eb+^=c$g8Q<`XRܶ+0|dhrCEN4K \6UUG5TxÒ{wٺKhm-0<-3vITf#GzuPMF)NtW0uǒB#2,}/Qfh8Xx;[2:|N Nj_)ߟ/ "m\<[k{;L̺0r?“Wtdp.ATo5{کv^ r./<0CbjLAD#+!8z6[I\ >'HatxK1G)axށZ-L4.H4K k΅0ya 4X'( b" tO֑4-8JŅ+&<M֒efEX,Rm ]1:( 'ahMӀ6z;Ⲅ-q > MH /.}x )GQ`- 枑үfaEn L5RG_G<1J)rQ7VqL鸌L:a]T0mϚ6O"6 K6uHty"DQigJV%雂H`2xGDRo ? }1}'𞸔W=ۜ˴UgDz?e]0S2brGH7a{=0FZQқ#6&xRĮ_s(]mمf9Ay6p-aȸx֧9byU{UynǪN%#^_!yA&Df!qtL ӑ,UVC$:Yǐ']P*:A`O:׎1jA͇/ư+Is@ho^JꇀW~GL:1`LC (ye tTKRܣC5D_BNIjA/K&W 9PnĸI=/1 wVqu+5O-~D&;!+ir"~Fߔb@ߚ[{@JH}ۿgr iTXtXML:com.adobe.xmp Screenshot 71 pHYs%%IR$tIME+($3 IDATxw|EgfwJޤĂ} (HK^$!(gG@EzR;3?,/"."n}fggvB!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!BBS4tT.">c6 (^=zÁB :.Ae<! !&BʰȏFf0sQ[ S߫ctPUCo 19r2oܸq 0oLʥ.8(!H:|ں %#/ ) W7ǎZGeKx;>*g}P(uߓǟtp]o` W^7ޔLLb@<yd- _-*Ln"d1O_DFg *Wi]Y2g}RFgomo:0jȟ6hU$vKsQC@@3myM@|d!9G΀Hޖ/r9(h'tSXy[|rM!9/f*O5U N"L f"|8ыn1]0r_\B꺨b&k*%^O]-9/ImycS]ƌpa7ӇCႈ}+y5{Μ)d)WrDZuqc w)-6u3a#=MntpҦ_Up]uZn!%OG1٪vkvHww, mژ΁[A^UFWo"Y߸OTF^.,3Ɗ7W΢ JjSjZG+8Fv-Pc=8-N;PmǕa"핡c@ЗvG5.dGΩ,Y)1 в{_zg]ݥgC Q#Kl %,@;`D@İQ1?k#`4B! !&B@BaA!0 B(Ґr՗8 .0e~++J T8 ̄Zڮ]ura}B p0xi:Sf,j_8!4U'_J~Q+dB݂*3kmI;$[M1?0vxP4DFg!T&z}\D5+7[-7& v4w%Q^$!Dѳ]fȔ\^o平؇( ER$^WXE ~-dm/v~so)ʸDg#jYJrOox<$;TYZ Z1>|r`o헱sӛ*' Q$֧/LJW+0qF[HXaG֦o дղ.GTϕp|ڞ֦~;kg"`e_^E$&LnUc/cge%6ŻIY)"5B~K5ʛU j4?Jf-V.BF br(^Cyez½>Kש: 9P#$9!m{w~KAۅo(Q q2bR[ȳ֩MZgU0gDeeVB& N$(G$&o˾y8nx)۪1B<p+b8q RyDD.v0<\@BvKQucL~XWz /x $M  VrsRV.yJZW5`:&F]Ĕ͏ᦁdnԚrczUYгFݪiodÜܖ g\+o&<}y F ~{'9ElG*<.YnNm|L֪UWo7q]CPڝd?Q#?)'G{r6:X}ynP$^Ց3&.<%ɴK GE􈥕I5Bs9s/'Fbe.{9n>)ME[[%yP%|--u C>su9nV37 \g[crGGqv{1V4N1rx90 94`L֪o5uʻUB[XL޴yef&{ҷ?/pÓ o_&&+x{vm38 ,ԵƀD_?-ӂwFʉbu\9*sUV!2B%rݨ3sj|nRdg<\0i^>ցt?aWM/׸2(L+ |}b\$>$O[ 2+vG[oA GƷLߚl/Vf@n mBWVXHEVo5Kw.Tz|F=RC pSqH_/wXXwsCW)nMB3?=C9x@.EKz|@{\} Ռxa]~@~cꊒLPut4[sY+ uM⋁ k`oY4^2ZӅBZUYRs@T2!w=ύC=='APחu"anYʉB:t|(c/Rn]]du @0ZY C('}dו sYC5CM2ە&L== ݼ}p0smUuL"X(mp&;PqeԽJM0W0D!b$EMY}'r.+4w^O ;1Rc)Oa R2'i|n-f)w=szx"pZ ˶%F[ [%M8r@y܉_{lEgLOo<y 7%"K1*0*cE !})ߙPо?lF~0ڼ%= ;d|_QU:euje:g}۟;m zk}֌vۮE&.l:b;u "?-E:+34p7ȻMi/kf9wE tY_ }pF&R;/IAORa}34ij|3}nͶ2y>_fkR/Lqw^?2-qv!N(9^|4cC8Kd^ q*7YJAaum]Kb~ t"nijio+r!#ZGZ_ؘrKr,P a^u rgEv! ÈvϛVvRMdGv7RmZqv5φQ7u u.Sr)k+q?3.kdО^ń#k%udmgw'>hkIKHz &MlP/JF. 7e W?vً\/[^kKq#@LV!߰O' < @k$kc_/n ⓡsF7x=fOqx#nB*Ͽ9ӔG:7DD8el՟s0pB~AkcH*yo}{/d.7Wo $r\A.y];vf0^$0W,Iu8hMY3b7ԗ=)r,zل:bļN5&>(;q1/`Mê\3gGt )cƌ侌j21\B&@|b sآPn"n-w6=4m'3~e2Fl1ԵZW`['ge*r7 M&$@0δ[#0Yo&P뛔0J oY/qBfxS-nrۦkCJ&1͒ȹV{ *IErk‰A{2)1agnu7ܭ2~rV^@g =GTӳ]g!ؖ6n.9Qth@gSχ ?֒tj{qkXR$1 Խ-1cRM@/;/芴.29Ϫ&GWԾfnRk-ZW{]z6Hb$E#S1/Uc`P7Aqr>붇a-O^KƏӓD+rBM{]j[ny8'X?T 3#!='f/ 赔S`QP$|]j71{il&Q[rSɷVu[~аH z` ;@O.4qum]c־@!a[j}27dR On9x*SOxk4N@\鍑CP'̄y@ `:DCfƯuYixϹͼَM//"^ ,2ZDˆB7em\~o6M.<%FA@.rf O{=)-`ޔ#>[G`kRpʉ"1 dz:EX*ڛCeܬZN)NcgiW'L:㍬g{6N=5wD`F{3j1ΚO;;/.O9c@0>"W!^nm{0S|un~e>I_ff9@Sݜ}ѝŝWĄpx*ITޙSU6N_WK <7{y5v.$,E?7N%?-)~Ms3&h@׬R{n$;D@]D-pxȤ!`CP=,Ƿ&`wI :=j5%y=%wȭ ڊ:|]#R'R;.lg @&`(k+='QwsO>~@s]8ueR35TBNj-h4F0qg0 oHkfe(aKMF)>xW&SK2 ejg 3NL`oɥwoNk⓮FDϒ )z/;͌z .@PQ5K[J0{KjrV< B'>>@@>a[o~eW5ݞND#W%]Ne>K?tmjlO[^!ߨ8n>LqZ)vӡnaO.TH>aô'w6i̎fD#F"XEe tjboubA'8U;e~Le<;tr.y2cRXn?6e@r 5˷J6RIQdajo?p|)]}VP 'Lz$cӼ3`Dk\9C zoUjS0a|I{4atP+0c̄$ >ni.5yIc0'>`6NA2A3a{$UB.s> L/jMY$/_^IQ'YnpNvobϡjW j=>D4!&%QRfd9w㻝0s݆^HS'H9 ',&XF"8Ai kQpar/wY"Q,jժ?H [.D]"OB!q \bv+[H/KS:_V[FfnS @yn nc| =yIc*Z:mC+OrK\}Ek)(dXk<ǔL\=ڝ8 Ω{^B$q.SCv3Rz)'oر h#o'˜7G9 =zl6ݟyiBimzs!S2?`u실.s;( >f,LoM~BW,B/&`< %PȔu@BEd$Cxn[L !TM ${N``Aj_#b{0 P5pwV4S9p[pb*& vO:OV,_3 s1 PK\lެ6!VJ riV g#PK|aMy anHmwoOCB- ϖ\û]8BA`{+-[ gƀ-ǟXI{~O ;tx< !{[=w@+oT-⮝/%֫2dk4w OXwBU/G~^1M[.ғi<BՔ-pv%U~L !T ufWSڔT6CtFB;[j,Z1mBa7'L:`&L !"(wq2q]t0m& ( Z]Krfuv=jpn-X-BU>+7 %Gtsnwo2k!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!P+iMVdtIENDB`bitstring-bitstring-4.2.3/doc/bitstring_logo_small_white.png000066400000000000000000000336541462444752600244510ustar00rootroot00000000000000PNG  IHDRHF.zTXtRaw profile type exifxڭiv8sq77`I-mV$dFFDe}̿j&Rs'|Mϟ~;gs?~nGoλa:_ƏZ|_? QЌFr;Ab\#SS؁b;qCq˪*ľ[d1V3n[J0_"d{^?pm-e#$Ώzߣ9~sN6xlj{s") NϽbۥrt`[;te uh kiNs Q@[mсv$w=E)ugL0M>Éx|g22@?'1k$lGGO(Sp^NĻ)xHSbBSOsBC;D*S69xz_hr Ry2g-j SC`!G)̒#M?==BCW/u#<H"cIvsTvJ#1%m 72XC2 ɳ[3ycEò/] *sYv:Xljƌlw@sUQ5]i?لfo6VHH'۔ER +gFO)nţyPq|uh [ 42+UK\GTjnC#u @Ɯ 4 XDc<(! '!L!sbPfft N,{c);7W e808@G|W.D/*|:;wJ}Z-2C.YGݓ!:sa܅|Rh2phμmI$z~#(-pꆓ&U@˜}$nH&F $D>xV&C楦;ӂR"rjEl$4dr3)v_[E)n1Eg%˜20rLL&J*EtTg-bR M HP?dkp9a밀v0T0H.&; -. W>zTs>x 1 \nFL|}X,z[EwS~`\W::1Z isz/ [yP#gpl6k cNa!2[*SWBL2J (uaH,ögEO AaDե#q@ʽ"qzUwJUX5E""L*PٶZ=ϵ*^䀶!cɁDP HYf<:%0i0[{G(McS툎U$V1R71Oe "[ M%iA ;(E [Zep-6BFk*{!"_ϒ6Lm.6 8l~F3&3&9gGrzH<xhBnPivi޲PK2BQ%ؐ VFIn]FEǣmDˍ"Z,Ɂ!3+K+:؈fJօ흨~P6:pSԶp?:>jc12CY 5:PL+ |ZNcJe"M#~A\>v].cPv~d jM|1)N;'9S&C1:F#IP-E=8 J.9إuAHz"aX`95LĠ@7oub4sWHuL<жFfa϶CQh6U [W3I.>)1LmkCB)q eZ0KF'ia sIMy(CRj O;-5ѣ tRD4Ω8IM l$pA`f!,`TBs0 T{( 5-X.`V"#SE6_֋K񝮯#yQ#1Q25+fm[FS//~PմrfK9 D@:P.G W^(ndv;F(Ċ]MҺ"ITx*dXo\2iKUn|i?dVb[Q-:j?x M1 u]m;@e)%~:`t"U ;Cy $kKwFd2B/3"zTo!ni辸 8:DyY_:xJ̊`hm[삙q/Ƚ`&)-2nPg e/hV) pA[wXLVKMK\AґI278lB7kS`7CPKy@qt\/vA;7#"ig^'5='z.A3'Tϋ4, ]g=/)$3(~**%Dm6!U;MPGXy e/53f1do3#0ׁ7KK&SnfP5>KGؑ47_dpܿ̃U0CW YܚWD3DtF ةu%M\)?@`*x\~ Bz6 p0.b$wmX'/,-qTxMZ,#, CIPpz|؂H=L0%פfyBPmIADm#Khj6Q]ѴMC ݃@q`7RpiX=تn  N~3íz5Km,Nl*8YR).5T~GC[k8R2uMe 1i?P} -z9竊w7 &( KȌ̄T sM1L ^0IEWLQ=(Qwc2NzEa_sN] jkI^ ?Pٔ9WU֎d5>ձpcpet3f=BW4miKv6q@z:?()f2#A;KT/nQ4&SBsB<>HDbf ]aP%ZU  .OcĹnAe:ؘݥUeG qӸ5@-ɧm`*Zq&i ~WOf?$.XyӃZOt>oZ>j_Z&YR,VX 3./FlX}Lwhr3HoxO7330fh+k#PruOЮn>8%8]rn&/iώA(!pѿ_Ul~+-$:nH!(bEH;b/%(_70-4z5B5B5B5B5B5B5B5B'(D0BB:J4,EgMmįYFT)=6 10]#P#d,^p&3B2@<@"D{vAKfS K: 'I_h|'Hc;V/l^p˳cɆ3OA6txYmmfKD_lb*oe&N_FĊ S[F!$h5L.'t6W-Y{]N7Ycm#a4 gu@KP=ac,o8fu& 3ǺNqkF6m̲a]]* [cҽwbrFE7$g5_g{r^giCCPICC profilex}=H@_SKE*vuPZU(BP+`r4iHR\ׂUg]\AIEJ_Zhq?{ܽFiV鶙N&lnU "E\f1'I)x{zYjbO$eio7m>qd8jsegLz8L,XbV25i∪/d[rUj}OP^_Y:Q$%H2*U'Ba/K!W BwVajJ@8ǎ<W'9뎦;Г!+i ~FߔoVo}>*uE^xwowoi\rվ3piTXtXML:com.adobe.xmp Screenshot ;BKbKGD4Ѱ- pHYs%%IR$tIME / IDATxy]Eg @&B2.5 %n⮈(Ѳ@@TQԀ;DTv,$HdO1B$ If|gΝ~o{󖙤w߽}O> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$XH?$ʄm^A$32>cfI [Ɨyb`v+H ɀ}if.H3I`'ICqVI'KzTR^I%zlY/&I[\풾$i$ IzXC7ISRϔ,&I?ȱCҫ>X;n,"'/L>&ț"3vgL ٦.eJ0vW{sUoyn>pK7>k Q/t%κ\O0FW3y` p(7 3irH`0kq+z 嵥V3DL%͗) Ű33ʨ/7wR%ݓ>IF|K%=5IZ#BI{gEҽGkD{VKKI_.RrI;f9H\9 }例r_."IȦ_ݒNp%.wr}tq5 ]%56b ,`f%{Yc^ \<ĶM> *t3}mIz4wI%>Me+wsS$]\jp')-Odj~WЏJfRj8;ޔ7ֹ=W/u87 r_4=;mPٗ}~OYBOm73u3>8a吏u> |m4 %!Cmz|e; Pfv g]u.,zo+5Bhv[2!/d0YXV έ^Owv5gʵ=wmo W:ޖ,V!ZgKIEtQNIGkS~ir WA|lY$9,RWs%M+ tS|ՅGF3[Yp6{Bҗǁ3w/ٿN$f COiq" \)zC6ii8 N&UD/a:̬C+= 7(h3*pO۫J_L9&S7ulTgLS3Bu-l@N]uw1b !"_(byX4nWʹf$94F,p۰ԙ~;QFٮ/Β4#vKqd4%ܷW,&^L%yE'm3XZ|}Ά+S,s`9xKI'J:DN:i^J %0K@X)x W5C*ܵ 7 D'oJ:[jHԭ6EVBdEhQ  "Tݗ 5bYK/\hLnSNѳ>ܶ.wX!}QT؋7dP dot3PG/~|ևXԢXOX:"TlFJLJpD̼"L><-F}Xc $5WpnjX#XA28(w+J~ T>ۺY4b(xkC-~P} edgJ wTff¡ygsv2ƊȄWAYR#$#W-jˀ529e4"׶ao>Pֈ~8$l~޿f33 .P23}( \Yy7٤S/N]%{7@-~J ́^a}S Rw"utY/{+"JXoM>xD`?:c$mHi{pLY|V-X>^@gVoۤ?fh'Jaw;r f8?R~SM-۰5 E^Oyl4Sr!;J۽3; j|Хg(~Wbdiy 3c~cyJr'aRf: S #㻃vઙXnG~`-nn4G`W.s{Q4%ZzY B=[ E B&Aif%~\1!7ƻxpw)!3tώoε;wQL>ȕlp0KW-k!<Y Il4p9%R}Xec& ˻3sѳxȩYwɚ0}lyoȚ;MOi晽ޛ}:ݦmHd_̜:|WB֠@GHڜۓܳټ [+^3#ܖNH&iYnh4;I'a[W)~R%m$*YʗτJ-+̻t,#+#Dуz|&smIаwV\8fX+!5PNv "'@O B\D >@/)'0-yVJm򄑡`߉:3RIHjEUL8#_T-2EFSM rs$KsekY& #f.^<\o@>3sF]IENDB`bitstring-bitstring-4.2.3/doc/conf.py000066400000000000000000000017001462444752600176100ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # import os import time import datetime year = datetime.datetime.utcfromtimestamp( int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) ).year project = 'bitstring' copyright = f'2006 - {year}, Scott Griffiths' author = 'Scott Griffiths' release = '4.2' extensions = [] templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] root_doc = 'index' add_function_parentheses = False add_module_names = False html_show_sphinx = False html_static_path = ['_static'] html_css_files = ["custom.css"] html_theme = 'piccolo_theme' html_theme_options = { "banner_text": "New requirement of Python 3.8 or later - see release notes for full details.", "banner_hiding": "permanent", "show_theme_credit": False, "globaltoc_maxdepth": 2, "source_url": 'https://github.com/scott-griffiths/bitstring/', } html_logo = './bitstring_logo_small_white.png' bitstring-bitstring-4.2.3/doc/constbitstream.rst000066400000000000000000000235611462444752600221150ustar00rootroot00000000000000.. currentmodule:: bitstring ConstBitStream ============== .. class:: ConstBitStream(auto: BitsType | int | None, /, length: int | None = None, offset: int | None = None, pos: int = 0, **kwargs) The :class:`Bits` class is the base class for :class:`ConstBitStream` and so all of its methods are also available for :class:`ConstBitStream` objects. The initialiser is the same as for :class:`Bits` except that an initial bit position :attr:`pos` can be given (defaults to 0). A :class:`ConstBitStream` is a :class:`Bits` with added methods and properties that allow it to be parsed as a stream of bits. ---- Reading and parsing --------------------- The :class:`BitStream` and :class:`ConstBitStream` classes contain number of methods for reading the bitstring as if it were a file or stream. Depending on how it was constructed the bitstream might actually be contained in a file rather than stored in memory, but these methods work for either case. In order to behave like a file or stream, every bitstream has a property :attr:`~ConstBitStream.pos` which is the current position from which reads occur. :attr:`~ConstBitStream.pos` can range from zero (its default value on construction) to the length of the bitstream, a position from which all reads will fail as it is past the last bit. Note that the :attr:`~ConstBitStream.pos` property isn't considered a part of the bitstream's identity; this allows it to vary for immutable :class:`ConstBitStream` objects and means that it doesn't affect equality or hash values. The property :attr:`~ConstBitStream.bytepos` is also available, and is useful if you are only dealing with byte data and don't want to always have to divide the bit position by eight. Note that if you try to use :attr:`~ConstBitStream.bytepos` and the bitstring isn't byte aligned (i.e. :attr:`~ConstBitStream.pos` isn't a multiple of 8) then a :exc:`ByteAlignError` exception will be raised. Reading using format strings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The :meth:`~ConstBitStream.read` / :meth:`~ConstBitStream.readlist` methods can also take a format string similar to that used in the auto initialiser. Only one token should be provided to :meth:`~ConstBitStream.read` and a single value is returned. To read multiple tokens use :meth:`~ConstBitStream.readlist`, which unsurprisingly returns a list. The format string consists of comma separated tokens that describe how to interpret the next bits in the bitstring. The tokens are given in :ref:`format_tokens`. For example we can read and interpret three quantities from a bitstream with:: start_code = s.read('hex32') width = s.read('uint12') height = s.read('uint12') and we also could have combined the three reads as:: start_code, width, height = s.readlist('hex32, 2*uint12') where here we are also using a multiplier to combine the format of the second and third tokens. You are allowed to use one 'stretchy' token in a :meth:`~ConstBitStream.readlist`. This is a token without a length specified, which will stretch to fill encompass as many bits as possible. This is often useful when you just want to assign something to 'the rest' of the bitstring:: a, b, everything_else = s.readlist('intle16, intle24, bits') In this example the ``bits`` token will consist of everything left after the first two tokens are read, and could be empty. It is an error to use more than one stretchy token, or to use a ``ue``, ``se``, ``uie`` or ``se`` token after a stretchy token (the reason you can't use exponential-Golomb codes after a stretchy token is that the codes can only be read forwards; that is you can't ask "if this code ends here, where did it begin?" as there could be many possible answers). The ``pad`` token is a special case in that it just causes bits to be skipped over without anything being returned. This can be useful for example if parts of a binary format are uninteresting:: a, b = s.readlist('pad12, uint4, pad4, uint8') Peeking ^^^^^^^^ In addition to the read methods there are matching peek methods. These are identical to the read except that they do not advance the position in the bitstring to after the read elements. :: s = ConstBitStream('0x4732aa34') if s.peek(8) == '0x47': t = s.read(16) # t is first 2 bytes '0x4732' else: s.find('0x47') ---- Methods ------- .. method:: ConstBitStream.bytealign() -> int Aligns to the start of the next byte (so that :attr:`pos` is a multiple of 8) and returns the number of bits skipped. If the current position is already byte aligned then it is unchanged. :: >>> s = ConstBitStream('0xabcdef') >>> s.pos += 3 >>> s.bytealign() 5 >>> s.pos 8 .. method:: ConstBitStream.peek(fmt: str | int) -> int | float | str | Bits | bool | bytes | None Reads from the current bit position :attr:`pos` in the bitstring according to the *fmt* string or integer and returns the result. The bit position is unchanged. For information on the format string see the entry for the :meth:`read` method. :: >>> s = ConstBitStream('0x123456') >>> s.peek(16) ConstBitStream('0x1234') >>> s.peek('hex8') '12' .. method:: ConstBitStream.peeklist(fmt: str | list[str | int], **kwargs) -> list[int | float | str | Bits | bool | bytes | None] Reads from current bit position :attr:`pos` in the bitstring according to the *fmt* string or iterable and returns a list of results. A dictionary or keyword arguments can also be provided. These will replace length identifiers in the format string. The position is not advanced to after the read items. See the entries for :meth:`read` and :meth:`readlist` for more information. .. method:: ConstBitStream.read(fmt: str | int) -> int | float | str | Bits | bool | bytes | None Reads from current bit position :attr:`pos` in the bitstring according the format string and returns a single result. If not enough bits are available then a :exc:`ReadError` is raised. *fmt* is either a token string that describes how to interpret the next bits in the bitstring or an integer. If it's an integer then that number of bits will be read, and returned as a new bitstring. A full list of the tokens is given in :ref:`format_tokens`. For example:: >>> s = ConstBitStream('0x23ef55302') >>> s.read('hex12') '23e' >>> s.read('bin4') '1111' >>> s.read('u5') 10 >>> s.read('bits4') ConstBitStream('0xa') The :meth:`~ConstBitStream.read` method is useful for reading exponential-Golomb codes. :: >>> s = ConstBitStream('se=-9, ue=4') >>> s.read('se') -9 >>> s.read('ue') 4 The ``pad`` token is not very useful when used in :meth:`~ConstBitStream.read` as it just skips a number of bits and returns ``None``. However when used within :meth:`~ConstBitStream.readlist` or :meth:`~Bits.unpack` it allows unimportant part of the bitstring to be simply ignored. .. method:: ConstBitStream.readlist(fmt: str | list[str | int], **kwargs) -> list[int | float | str | Bits | bool | bytes | None] Reads from current bit position :attr:`pos` in the bitstring according to the *fmt* string or iterable and returns a list of results. If not enough bits are available then a :exc:`ReadError` is raised. A dictionary or keyword arguments can also be provided. These will replace length identifiers in the format string. The position is advanced to after the read items. See :ref:`format_tokens` for information on the format strings. For multiple items you can separate using commas or given multiple parameters:: >>> s = ConstBitStream('0x43fe01ff21') >>> s.readlist('hex8, uint6') ['43', 63] >>> s.readlist(['bin3', 'intle16']) ['100', -509] >>> s.pos = 0 >>> s.readlist('hex:b, uint:d', b=8, d=6) ['43', 63] .. method:: ConstBitStream.readto(bs: BitsType, bytealigned: bool | None = None) -> ConstBitStream Reads up to and including the next occurrence of the bitstring *bs* and returns the results. If *bytealigned* is `True` it will look for the bitstring starting only at whole-byte positions. Raises a :exc:`ReadError` if *bs* is not found, and :exc:`ValueError` if *bs* is empty. >>> s = ConstBitStream('0x47000102034704050647') >>> s.readto('0x47', bytealigned=True) ConstBitStream('0x47') >>> s.readto('0x47', bytealigned=True) ConstBitStream('0x0001020347') >>> s.readto('0x47', bytealigned=True) ConstBitStream('0x04050647') ---- Properties ---------- The ``ConstBitStream`` and ``BitStream`` classes have the concept of a current bit position. This position will be set to zero by default on construction, and will be modified by many of the methods described above as the stream is being read. Using :meth:`~Bits.find` or :meth:`~Bits.rfind` will move ``pos`` to the start of the substring if it is found. Note that the ``pos`` property isn’t considered a part of the bitstring's identity; this allows it to vary for immutable ``ConstBitStream`` objects and means that it doesn't affect equality or hash values. It also will be reset to zero if a bitstring is copied. .. attribute:: ConstBitStream.bytepos :type: int Property for setting and getting the current byte position in the bitstring. The value of ``pos`` will always be ``bytepos * 8`` as the two values are not independent. When used as a getter will raise a :exc:`ByteAlignError` if the current position in not byte aligned. .. attribute:: ConstBitStream.pos :type: int .. attribute:: ConstBitStream.bitpos :type: int Read and write property for setting and getting the current bit position in the bitstring. Can be set to any value from ``0`` to ``len(s)``. The :attr:`pos` and :attr:`bitpos` properties are exactly equivalent - you can use whichever you prefer. :: if s.pos < 100: s.pos += 10 bitstring-bitstring-4.2.3/doc/dtypes.rst000066400000000000000000000066011462444752600203600ustar00rootroot00000000000000.. currentmodule:: bitstring Dtypes ====== A data type (or 'dtype') concept is used in the bitstring module to encapsulate how to create, parse and present different bit interpretations. The properties described above are all examples of dtypes. .. class:: Dtype(token: str | Dtype, /, length: int | None = None, scale: int | float | None = None) Dtypes are immutable and cannot be changed after creation. The first parameter is a format token string that can optionally include a length. For example ``'ue'``, ``'int'`` or ``'float16'``. If the first parameter doesn't include a length and one is appropriate, the `length` parameter can be used to specify the length of the dtype. The `scale` parameter can be used to specify a multiplicative scaling factor for the interpretation of the data. This is primarily intended for use with floating point formats of 8 bits or less, but can be used on other types. In most situations the token string can be used instead of `Dtype` object when it is needed, and the `Dtype` will be constructed automatically, which is why the `Dtype` object is rarely used directly in this documentation. It can however be advantageous to to create `Dtype` objects directly for efficiency reasons, or for using dtypes programmatically. If you need to use the `scale` parameter then there is no way to specify this in the format token string, so you must directly use a `Dtype` object. ---- Methods ------- .. method:: Dtype.build(value: Any, /) -> Bits Create a bitstring from a value. The *value* parameter should be of a type appropriate to the dtype. >>> d = Dtype('u10') >>> d.build(85) # Equivalent to: Bits(u10=85) Bits('0b0001010101') .. method:: Dtype.parse(b: BitsType, /) -> Any Parse a bitstring to find its value. The *b* parameter should be a bitstring of the appropriate length, or an object that can be converted to a bitstring. >>> d = Dtype('u10') >>> d.parse('0b0001010101') # Equivalent to: Bits('0b0001010101').u10 85 ---- Properties ---------- All properties are read-only. .. attribute:: Dtype.bitlength :type: int | None The number of bits needed to represent a single instance of the data type. Will be set to ``None`` for variable length dtypes. .. attribute:: Dtype.bits_per_item :type: int The number of bits for each unit of length. Usually 1, but equals 8 for `bytes` type. .. attribute:: Dtype.get_fn :type: Callable A function to get the value of the data type. .. attribute:: Dtype.is_signed :type: bool If True then the data type represents a signed quantity. .. attribute:: Dtype.length :type: int | None The length of the data type in units of `bits_per_item`. Will be set to ``None`` for variable length dtypes. .. attribute:: Dtype.name :type: str A string giving the name of the data type. .. attribute:: Dtype.read_fn :type: Callable A function to read the value of the data type. .. attribute:: Dtype.return_type :type: type The type of the value returned by the `parse` method, such as ``int``, ``float`` or ``str``. .. attribute:: Dtype.scale :type: int | float | None The multiplicative scale applied when interpreting the data. .. attribute:: Dtype.set_fn :type: Callable A function to set the value of the data type. .. attribute:: Dtype.variable_length :type: bool If True then the length of the data type depends on the data being interpreted, and must not be specified.bitstring-bitstring-4.2.3/doc/exotic_floats.rst000066400000000000000000000347221462444752600217200ustar00rootroot00000000000000.. currentmodule:: bitstring .. _Exotic floats: Exotic Floating Point Formats ============================= Python floats are typically 64 bits long, but 32 and 16 bit sizes are also supported through the ``struct`` module. These are the well-known IEEE formats. Recently, lower precision floating points have become more widely used, largely driven by the requirements of machine learning algorithms and hardware. As well as the 'half' precision 16 bit standard, a truncated version of the 32 bit standard called 'bfloat16' is used which has the range of a 32-bit float but less precision. The #bits value in the tables below show how the available bits are split into `sign` + `exponent` + `mantissa`. There's always 1 bit to determine the sign of the floating point value. The more bits in the exponent, the larger the range that can be represented. The more bits in the mantissa, the greater the precision (~significant figures) of the values. .. list-table:: :header-rows: 1 * - Type - # bits - Standard - +ive Range - bitstring / struct format * - Double precision - 1 + 11 + 52 - IEEE 754 - 10\ :sup:`-308` → 10\ :sup:`308` - ``'float64'`` / ``'d'`` * - Single precision - 1 + 8 + 23 - IEEE 754 - 10\ :sup:`-38` → 10\ :sup:`38` - ``'float32'`` / ``'f'`` * - Half precision - 1 + 5 + 10 - IEEE 754 - 6×10\ :sup:`-8` → 65504 - ``'float16'`` / ``'e'`` * - bfloat - 1 + 8 + 7 - ``-`` - 10\ :sup:`-38` → 10\ :sup:`38` - ``'bfloat'`` / ``-`` An example of creation and interpretation of a bfloat:: >>> a = Bits(bfloat=4.5e23) # No need to specify length as always 16 bits >>> a Bits('0x66be') >>> a.bfloat 4.486248158726163e+23 # Converted to Python float IEEE 8-bit Floating Point Types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: This is an experimental feature and may be modified in future point releases. In bitstring prior to version 4.2 the `p4binary8` and `p3binary8` formats were called `e4m3float` and `e5m2float` respectively. The two formats are almost identical, the difference being the addition of `inf` values that replace the largest positive and negative values that were previously available. Neither should be confused with the `e4m3mxfp` and `e5m2mxfp` formats from the Open Compute Project described below. The 'binary8' formats are part of an ongoing IEEE standardisation process. This implementation here is based on a publicly available draft of the standard. There are seven formats defined in the draft standard, but only two are supported here. If you'd like the other precisions supported then raise a feature request! The ``p4binary8`` has a single sign bit, 4 bits for the exponent and 3 bits for the mantissa. For a bit more range and less precision you can use ``p3binary8`` which has 5 bits for the exponent and only 2 for the mantissa. Note that in the standard they are called `binary8p4` and `binary8p3`, but for bitstring types any final digits should be the length of the type, so these slightly modified names were chosen. .. list-table:: :header-rows: 1 * - Type - # bits - +ive Range - bitstring format * - binary8p4 - 1 + 4 + 3 - 10\ :sup:`-3` → 224 - ``'p4binary8'`` * - binary8p3 - 1 + 5 + 2 - 8×10\ :sup:`-6` → 49152 - ``'p3binary8'`` As there are just 256 possible values, both the range and precision of these formats are extremely limited. It's remarkable that any useful calculations can be performed, but both inference and training of large machine learning models can be done with these formats. You can easily examine every possible value that these formats can represent using a line like this:: >>> [Bits(uint=x, length=8).p3binary8 for x in range(256)] or using the :class:`Array` type it's even more concise - we can create an Array and pretty print all the values with this line:: >>> Array('p4binary8', bytearray(range(256))).pp(width=90) [ 0: 0.0 0.0009765625 0.001953125 0.0029296875 0.00390625 0.0048828125 6: 0.005859375 0.0068359375 0.0078125 0.0087890625 0.009765625 0.0107421875 12: 0.01171875 0.0126953125 0.013671875 0.0146484375 0.015625 0.017578125 18: 0.01953125 0.021484375 0.0234375 0.025390625 0.02734375 0.029296875 24: 0.03125 0.03515625 0.0390625 0.04296875 0.046875 0.05078125 30: 0.0546875 0.05859375 0.0625 0.0703125 0.078125 0.0859375 36: 0.09375 0.1015625 0.109375 0.1171875 0.125 0.140625 42: 0.15625 0.171875 0.1875 0.203125 0.21875 0.234375 48: 0.25 0.28125 0.3125 0.34375 0.375 0.40625 54: 0.4375 0.46875 0.5 0.5625 0.625 0.6875 60: 0.75 0.8125 0.875 0.9375 1.0 1.125 66: 1.25 1.375 1.5 1.625 1.75 1.875 72: 2.0 2.25 2.5 2.75 3.0 3.25 78: 3.5 3.75 4.0 4.5 5.0 5.5 84: 6.0 6.5 7.0 7.5 8.0 9.0 90: 10.0 11.0 12.0 13.0 14.0 15.0 96: 16.0 18.0 20.0 22.0 24.0 26.0 102: 28.0 30.0 32.0 36.0 40.0 44.0 108: 48.0 52.0 56.0 60.0 64.0 72.0 114: 80.0 88.0 96.0 104.0 112.0 120.0 120: 128.0 144.0 160.0 176.0 192.0 208.0 126: 224.0 inf nan -0.0009765625 -0.001953125 -0.0029296875 132: -0.00390625 -0.0048828125 -0.005859375 -0.0068359375 -0.0078125 -0.0087890625 138: -0.009765625 -0.0107421875 -0.01171875 -0.0126953125 -0.013671875 -0.0146484375 144: -0.015625 -0.017578125 -0.01953125 -0.021484375 -0.0234375 -0.025390625 150: -0.02734375 -0.029296875 -0.03125 -0.03515625 -0.0390625 -0.04296875 156: -0.046875 -0.05078125 -0.0546875 -0.05859375 -0.0625 -0.0703125 162: -0.078125 -0.0859375 -0.09375 -0.1015625 -0.109375 -0.1171875 168: -0.125 -0.140625 -0.15625 -0.171875 -0.1875 -0.203125 174: -0.21875 -0.234375 -0.25 -0.28125 -0.3125 -0.34375 180: -0.375 -0.40625 -0.4375 -0.46875 -0.5 -0.5625 186: -0.625 -0.6875 -0.75 -0.8125 -0.875 -0.9375 192: -1.0 -1.125 -1.25 -1.375 -1.5 -1.625 198: -1.75 -1.875 -2.0 -2.25 -2.5 -2.75 204: -3.0 -3.25 -3.5 -3.75 -4.0 -4.5 210: -5.0 -5.5 -6.0 -6.5 -7.0 -7.5 216: -8.0 -9.0 -10.0 -11.0 -12.0 -13.0 222: -14.0 -15.0 -16.0 -18.0 -20.0 -22.0 228: -24.0 -26.0 -28.0 -30.0 -32.0 -36.0 234: -40.0 -44.0 -48.0 -52.0 -56.0 -60.0 240: -64.0 -72.0 -80.0 -88.0 -96.0 -104.0 246: -112.0 -120.0 -128.0 -144.0 -160.0 -176.0 252: -192.0 -208.0 -224.0 -inf ] You'll see that there is only 1 zero value and only one 'nan' value, together with positive and negative 'inf' values. When converting from a Python float (which will typically be stored in 64-bits) unrepresented values are rounded to nearest, with ties-to-even. This is the standard method used in IEEE 754. Microscaling Formats ^^^^^^^^^^^^^^^^^^^^ .. note:: This is an experimental feature and may be modified in future point releases. A range of formats from the Microscaling Formats (MX) Alliance are supported. These are part of the Open Compute Project, and will usually have an external scale factor associated with them. Eight-bit floats similar to the IEEE `p3binary8` and `p4binary8` are available, though these seem rather arbitrary and ugly in places in comparison to the IEEE definitions. There is also a format to use for the scaling factor, an int-like format which is really a float, and some sensible six and four bit float formats. .. list-table:: :header-rows: 1 * - Type - # bits - +ive Range - bitstring format * - E5M2 - 1 + 5 + 2 - 10\ :sup:`-6` → 57344 - ``'e5m2mxfp'`` * - E4M3 - 1 + 4 + 3 - 2×10\ :sup:`-3` → 448 - ``'e4m3mxfp'`` * - E3M2 - 1 + 3 + 2 - 0.0625 → 28 - ``'e3m2mxfp'`` * - E2M3 - 1 + 2 + 3 - 0.125 → 7.5 - ``'e2m3mxfp'`` * - E2M1 - 1 + 2 + 1 - 0.5 → 6 - ``'e2m1mxfp'`` * - E8M0 - 0 + 8 + 0 - 10\ :sup:`-38` → 10\ :sup:`38` - ``'e8m0mxfp'`` * - INT8 - 8 - 0.015625 → 1.984375 - ``'mxint'`` * The E8M0 format is unsigned and designed to use as a scaling factor for blocks of the other formats. * The INT8 format is like a signed two's complement 8-bit integer but with a scaling factor of 2\ :sup:`-6`. So despite its name it is actually a float. The standard doesn't specify whether the largest negative value (-2.0) is a supported number or not. This implementation allows it. * The E4M3 format is similar to the `p4binary8` format but with a different exponent bias and it wastes some values. It has no 'inf' values, instead opting to have two 'nan' values and two zero values. * The E5M2 format is similar to the `p3binary8` format but wastes even more values. It does have positive and negative 'inf' values, but also six 'nan' values and two zero values. The MX formats are designed to work with an external scaling factor. This should be in the E8M0 format, which uses a byte to encode the powers of two from 2\ :sup:`-127` to 2\ :sup:`127`, plus a 'nan' value. This can be specified in bitstring as part of the `Dtype`, and is very useful inside an ``Array``. :: >>> d = b'some_byte_data' >>> a = Array(Dtype('e2m1mxfp', scale=2**10), d) >>> a.pp() [ 0: 6144.0 1536.0 4096.0 -6144.0 4096.0 -3072.0 4096.0 3072.0 3072.0 -6144.0 4096.0 1024.0 6144.0 -512.0 14: 6144.0 2048.0 4096.0 3072.0 3072.0 -6144.0 4096.0 2048.0 4096.0 512.0 6144.0 2048.0 4096.0 512.0 ] To change the scale, replace the dtype in the `Array`:: >>> a.dtype = Dtype('e2m1mxfp', scale=2**6) When initialising an `Array` from a list of values, you can also use the string ``'auto'`` as the scale, and an appropriate scale will be calculated based on the data. :: >>> a = Array(Dtype('e2m1mxfp', scale='auto'), [0.0, 0.5, 40.5, 106.25, -52.0, -8.0]) >>> a.pp() [ 0: 0.0 0.0 32.0 96.0 -48.0 -8.0 ] The scale is calculated based on the maximum absolute value of the data and the maximum representable value of the format. The auto-scale feature is only available for 8-bit and smaller floating point formats, plus the IEEE 16-bit format. If all of the data is zero, then the scale is set to 1. For more details on this and these formats in general see the `OCP Microscaling formats specification. `_ Conversion ^^^^^^^^^^ When converting from a Python float to an any of the 8-or-fewer bit formats, the 'rounds to nearest, ties to even' rule is used. This is the same as is used in the IEEE 754 standard. Note that for efficiency reasons Python floats are converted to 16-bit IEEE floats before being converted to their final destination. This can mean that in edge cases the rounding to the 16-bit float will cause the next rounding to go in the other direction. The 16-bit float has 11 bits of precision, whereas the final format has at most 4 bits of precision, so this shouldn't be a real-world problem, but it could cause discrepancies when comparing with other methods. I could add a slower, more accurate mode if this is a problem (add a bug report). Values that are out of range after rounding are dealt with as follows: - ``p3binary8`` - Out of range values are set to ``+inf`` or ``-inf``. - ``p4binary8`` - Out of range values are set to ``+inf`` or ``-inf``. - ``e5m2mxfp`` - Out of range values are dealt with according to the ``bitstring.options.mxfp_overflow`` setting: - ``'saturate'`` (the default): values are set to the largest positive or negative finite value, as appropriate. Infinities will also be set to the largest finite value, despite the fact that the format has infinities. - ``'overflow'``: Out of range values are set to ``+inf`` or ``-inf``. - ``e4m3mxfp`` - Out of range values are dealt with according to the ``bitstring.options.mxfp_overflow`` setting: - ``'saturate'`` (the default): values are set to the largest positive or negative value, as appropriate. - ``'overflow'``: Out of range values are set to ``nan``. - ``e3m2mxfp`` - Out of range values are saturated to the largest positive or negative value. - ``e2m3mxfp`` - Out of range values are saturated to the largest positive or negative value. - ``e2m1mxfp`` - Out of range values are saturated to the largest positive or negative value. - ``mxint`` - Out of range values are saturated to the largest positive or negative value. Note that the most negative value in this case is ``-2.0``, which it is optional for an implementation to support. - ``e8m0mxfp`` - No rounding is done. A ``ValueError`` will be raised if you try to convert a non-representable value. This is because the format is designed as a scaling factor, so it should generally be specified exactly.bitstring-bitstring-4.2.3/doc/exp-golomb.rst000066400000000000000000000101151462444752600211140ustar00rootroot00000000000000.. currentmodule:: bitstring .. _exp-golomb: Exponential-Golomb Codes ======================== As this type of representation of integers isn't as well known as the standard base-2 representation I thought that a short explanation of them might be welcome. Exponential-Golomb codes represent integers using bit patterns that get longer for larger numbers. For unsigned and signed numbers (the bitstring properties :attr:`~Bits.ue` and :attr:`~Bits.se` respectively) the patterns start like this: ============= =========== =========== Bit pattern Unsigned Signed ============= =========== =========== ``1`` 0 0 ``010`` 1 1 ``011`` 2 -1 ``00100`` 3 2 ``00101`` 4 -2 ``00110`` 5 3 ``00111`` 6 -3 ``0001000`` 7 4 ``0001001`` 8 -4 ``0001010`` 9 5 ``0001011`` 10 -5 ``0001100`` 11 6 ``...`` ... ... ============= =========== =========== They consist of a sequence of n '0' bits, followed by a '1' bit, followed by n more bits. The bits after the first '1' bit count upwards as ordinary base-2 binary numbers until they run out of space and an extra '0' bit needs to get included at the start. The advantage of this method of representing integers over many other methods is that it can be quite efficient at representing small numbers without imposing a limit on the maximum number that can be represented. ue ^^ The :attr:`~Bits.ue` property interprets the bitstring as a single unsigned exponential-Golomb code and returns an integer. If the bitstring is not exactly one code then an :exc:`InterpretError` is raised instead. If you instead wish to read the next bits in the stream and interpret them as a code use the read function or unpack with a ``ue`` format string. :: >>> s = BitStream(ue=12) >>> s.bin '0001101' >>> s.append('ue=3') >>> print(s.unpack('2*ue')) [12, 3] se ^^ The :attr:`~Bits.se` property does much the same as ``ue`` and the provisos there all apply. The obvious difference is that it interprets the bitstring as a signed exponential-Golomb rather than unsigned. :: >>> s = BitStream('0x164b') >>> s.se InterpretError: Bitstring is not a single exponential-Golomb code. >>> while s.pos < len(s): ... print(s.read('se')) -5 2 0 -1 Exercise ^^^^^^^^ Using the table above decode this sequence of unsigned Exponential Golomb codes: ``001001101101101011000100100101`` The answer is that it decodes to 3, 0, 0, 2, 2, 1, 0, 0, 8, 4. Note how you don’t need to know how many bits are used for each code in advance - there’s only one way to decode it. To create this bitstring you could have written something like:: >>> a = Bits().join(f'ue={i}' for i in [3,0,0,2,2,1,0,0,8,4]) and to unpack it again:: >>> a.unpack('10*ue') [3, 0, 0, 2, 2, 1, 0, 0, 8, 4] The notation ``ue`` and ``se`` for the exponential-Golomb code properties comes from the H.264 video standard, which uses these types of code a lot. There are other ways to map the bitstrings to integers: Interleaved codes ^^^^^^^^^^^^^^^^^ This type of code is used in the Dirac video standard, and is represented by the attributes :attr:`~Bits.uie` and :attr:`~Bits.sie`. For the interleaved codes the pattern is very similar to before for the unsigned case: ============= =========== Bit pattern Unsigned ============= =========== ``1`` 0 ``001`` 1 ``011`` 2 ``00001`` 3 ``00011`` 4 ``01001`` 5 ``01011`` 6 ``0000001`` 7 ``0000011`` 8 ``0001001`` 9 ``...`` ... ============= =========== For the signed code it looks a little different: ============= =========== Bit pattern Signed ============= =========== ``1`` 0 ``0010`` 1 ``0011`` -1 ``0110`` 2 ``0111`` -2 ``000010`` 3 ``000011`` -3 ``000110`` 4 ``000111`` -4 ``010010`` 5 ``010011`` -5 ``...`` ... ============= =========== I'm sure you can work out the pattern yourself from here!bitstring-bitstring-4.2.3/doc/functions.rst000066400000000000000000000247201462444752600210620ustar00rootroot00000000000000.. currentmodule:: bitstring Functions --------- pack ^^^^ .. function:: pack(format[, *values, **kwargs]) Packs the values and keyword arguments according to the *format* string and returns a new :class:`BitStream`. :param format: string with comma separated tokens :param values: extra values used to construct the :class:`BitStream` :param kwargs: a dictionary of token replacements :rtype: BitStream The format string consists of comma separated tokens, see :ref:`format_tokens` and :ref:`compact_format` for details. The tokens can be 'literals', like ``0xef``, ``0b110``, ``uint8=55``, etc. which just represent a set sequence of bits. They can also have the value missing, in which case the values contained in ``*values`` will be used. :: >>> a = pack('bin3, hex4', '001', 'f') >>> b = pack('uint10', 33) A dictionary or keyword arguments can also be provided. These will replace items in the format string. :: >>> c = pack('int:a=b', a=10, b=20) >>> d = pack('int8=a, bin=b, int4=a', a=7, b='0b110') Plain names can also be used as follows:: >>> e = pack('a, b, b, a', a='0b11', b='0o2') Tokens starting with an endianness identifier (``<``, ``>`` or ``=``) implies a struct-like compact format string (see :ref:`compact_format`). For example this packs three little-endian 16-bit integers:: >>> f = pack('<3h', 12, 3, 108) And of course you can combine the different methods in a single pack. A :exc:`ValueError` will be raised if the ``*values`` are not all used up by the format string, and if a value provided doesn't match the length specified by a token. As an example of using just the ``*values`` arguments we can say:: s = bitstring.pack('hex32, uint12, uint12', '0x000001b3', 352, 288) which is equivalent to initialising as:: s = BitStream('0x0000001b3, uint12=352, uint12=288') The advantage of the pack function is if you want to write more general code for creation. :: def foo(a, b, c, d): return bitstring.pack('uint8, 0b110, int6, bin, bits', a, b, c, d) s1 = foo(12, 5, '0b00000', '') s2 = foo(101, 3, '0b11011', s1) Note how you can use some tokens without sizes (such as ``bin`` and ``bits`` in the above example), and use values of any length to fill them. If the size had been specified then a :exc:`ValueError` would be raised if the parameter given was the wrong length. Note also how bitstring literals can be used (the ``0b110`` in the bitstring returned by ``foo``) and these don't consume any of the items in ``*values``. You can also include keyword, value pairs (or an equivalent dictionary) as the final parameter(s). The values are then packed according to the positions of the keywords in the format string. This is most easily explained with some examples. Firstly the format string needs to contain parameter names:: format = 'hex32=start_code, uint12=width, uint12=height' Then we can make a dictionary with these parameters as keys and pass it to pack:: d = {'start_code': '0x000001b3', 'width': 352, 'height': 288} s = bitstring.pack(format, **d) Another method is to pass the same information as keywords at the end of pack's parameter list:: s = bitstring.pack(format, width=352, height=288, start_code='0x000001b3') You can include constant bitstring tokens such as '0x101', '0xff', 'uint7=81' etc. and also use a keyword for the length specifier in the token, for example:: s = bitstring.pack('0xabc, int:n=-1', n=100) Finally it is also possible just to use a keyword as a token:: s = bitstring.pack('hello, world', world='0x123', hello='0b110') ---- Options ------- The bitstring module has an ``options`` object that allows certain module-wide behaviours to be set. lsb0 ^^^^ .. data:: bitstring.options.lsb0 : bool By default bit numbering in the bitstring module is done from 'left' to 'right'. That is, from bit ``0`` at the start of the data to bit ``n - 1`` at the end. This allows bitstrings to be treated like an ordinary Python container that is only allowed to contain single bits. The ``lsb0`` option allows bitstrings to use Least Significant Bit Zero (LSB0) bit numbering; that is the right-most bit in the bitstring will be bit 0, and the left-most bit will be bit (n-1), rather than the other way around. LSB0 is a more natural numbering system in many fields, but is the opposite to Most Significant Bit Zero (MSB0) numbering which is the natural option when thinking of bitstrings as standard Python containers. For example, if you set a bitstring to be the binary ``010001111`` it will be stored in the same way for MSB0 and LSB0 but slicing, reading, unpacking etc. will all behave differently. .. list-table:: MSB0 → :header-rows: 1 * - bit index - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 * - value - ``0`` - ``1`` - ``0`` - ``0`` - ``0`` - ``1`` - ``1`` - ``1`` - ``1`` In MSB0 everything behaves like an ordinary Python container. Bit zero is the left-most bit and reads/slices happen from left to right. .. list-table:: ← LSB0 :header-rows: 1 * - bit index - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 * - value - ``0`` - ``1`` - ``0`` - ``0`` - ``0`` - ``1`` - ``1`` - ``1`` - ``1`` In LSB0 the final, right-most bit is labelled as bit zero. Reads and slices happen from right to left. When bitstrings (or slices of bitstrings) are interpreted as integers and other types the left-most bit is considered as the most significant bit. It's important to note that this is the case irrespective of whether the first or last bit is considered the bit zero, so for example if you were to interpret a whole bitstring as an integer, its value would be the same with and without `lsb0` being set to `True`. To illustrate this, for the example above this means that the bin and int representations would be ``010001111`` and ``143`` respectively for both MSB0 and LSB0 bit numbering. To switch from the default MSB0, use ``bitstring.options.lsb0``. This defaults to ``False`` and unless explicitly stated all examples and documentation related to the bitstring module use the default MSB0 indexing. >>> bitstring.options.lsb0 = True Slicing is still done with the start bit smaller than the end bit. For example: >>> s = Bits('0b010001111') >>> s[0:5] # LSB0 so this is the right-most five bits Bits('0b01111') >>> s[0] True .. note:: In some standards and documents using LSB0 notation the slice of the final five bits would be shown as ``s[5:0]``, which is reasonable as bit 5 comes before bit 0 when reading left to right, but this notation isn't used in this module as it clashes too much with the usual Python notation. Negative indices work as you'd expect, with the first stored bit being ``s[-1]`` and the final stored bit being ``s[-n]``. Reading, peeking and unpacking of bitstrings are also affected by the ``lsb0`` flag, so reading always increments the bit position, and will move from right to left if ``lsb0`` is ``True``. Because of the way that exponential-Golomb codes are read (with the left-most bits determining the length of the code) these interpretations are not available in LSB0 mode, and using them will raise an exception. For ``BitStream`` and ``ConstBitStream`` objects changing the value of ``bitstring.options.lsb0`` invalidates the current position in the bitstring, unless that value is ``0``, and future results are undefined. Basically don't perform reads or change the current bit position before switching the bit numbering system! bytealigned ^^^^^^^^^^^ .. data:: bitstring.options.bytealigned : bool A number of methods take a bytealigned parameter to indicate that they should only work on byte boundaries (e.g. :meth:`~Bits.find`, :meth:`~Bits.findall`, :meth:`~Bits.split` and :meth:`~BitArray.replace`). This parameter defaults to ``bitstring.options.bytealigned``, which itself defaults to ``False``, but can be changed to modify the default behaviour of the methods. For example:: >>> a = BitArray('0x00 ff 0f ff') >>> a.find('0x0f') (4,) # found first not on a byte boundary >>> a.find('0x0f', bytealigned=True) (16,) # forced looking only on byte boundaries >>> bitstring.options.bytealigned = True # Change default behaviour >>> a.find('0x0f') (16,) >>> a.find('0x0f', bytealigned=False) (4,) If you’re only working with bytes then this can help avoid some errors and save some typing. mxfp_overflow ^^^^^^^^^^^^^ .. data:: bitstring.options.mxfp_overflow : str This option can be used to change the out-of-range behaviour of some 8-bit floating point types. The default value is ``'saturate'`` but it can also be set to ``'overflow'``. See :ref:`Exotic floats` for details. no_color ^^^^^^^^ .. data:: bitstring.options.no_color : bool The bitstring module can use ANSI escape codes to colourise the output of the :meth:`Bits.pp` and :meth:`Array.pp` methods. If a ``NO_COLOR`` environment variable is found and is not an empty string then this option will be set to ``True``, otherwise it defaults to ``False``. See https://no-color.org for more information. The terminal colours can also be turned off by setting ``bitstring.options.no_color`` to ``True``. ---- Command Line Usage ------------------ The bitstring module can be called from the command line to perform simple operations. For example:: $ python -m bitstring int16=-400 0xfe70 $ python -m bitstring float32=0.2 bin 00111110010011001100110011001101 $ python -m bitstring 0xff "3*0b01,0b11" uint 65367 $ python -m bitstring hex=01, uint12=352.hex 01160 Command-line parameters are concatenated and a bitstring created from them. If the final parameter is either an interpretation string or ends with a ``.`` followed by an interpretation string then that interpretation of the bitstring will be used when printing it. If no interpretation is given then the bitstring is just printed. ---- Exceptions ---------- .. exception:: Error(Exception) Base class for all module exceptions. .. exception:: InterpretError(Error, ValueError) Inappropriate interpretation of binary data. For example using the 'bytes' property on a bitstring that isn't a whole number of bytes long. .. exception:: ByteAlignError(Error) Whole-byte position or length needed. .. exception:: CreationError(Error, ValueError) Inappropriate argument during bitstring creation. .. exception:: ReadError(Error, IndexError) Reading or peeking past the end of a bitstring. bitstring-bitstring-4.2.3/doc/index.rst000066400000000000000000000152621462444752600201620ustar00rootroot00000000000000 .. currentmodule:: bitstring .. image:: bitstring_logo.png :width: 400px `bitstring `_ is a Python module to help make the creation and analysis of all types of bit-level binary data as simple and efficient as possible. It has been maintained since 2006. .. image:: https://github.com/scott-griffiths/bitstring/actions/workflows/.github/workflows/ci.yml/badge.svg :target: https://github.com/scott-griffiths/bitstring/actions/workflows/ci.yml .. image:: https://img.shields.io/readthedocs/bitstring :target: https://bitstring.readthedocs.io/en/latest/ .. image:: https://img.shields.io/codacy/grade/8869499b2eed44548fa1a5149dd451f4?logo=codacy :target: https://app.codacy.com/gh/scott-griffiths/bitstring/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade .. image:: https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue :target: https://www.pepy.tech/projects/bitstring .. image:: https://img.shields.io/pypi/dm/bitstring?label=%40&labelColor=blue&color=blue) :target: https://pypistats.org/packages/bitstring ------ Overview ======== * Efficiently store and manipulate binary data in idiomatic Python. * Create bitstrings from hex, octal, binary, files, formatted strings, bytes, integers and floats of different endiannesses. * Powerful binary packing and unpacking functions. * Bit level slicing, joining, searching, replacing and more. * Read from and interpret bitstrings as streams of binary data. * Create efficiently stored arrays of any fixed-length format. * Rich API - chances are that whatever you want to do there's a simple and elegant way of doing it. * Open source software, released under the MIT licence. It is not difficult to manipulate binary data in Python, for example using the ``struct`` and ``array`` modules, but it can be quite fiddly and time consuming even for quite small tasks, especially if you are not dealing with whole-byte data. The bitstring module provides support many different bit formats, allowing easy and efficient storage, interpretation and construction. Documentation Quick Start ^^^^^^^^^^^^^^^^^^^^^^^^^ You can take a look at the introductory walkthrough notebook `here `_. The :ref:`quick_reference` provides a summary of the classes and their methods. The :ref:`reference` section has a complete list of all the classes, methods, properties and functions of the bitstring module, together with short examples for many items. * :class:`Bits` - an immutable container of bits. * :class:`BitArray` - adds mutating methods to ``Bits``. * :class:`BitStream` - adds a bit position and read methods. * :class:`Array` - an array of bitstrings of the same type. .. toctree:: :hidden: self .. toctree:: :hidden: quick_reference reference appendices Mixed format bitstrings ^^^^^^^^^^^^^^^^^^^^^^^ If you have binary data (or want to construct it) from multiple types then you could use the :class:`BitArray` class. The example below constructs a 28 bit bitstring from a hexadecimal string, then unpacks it into multiple bit interpretations. It also demonstrates how it can be flexibly modified and sliced using standard notation, and how properties such as ``bin`` and ``float`` can be used to interpret the data. :: >>> s = bitstring.BitArray('0x4f8e220') >>> s.unpack('uint12, hex8, bin') [1272, 'e2', '00100000'] >>> '0b11000' in s True >>> s += 'f32=0.001' >>> s.bin '010011111000111000100010000000111010100000110001001001101111' >>> s[-32:].float 0.0010000000474974513 The module also supplies the :class:`BitStream` class, which adds a bit position so that objects can also be read from, searched in, and navigated in, similar to a file or stream. Bitstrings are designed to be as lightweight as possible and can be considered to be just a list of binary digits. They are however stored efficiently - although there are a variety of ways of creating and viewing the binary data, the bitstring itself just stores the byte data, and all views are calculated as needed, and are not stored as part of the object. The different views or interpretations on the data are accessed through properties such as :attr:`~Bits.hex`, :attr:`~Bits.bin` and :attr:`~Bits.int`, and an extensive set of functions is supplied for modifying, navigating and analysing the binary data. There are also a companion classes called :class:`Bits` and :class:`ConstBitStream` which are immutable versions of :class:`BitArray` and :class:`BitStream` respectively. See the reference documentation for full details. Arrays of bitstrings ^^^^^^^^^^^^^^^^^^^^ If you are dealing with just one type of data but perhaps it's not one of the dozen or so supported in the ``array`` module in the standard library, then we have you covered with the :class:`Array` class. A ``bitstring.Array`` works in a similar way to a ``array.array``, except that you can efficiently pack in any fixed-length binary format. Want an array of 5 bit unsigned integers, or of 8 or 16 bit floating point numbers? No problem. You can also easily change the data's interpretation, convert to another format, and freely modify the underlying data which is stored as a :class:`BitArray` object. :: >>> a = bitstring.Array('uint16', [0, 1, 4, 6, 11, 2, 8, 7]) >>> a.data BitArray('0x0000000100040006000b000200080007') >>> b = a.astype('uint5') >>> b.data BitArray('0x0048658907') >>> a.tolist() == b.tolist() True You can also take and set slices as you'd expect, and apply operations to each element in the ``Array``. :: >>> a[::2] *= 5 >>> a Array('uint16', [0, 1, 20, 6, 55, 2, 40, 7]) >>> a >> 2 Array('uint16', [0, 0, 5, 1, 13, 0, 10, 1]) Installation and download ^^^^^^^^^^^^^^^^^^^^^^^^^ To install just ``pip install bitstring``. To download the module, as well as for defect reports, enhancement requests and Git repository browsing go to `the project's home on GitHub. `_ Release Notes ^^^^^^^^^^^^^ To see what been added, improved or fixed, and possibly also to see what's coming in the next version, see the `release notes `_ on GitHub. Credits ^^^^^^^ Created by Scott Griffiths in 2006 to help with ad hoc parsing and creation of compressed video files. Maintained and expanded ever since as it became unexpectedly popular. Thanks to all those who have contributed ideas, code and bug reports over the years. These docs are styled using the `Piccolo theme `_. bitstring-bitstring-4.2.3/doc/interpretation.rst000066400000000000000000000151711462444752600221210ustar00rootroot00000000000000.. currentmodule:: bitstring Interpreting Bitstrings ======================= Bitstrings don't know or care how they were created; they are just collections of bits. This means that you are quite free to interpret them in any way that makes sense. Several Python properties are used to create interpretations for the bitstring. These properties call private functions which will calculate and return the appropriate interpretation. These don’t change the bitstring in any way and it remains just a collection of bits. If you use the property again then the calculation will be repeated. Note that these properties can potentially be very expensive in terms of both computation and memory requirements. For example if you have initialised a bitstring from a 10 GiB file object and ask for its binary string representation then that string will be around 80 GiB in size! If you're in an interactive session then the pretty-print method :meth:`~Bits.pp` can be useful as it will only convert the bitstring one chunk at a time for display. ---- Properties ---------- Many of the more commonly used interpretations have single letter equivalents. The ``hex``, ``bin``, ``oct``, ``int``, ``uint`` and ``float`` properties can be shortened to ``h``, ``b``, ``o``, ``i``, ``u`` and ``f`` respectively. Properties can have bit lengths appended to them to make properties such as ``f64``, ``u32`` or ``floatle32``. When used as a getter these just add an extra check on the bitstring's length - if the bitstring is not the stated length then an :exc:`InterpretError` is raised. When used as a setter they define the new length of the bitstring. :: s = BitArray() # Empty bitstring s.f32 = 101.5 # New length is 32 bits, representing a float For the properties described below we will use these:: >>> a = BitArray('0x123') >>> b = BitArray('0b111') bin / hex / oct --------------- The most fundamental interpretation is perhaps as a binary string (a ‘bitstring’). The :attr:`~Bits.bin` property returns a string of the binary representation of the bitstring. All bitstrings can use this property and it is used to test equality between bitstrings. :: >>> a.bin '000100100011' >>> b.b '111' Note that the initial zeros are significant; for bitstrings the zeros are just as important as the ones! For whole-byte bitstrings the most natural interpretation is often as hexadecimal, with each byte represented by two hex digits. If the bitstring does not have a length that is a multiple of four bits then an :exc:`InterpretError` exception will be raised. This is done in preference to truncating or padding the value, which could hide errors in user code. :: >>> a.hex '123' >>> b.h ValueError: Cannot convert to hex unambiguously - not multiple of 4 bits. For an octal interpretation use the :attr:`~Bits.oct` property. If the bitstring does not have a length that is a multiple of three then an :exc:`InterpretError` exception will be raised. :: >>> a.oct '0443' >>> b.o '7' >>> (b + '0b0').oct ValueError: Cannot convert to octal unambiguously - not multiple of 3 bits. Integer types ------------- To interpret the bitstring as a binary (base-2) bit-wise big-endian unsigned integer (i.e. a non-negative integer) use the :attr:`~Bits.uint` property. >>> a.uint 283 >>> b.u 7 For byte-wise big-endian, little-endian and native-endian interpretations use :attr:`~Bits.uintbe`, :attr:`~Bits.uintle` and :attr:`~Bits.uintne` respectively. These will raise a :exc:`ValueError` if the bitstring is not a whole number of bytes long. :: >>> s = BitArray('0x000001') >>> s.uint # bit-wise big-endian 1 >>> s.uintbe # byte-wise big-endian 1 >>> s.uintle # byte-wise little-endian 65536 >>> s.uintne # byte-wise native-endian (will be 1 on a big-endian platform!) 65536 For a two's complement interpretation as a base-2 signed integer use the :attr:`~Bits.int` property. If the first bit of the bitstring is zero then the :attr:`~Bits.int` and :attr:`~Bits.uint` interpretations will be equal, otherwise the :attr:`~Bits.int` will represent a negative number. :: >>> a.int 283 >>> b.i -1 For byte-wise big, little and native endian signed integer interpretations use :attr:`~Bits.intbe`, :attr:`~Bits.intle` and :attr:`~Bits.intne` respectively. These work in the same manner as their unsigned counterparts described above. bytes ----- A common need is to retrieve the raw bytes from a bitstring for further processing or for writing to a file. For this use the :py:attr:`~Bits.bytes` interpretation, which returns a ``bytes`` object. If the length of the bitstring isn't a multiple of eight then a :exc:`ValueError` will be raised. This is because there isn't an unequivocal representation as ``bytes``. You may prefer to use the method :meth:`~Bits.tobytes` as this will be pad with between one and seven zero bits up to a byte boundary if necessary. :: >>> open('somefile', 'wb').write(a.tobytes()) >>> open('anotherfile', 'wb').write(('0x0'+a).bytes) >>> a1 = BitArray(filename='somefile') >>> a1.hex '1230' >>> a2 = BitArray(filename='anotherfile') >>> a2.hex '0123' Note that the :meth:`~Bits.tobytes` method automatically padded with four zero bits at the end, whereas for the other example we explicitly padded at the start to byte align before using the :attr:`~Bits.bytes` property. Floating point types -------------------- For a floating point interpretation use the :attr:`~Bits.float` property. This uses the IEEE 754 floating point representation and will only work if the bitstring is 16, 32 or 64 bits long. Different endiannesses are provided via :attr:`~Bits.floatle` and :attr:`~Bits.floatne`. Note that as floating point interpretations are only valid on whole-byte bitstrings there is no difference between the bit-wise big-endian :attr:`~Bits.float` and the byte-wise big-endian :attr:`~Bits.floatbe`. Note also that standard floating point numbers in Python are stored in 64 bits, so use this size if you wish to avoid rounding errors. Other floating point types -------------------------- A range of floating point types that are mostly used in machine learning are also availabe. They include ``bfloat16`` which is a truncated ``float32``, together with IEEE 8-bit formats and a range of OCP Microscaling 8-bit, 6-bit and 4-bit formats. See :ref:`Exotic floats` for more information. Exponential-Golomb types ------------------------ Some variable length integer types are supported. The lengths of these types depends upon the data being read and they are mainly used in video codecs. See :ref:`exp-golomb` for more information. bitstring-bitstring-4.2.3/doc/introduction.rst000066400000000000000000000416071462444752600215760ustar00rootroot00000000000000 **************** Introduction **************** .. module:: bitstring .. moduleauthor:: Scott Griffiths The bitstring classes --------------------- Five classes are provided by the bitstring module, four are simple containers of bits: * :class:`Bits`: This is the most basic class. It is immutable and so its contents can't be changed after creation. * :class:`BitArray`: This adds mutating methods to its base class. * :class:`ConstBitStream`: This adds methods and properties to allow the bits to be treated as a stream of bits, with a bit position and reading/parsing methods. * :class:`BitStream`: This is the most versatile class, having both the bitstream methods and the mutating methods. :class:`Bits` and :class:`BitArray` are intended to loosely mirror the ``bytes`` and ``bytearray`` types in Python. The term 'bitstring' is used in this documentation to refer generically to any of these four classes. The fifth class is :class:`Array` which is a container of fixed-length bitstrings. The rest of this introduction mostly concerns the more basic types - for more details on :class:`Array` you can go directly to the reference documentation, but understanding how bit format strings are specified will be helpful. To summarise when to use each class: * If you need to change the contents of the bitstring then you must use :class:`BitArray` or :class:`BitStream`. Truncating, replacing, inserting, appending etc. are not available for the const classes. * If you need to use a bitstring as the key in a dictionary or as a member of a ``set`` then you must use :class:`Bits` or a :class:`ConstBitStream`. As :class:`BitArray` and :class:`BitStream` objects are mutable they do not support hashing and so cannot be used in these ways. * If you are creating directly from a file then a :class:`BitArray` or :class:`BitStream` will read the whole file into memory whereas a :class:`Bits` or :class:`ConstBitStream` will not, so using the const classes allows extremely large files to be examined. * If you don't need the extra functionality of a particular class then the simpler ones might be faster and more memory efficient. The fastest and most memory efficient class is :class:`Bits`. The :class:`Bits` class is the base class of the other three class. This means that ``isinstance(s, Bits)`` will be true if ``s`` is an instance of any of the four classes. ---- Constructing bitstrings ----------------------- When initialising a bitstring you need to specify at most one initialiser. This can either be the first parameter in the constructor ('auto' initialisation, described below), or using a keyword argument for a data type. ``Bits(auto, /, length: Optional[int], offset: Optional[int], **kwargs)`` Some of the keyword arguments that can be used are: * ``bytes`` : A ``bytes`` object, for example read from a binary file. * ``hex``, ``oct``, ``bin``: Hexadecimal, octal or binary strings. * ``int``, ``uint``: Signed or unsigned bit-wise big-endian binary integers. * ``intle``, ``uintle``: Signed or unsigned byte-wise little-endian binary integers. * ``intbe``, ``uintbe``: Signed or unsigned byte-wise big-endian binary integers. * ``intne``, ``uintne``: Signed or unsigned byte-wise native-endian binary integers. * ``float`` / ``floatbe``, ``floatle``, ``floatne``: Big, little and native endian floating point numbers. * ``bool`` : A boolean (i.e. True or False). * ``filename`` : Directly from a file, without reading into memory if using :class:`Bits` or :class:`ConstBitStream`. There are also various other flavours of 16-bit, 8-bit and smaller floating point types (see :ref:`Exotic floats`) and exponential-Golomb integer types (see :ref:`exp-golomb`). The ``hex``, ``oct``, ``bin``, ``float``, ``int`` and ``uint`` can all be shortened to just their initial letters. The data type name can be combined with its length if appropriate, or the length can be specified separately. For example:: a = Bits(hex='deadbeef') b = BitArray(f32=100.25) # or = BitArray(float=100.25, length=32) c = ConstBitStream(filename='a_big_file') d = Bits(u12=105) e = BitArray(bool=True) Note that some types need a length to be specified, some don't need one, and others can infer the length from the value. Another way to create a bitstring is via the ``pack`` function, which packs multiple values according to a given format. See the entry on :func:`pack` for more information. ---- .. _auto_init: The auto initialiser -------------------- The first parameter when creating a bitstring is a positional only parameter, referred to as 'auto', that can be a variety of types: * An iterable, whose elements will be evaluated as booleans (imagine calling ``bool()`` on each item) and the bits set to ``1`` for ``True`` items and ``0`` for ``False`` items. * A positive integer, used to create a bitstring of that many zero bits. * 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: * Starting with ``'0x'`` or ``'hex='`` implies hexadecimal. e.g. ``'0x013ff'``, ``'hex=013ff'`` * Starting with ``'0o'`` or ``'oct='`` implies octal. e.g. ``'0o755'``, ``'oct=755'`` * Starting with ``'0b'`` or ``'bin='`` implies binary. e.g. ``'0b0011010'``, ``'bin=0011010'`` * Starting with ``'int'`` or ``'uint'`` followed by a length in bits and ``'='`` gives base-2 integers. e.g. ``'uint8=255'``, ``'int4=-7'`` * To get big, little and native-endian whole-byte integers append ``'be'``, ``'le'`` or ``'ne'`` respectively to the ``'uint'`` or ``'int'`` identifier. e.g. ``'uintle32=1'``, ``'intne16=-23'`` * For floating point numbers use ``'float'`` followed by the length in bits and ``'='`` and the number. The default is big-endian, but you can also append ``'be'``, ``'le'`` or ``'ne'`` as with integers. e.g. ``'float64=0.2'``, ``'floatle32=-0.3e12'`` * Starting with ``'ue='``, ``'uie='``, ``'se='`` or ``'sie='`` implies an exponential-Golomb coded integer. e.g. ``'ue=12'``, ``'sie=-4'`` Multiples tokens can be joined by separating them with commas, so for example ``'uint4=4, 0b1, se=-1'`` represents the concatenation of three elements. Parentheses and multiplicative factors can also be used, for example ``'2*(0b10, 0xf)'`` is equivalent to ``'0b10, 0xf, 0b10, 0xf'``. The multiplying factor must come before the thing it is being used to repeat. Promotion to bitstrings ^^^^^^^^^^^^^^^^^^^^^^^ Almost anywhere that a bitstring is expected you can substitute something that will get 'auto' promoted to a bitstring. For example:: >>> BitArray('0xf') == '0b1111' True Here the equals operator is expecting another bitstring so creates one from the string. The right hand side gets promoted to ``Bits('0b1111')``. Methods that need another bitstring as a parameter will also 'auto' promote, for example:: for bs in s.split('0x40'): if bs.endswith('0b111'): bs.append([1, 0]) ... if 'u8=42' in bs: bs.prepend(b'\x01') which illustrates a variety of methods promoting strings, iterables and a bytes object to bitstrings. Anything that can be used as the first parameter of the ``Bits`` constructor can be auto promoted to a bitstring where one is expected, with the exception of integers. Integers won't be auto promoted, but instead will raise a ``TypeError``:: >>> a = BitArray(100) # Create bitstring with 100 zeroed bits. >>> a += 0xff # TypeError - 0xff is the same as the integer 255. >>> a += '0xff' # Probably what was meant - append eight '1' bits. >>> a += Bits(255) # If you really want to do it then code it explicitly. ``BitsType`` ^^^^^^^^^^^^ .. class:: BitsType(Bits | str | Iterable[Any] | bool | BinaryIO | bytearray | bytes | memoryview | bitarray.bitarray) 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. It's just a union of types rather than an actual class (though it's documented here as a class as I could find no alternative). It's not user accessible, but is just a shorthand way of saying any of the above types. ---- Keyword initialisers -------------------- If the 'auto' initialiser isn't used then at most one keyword initialiser can be used. From a hexadecimal string ^^^^^^^^^^^^^^^^^^^^^^^^^ >>> c = BitArray(hex='0x000001b3') >>> c.hex '000001b3' The initial ``0x`` or ``0X`` is optional. Whitespace is also allowed and is ignored. Note that the leading zeros are significant, so the length of ``c`` will be 32. If you include the initial ``0x`` then you can use the 'auto' initialiser instead. As it is the first parameter in :class:`__init__` this will work equally well:: c = BitArray('0x000001b3') From a binary string ^^^^^^^^^^^^^^^^^^^^ >>> d = BitArray(bin='0011 00') >>> d.bin '001100' An initial ``0b`` or ``0B`` is optional and whitespace will be ignored. As with ``hex``, the 'auto' initialiser will work if the binary string is prefixed by ``0b``:: >>> d = BitArray('0b001100') From an octal string ^^^^^^^^^^^^^^^^^^^^ >>> o = BitArray(oct='34100') >>> o.oct '34100' An initial ``0o`` or ``0O`` is optional, but ``0o`` (a zero and lower-case 'o') is preferred as it is slightly more readable. As with ``hex`` and ``bin``, the 'auto' initialiser will work if the octal string is prefixed by ``0o``:: >>> o = BitArray('0o34100') From an integer ^^^^^^^^^^^^^^^ >>> e = BitArray(uint=45, length=12) >>> f = BitArray(int=-1, length=7) >>> e.bin '000000101101' >>> f.bin '1111111' For initialisation with signed and unsigned binary integers (``int`` and ``uint`` respectively) the ``length`` parameter is mandatory, and must be large enough to contain the integer. So for example if ``length`` is 8 then ``uint`` can be in the range 0 to 255, while ``int`` can range from -128 to 127. Two's complement is used to represent negative numbers. The 'auto' initialiser can be used by giving the length in bits immediately after the ``int`` or ``uint`` token, followed by an equals sign then the value:: >>> e = BitArray('uint12=45') >>> f = BitArray('int7=-1') The ``uint`` and ``int`` names can be shortened to just ``u`` and ``i`` respectively. For mutable bitstrings you can change value by assigning to a property with a length:: >>> e = BitArray() >>> e.u12 = 45 >>> f = BitArray() >>> f.i7 = -1 The plain ``int`` and ``uint`` initialisers are bit-wise big-endian. That is to say that the most significant bit comes first and the least significant bit comes last, so the unsigned number one will have a ``1`` as its final bit with all other bits set to ``0``. These can be any number of bits long. For whole-byte bitstring objects there are more options available with different endiannesses. Big and little-endian integers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >>> big_endian = BitArray(uintbe=1, length=16) >>> little_endian = BitArray(uintle=1, length=16) >>> native_endian = BitArray(uintne=1, length=16) There are unsigned and signed versions of three additional 'endian' types. The unsigned versions are used above to create three bitstrings. The first of these, ``big_endian``, is equivalent to just using the plain bit-wise big-endian ``uint`` initialiser, except that all ``intbe`` or ``uintbe`` interpretations must be of whole-byte bitstrings, otherwise a :exc:`ValueError` is raised. The second, ``little_endian``, is interpreted as least significant byte first, i.e. it is a byte reversal of ``big_endian``. So we have:: >>> big_endian.hex '0001' >>> little_endian.hex '0100' Finally we have ``native_endian``, which will equal either ``big_endian`` or ``little_endian``, depending on whether you are running on a big or little-endian machine (if you really need to check then use ``import sys; sys.byteorder``). From a floating point number ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >>> f1 = BitArray(float=10.3, length=32) >>> f2 = BitArray('float64=5.4e31') Floating point numbers can be used for initialisation provided that the bitstring is 16, 32 or 64 bits long. Standard Python floating point numbers are 64 bits long, so if you use 32 bits then some accuracy could be lost. The 16 bit version has very limited range and is used mainly in specialised areas such as machine learning. The exact bits used to represent the floating point number will conform to the IEEE 754 standard, even if the machine being used does not use that standard internally. Similar to the situation with integers there are big and little endian versions. The plain ``float`` is big endian and so ``floatbe`` is just an alias. As with other initialisers you can also 'auto' initialise, as demonstrated with the second example below:: >>> little_endian = BitArray(floatle=0.0, length=64) >>> native_endian = BitArray('floatne:32=-6.3') See also :ref:`Exotic floats` for information on other floating point representations that are supported (bfloat and different 8-bit and smaller float formats). From exponential-Golomb codes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Initialisation with integers represented by exponential-Golomb codes is also possible. ``ue`` is an unsigned code while ``se`` is a signed code. Interleaved exponential-Golomb codes are also supported via ``uie`` and ``sie``:: >>> g = BitArray(ue=12) >>> h = BitArray(se=-402) >>> g.bin '0001101' >>> h.bin '0000000001100100101' For these initialisers the length of the bitstring is fixed by the value it is initialised with, so the length parameter must not be supplied and it is an error to do so. If you don't know what exponential-Golomb codes are then you are in good company, but they are quite interesting, so I’ve included a section on them (see :ref:`exp-golomb`). The 'auto' initialiser may also be used by giving an equals sign and the value immediately after a ``ue`` or ``se`` token:: >>> g = BitArray('ue=12') >>> h = BitArray('se=-402') You may wonder why you would bother doing this in this case as the syntax is slightly longer. Hopefully all will become clear in the next section. From raw byte data ^^^^^^^^^^^^^^^^^^ Using the length and offset parameters to specify the length in bits and an offset at the start to be ignored is particularly useful when initialising from raw data or from a file. :: a = BitArray(bytes=b'\x00\x01\x02\xff', length=28, offset=1) b = BitArray(bytes=open("somefile", 'rb').read()) The ``length`` parameter is optional; it defaults to the length of the data in bits (and so will be a multiple of 8). You can use it to truncate some bits from the end of the bitstring. The ``offset`` parameter is also optional and is used to truncate bits at the start of the data. You can also use a ``bytearray`` or a ``bytes`` object, either explicitly with a ``bytes=some_bytearray`` keyword or via the 'auto' initialiser:: c = BitArray(a_bytearray_object) d = BitArray(b'\x23g$5') From a file ^^^^^^^^^^^ Using the ``filename`` initialiser allows a file to be analysed without the need to read it all into memory. The way to create a file-based bitstring is:: p = Bits(filename="my200GBfile") This will open the file in binary read-only mode. The file will only be read as and when other operations require it, and the contents of the file will not be changed by any operations. If only a portion of the file is needed then the ``offset`` and ``length`` parameters (specified in bits) can be used. Note that we created a :class:`Bits` here rather than a :class:`BitArray`, as they have quite different behaviour in this case. The immutable :class:`Bits` will never read the file into memory (except as needed by other operations), whereas if we had created a :class:`BitArray` then the whole of the file would immediately have been read into memory. This is because in creating a :class:`BitArray` you are implicitly saying that you want to modify it, and so it needs to be in memory. It's also possible to use the 'auto' initialiser for file objects. It's as simple as:: f = open('my200GBfile', 'rb') p = Bits(f) .. note:: For the immutable types ``Bits`` and ``ConstBitstream`` the file is memory mapped (mmap) in a read-only mode for efficiency. This behaves slightly differently depending on the platform; in particular Windows will lock the file against any further writing whereas Unix-like systems will not. This means that you won't be able to write to the file from Windows OS while the ``Bits`` or ``ConstBitStream`` object exists. The work-arounds for this are to either (i) Delete the object before opening the file for writing, (ii) Use either ``BitArray`` or ``BitStream`` which will read the whole file into memory or (iii) Stop using Windows (or run in WSL). bitstring-bitstring-4.2.3/doc/optimisation.rst000066400000000000000000000077701462444752600215770ustar00rootroot00000000000000.. currentmodule:: bitstring 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. 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. Use combined read and interpretation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When parsing a bitstring one way to write code is in the following style:: width = s.read(12).uint height = s.read(12).uint flags = s.read(4).bin This works fine, but is not very quick. The problem is that the call to :meth:`~ConstBitStream.read` constructs and returns a new bitstring, which then has to be interpreted. The new bitstring isn't used for anything else and so creating it is wasted effort. Instead it is better to use a string parameter that does the read and interpretation together:: width = s.read('uint12') height = s.read('uint12') flags = s.read('bin4') This is much faster, although probably not as fast as the combined call:: width, height, flags = s.readlist('uint12, uint12, bin4') Choose the simplest class you can ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you don't need to modify your bitstring after creation then prefer the immutable :class:`Bits` over the mutable :class:`BitArray`. This is typically the case when parsing, or when creating directly from files. The speed difference between the classes is noticeable, and there are also memory usage optimisations that are made if objects are known to be immutable. You should also prefer :class:`ConstBitStream` to :class:`BitStream` if you won't need to modify any bits. One anti-pattern to watch out for is using ``+=`` on a :class:`Bits` object. For example, don't do this:: s = Bits() for i in range(1000): s += '0xab' Now this is inefficient for a few reasons, but the one I'm highlighting is that as the immutable bitstring doesn't have an ``__iadd__`` special method the ordinary ``__add__`` gets used instead. In other words ``s += '0xab'`` gets converted to ``s = s + '0xab'``, which creates a new :class:`Bits` from the old on every iteration. This isn't what you'd want or possibly expect. If ``s`` had been a :class:`BitArray` then the addition would have been done in-place, and have been much more efficient. Another problem is that the string ``0xab`` needs to be converted to a bitstring on every iteration. There are cacheing mechanisms that will make this faster after the first time, but if there is a constant conversion happening in a loop like this it is better to hoist it out of the loop by declaring ``ab = Bits('0xab')`` first and then adding this object instead of the string. Use dedicated functions for bit setting and checking ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you need to set or check individual bits then there are special functions for this. For example one way to set bits would be:: s = BitArray(1000) for p in [14, 34, 501]: s[p] = '0b1' This creates a 1000 bit bitstring and sets three of the bits to '1'. Unfortunately the crucial line spends most of its time creating a new bitstring from the '0b1' string. You could make it slightly quicker by using ``s[p] = True``, but it is much faster (and I mean at least an order of magnitude) to use the :meth:`~BitArray.set` method:: s = BitArray(1000) s.set(True, [14, 34, 501]) As well as :meth:`~BitArray.set` and :meth:`~BitArray.invert` there are also checking methods :meth:`~Bits.all` and :meth:`~Bits.any`. So rather than using :: if s[100] and s[200]: do_something() it's better to say :: if s.all(True, (100, 200)): do_something() If the pattern of setting or getting can be expressed as a ``range`` then it is much faster to pass in the range object so that it can be used to optimize the pattern. For example, instead of :: for i in range(0, len(s), 2): s.set(True, i) you should just write :: s.set(True, range(0, len(s), 2))bitstring-bitstring-4.2.3/doc/python-logo.png000066400000000000000000002431541462444752600213110ustar00rootroot00000000000000PNG  IHDRY]Ɇ&sBIT|d pHYs B4tEXtCreation Time06/05/04Mq%tEXtSoftwareMacromedia Fireworks MX 2004vprVWx헿o@ǟ\'9`Ab1 lLH "2  ?`r1ld@:Vwq\EBc9ۯ}|v6Z+ s]T,tc J+GUcQ̕ʋ2/TP( #COϿ?#8={pK޴} k<%3}D ZT&Gr"϶׿uX:$DF%~閟$ )y7H& 6SaSbA:DyɃ`B; G/s|F5.`1R/pM svQ33SO3_p) 'G,rqe^r o-'ˑ"ӊyFYpFƵHt\1#}")ԧDZ3^Mv$}T1o yX?cK<{Էaߍ%O<O5jNv FW,"sѥ[IX6xW>+ 6qx1=y|n݅|ΎY8=nɛZϓ@](ꃾ*dOSwVt19>@c&pôgǙh# gO&|{atWƫYgx[˛9\;f+Mhd&Qxhg3T*d=bO6N}} \C8co4נ\Zt;’T7MMGӆ˵N$񰰿ձ_hnI{+qnA^oͯjYкGCw$<\89G"Y:~ҎԦCp.Oz;.fiPCb!{a>:رPlMCŦb?lkpPcܯܭO!4:]a839X&VkbŚX&Xf]P;df/4y4i? W ׃ ɾ]E}?UC\q+qCZ=>l.<4Pw7^HkVYgs3f6( H79VJ^Nh%u8JbnE8)99{-fy2ݏfdܙw :+e#Ź%񬰂 vX֕A? l˜Q+#hX$U-ښB1e 3r0dKcŵ,`C3 ۢt %ډJ#ƹXۼvW%i$_]^Uyq粯3 ζ|. y-tԝE\JGl[ldNiɟ۹.\ v%%1hHM|(SK0<ћ34~3FuxGZM0}kÝڄϡq5hl\٢MAEXcѷ 8a5]9sm8K3ue%o6QMo[pc5U q G4Gџ(i+F `p(T*aAU[џ0S ٔ)v抝23^T/Eb(+2`ЉT)׺yh&I=NЋr" 0WI nD{Q {h+GmR2PhIbR2z7e6~n[̹˙сWR[ 5tqBqPBhMρbh@C&/$R3;iG: "'xä7{$Cbc u0)t6M.DĶpI¢Ѩ?H>x꿊;^MqݔhDsmDww+FSiGRM`t`Su \P:ٜ(vq+<:/{Sf IqƥKW>>|ljͺYbNu>MgS:DI>kfiswȭސ.ݡfv>SQ0s XEx`R[Tp|'WґTE;sBm11; u*iɔW. >*e:v HpG^C|GVʲO湜hyϣtmɼ9>[,>h2{r= E䵌dGo̰l[lYfygaWsKzI甏c?p'oȎLgʩ|Hz*A71m9&O/&726Qe++MZ tH9+q,n Jn " VYX0ٌzbj80HG'Ucu *yN#ut)"reiq&'UCo*x6 P7"AFЊeD}a`a93^,`yf1MΙ}4/w7(GtX_M+uBA2&wޟVWN2aw\Y4$7 x)®b vUwI7{#o98PSkվj^<|:R:ZKk~.bzɌkIn$7*IFGa#E\fxQ*It;jb/n k^l5A0LNĐW̹ Xv1EQ4r<}EtYT-ZNjɉ^,CU6fUS*}6h:Qe)ĭB``[&OyB`/fdDHmd|8Ey9U,qZ㞜&Ǥ fx*d4f#nU%'yJ  J F1iR:a֨^_R| \ (49I&NfqxVr2ITL$>N8 n4R"\_"Uu*_mCsYŚ.Ccs*A] Ue }{'LZprЋ& ?M|(!i$CCi#xDWjFO_}Q]ib ጸ5H\h爮j/jqkb4z'1hEbt.I&SzOaKVv:1I+6A??]T!Hy~o2y$@N`Ǝ}/4bG4at:}|:4aԷZSÓ/Hm[8w/ȥވOGjtg[mT4+Ѩ(ird=A?yʫIDG4ڄ#8X/s)\G;SK Z*[9CߧK(rwri\u/+wiSoUx&= 0++(4-?J)bϝO0O^'M%mUrnJi**jX9-y!TG)r瞜z_r,WqR#,k*K?'Zsr?{ha}i)L*ǒ m˾Wv˅c ߓlZ0e(m.F.$q?eٟBm1vRC-)Si?}y.VQc'oOv-DFbktF|@wRk^#EdkGF.֮zW~9xϮ1{7ט}0#K txkk&Q5B#4[#B{gޕ5@gF+wktwkt^z< /syF5BA˘yF5Bgޭzгjͼ[c3֘Y}3̻5F1M2fޭzwk^#@ W)B/kUhmja:eKAZd=g]WmY wzoΥ3ZRHu+ؾֵZ⌡.Oײκ]X>VE77zL>)KGљh<^XmeǢy1%mߔX B@DzUA_%Fv5F1z_Y|ܐի^TmY͵ a4ݠxLіLD4q0%tXQ[h@>~biUN&c‡kb׌X}I+_ɲ5pWm ZIʸ3w-C+6~^fmiρ{oF^͈nUb7oLIWr71J?ʵ*eׁCGRb);'>O [r,}k|Onzô>hJ;ԚHI=tF8A: \F-W6/}&J5JQzk(;~eW,dո[х u_;/B͓cNo/佣"7g粴g+3̮9GdSfQE#\5|Tp:caVo.P~I \nRAY0;=#Rr,I#Wz(_v7OX`"9=2Kcm|Qb\G~u1kb2CV[7ܽ'99T;V|u5杌FUuz,'>9[@&KF:x1^{֓I.EDG_Kvsݤ[%EN:CxzSFY>HI~ag-L=V o8֘OPT2.GM+Y")W6(Dk]Fd1O넣u8Y[h{v5MFR_3kUVogK\>~Ws$B_l[.c´ff>efɹWY5R+эʊ#u(1wÍ$b9PFRFL_!רyA;_}c|<R=V!j5Bd~$e03q{3e26ǿ7s٣_v-UW<ì^je2箞L~1XjٵXpTj59Ns={x{)r\a{IA앦w+ +YD~U;jݢnQw]pxoŬXW _{Wm*XXyvׇv^x#yk od &1s]WS/F\>FdQ]Ga#^j5/sH\-pk,yX>"kɧ{}H_tw_ȑ6*k\O<>3=sߒ|}/hجxtu!;/eY_c3l{$QN$>݅{都(O] ;$>ݓLaýݧOP{ ~3`CG[ܔ#GbfQM7LM(oh??T&@!vpATŃe=[a?|yx7_c+(yCԙN14g-iGp!=dCG*5a>K?DQ>"!yDWuʎR䜐|=P~+u{ 2DhRhR='T 1KIByV')^,SzIJRt ZR鑔YH45)T{4Td8$su, D.T{T)I)Jq/fx:L r4 &RG4i] 61gmuaY!XHYd Q'VqGq}G- Hm#g$ @>P~{J}D?ffEٴ$,h5`Oy|U JQBNGmm @ 1O=ZI>mAOb_ AHkwhq'@ )L+>0*tC3"CUp6!pVutr zje=8F)xVtYpB BB^bvKniN ^PK^C+Mx \y0v1KD%:$Z%F-x Ujj%ޘȯ(D^- D?&jRPPxF E7.k`(&J1Q]C1Q⍉/%h:VxFC`o[C/p/^+qkLrtΪC9{|'3ݺ+ӫ[wL灴ҽ=̊l!{LmWÖLmm}#ۏ4喷GRɸGdr[E}W)z.2*<&vS$.AKՁMʙ'3W1XݦwroDBT(O}\vpa*\&VA2:Q&TE ++ΕʬW;$SnfI:1.A'!H(2`!mc܆zHƭjaE<\ {4H s3 != r,mpYIΡ0Az> 9SX[@cֳ7x|/xƧ{1Aًw8C|ݡp&Tz _2NJ'ѵ0~^M{fCj6YۀƇbh;kl&`6J8OS{7:77McJ$OAB,a:w8r9DcX% !WK{<&h?>i+ 4yen֚#oN<^O.`D#f;/M@m29yb@H f!8 M?gA"WО720a.$at=<I,:CAM,]$-(a\ E2 cyzrN5: ?' 9o^l,[9! nm޾Fh홋ؼV"[Fh놟ঔ7dㆦ7uneVA}eY _Jh*_oIeE|5%-MLem`pZ FpHšשyW~pNI+kz")lo%;_˖,^:q (/St T&ziF튯%c}'Q*wy\ѩ~f"9X; Mc7z!7*B0|ѢKQܠG>3C$]שnc_:m,46lKNgޗoB4yy߬Fzz5P;ow(/ Yx YZ kjƿ|V;$4NqSHԭy&ٟ;Ư^j>peC]VnZutN{TO|o,ۉź8oQ"Jw~#5x"T{Ro ѥE-}ViU<GJDž8 Lt.nk6O/:eP[b@w!X{?72K@ޖTs]ꠈ-6 S=s:{~8д͎n7KΡ޶O4[Ѧn%\:'jM?յ>$jtK'(ikeTl#9Hpo##0}=XھLaޡ]Uҽ㾅sǨryjكg3g"}TU"e-bYjT>rF==쇨z G[,WJE+u$T:<)tD`+f-)؂ la|S lW j? iH6:V3>+gƇ/MTdRFj⇔=*Yv[Ȋ%,5-,˿oٽYvӆyD}2-?gp`;HG,{od^Weᰄu8WܭSmMO RoԹkLRzJj[)cyue7.,$6R#mlt=DVPzKW3XrҜqjM[ mV9KO ;9y@ 4fIs=ehYjݞ-E&N]|ݰXע^f2{8J&KEsW_,/˖/,[6YM-/W?xWcWg\-e\]4q@Z(M4J+~ڼZnGiu(MW|s!j6_WYu\gĕìz0Wo#1ڻxև- 2 2 2 2*+z%~gɬ>/m֝-~U,~֢DtK{,)Lg6=vV6o]h,nsa9)1m,mpھ=zc|Y7;1WQTM?VN 4j -hJG94 ?-翃^~?X4 ]yc5>DI˿Z2zޛvś0wP\ٱHj7=7;PKȖiM^2(2={9%Sjg槵6v~5棻)0.CYۓi*"|YϞZl5)tٺ5Fe>r:u#GwZlvd%ݞv5YUX7ۗ BܼY崱)Wxteٮu pRWW,fGxEh]5V>45Xs֞ ~Wub;㫖ӊ\;QAtU _`\/OX.cQ\<^2@ݫ3o,&Ɨ3qQZso./ JtOs1*ݜ+Kx۲.Sǹ=o z|s u&d]ڼԿzytZQ]MΝ72 ]r ]2+ ](˃Z΃ !BohTz= ?*EQahFyuT:oTZ]VW\ :Z|f˸6k 㫖ӊjrl\\Ƣ%ڭOY,(vSֹ*eyﴈƗ3qt\./&醟X2nbTz93/?*]N_-_s=s?o~ѾEͻY Mț_Zϛϣӊ+e[yC- >ߍ"X"9;k(wݚbF䓘(Ͽ貥S_Edv3-mFc˖"+J;exH?F7SZEg_q9?E[/6m}ol 8󣴥=EDjWDDZ[z\~7QsѼJr^v̹ZUSSր7E˵yB˷xXˈg|ˎxڍ :"YdFu/.F`KƋ ,k@fuڋW݌H]],є~7 3:3."|2{}8zi錿R fF->QfyY}w*5{( ,\}0ֿlcfmـ eu*58:nJDymRƈ.o2~\"3:躙9HGYtP%ɿ4H`L;z%XWWxȺׯ$.ZT n{k6#0Ռyd O d$|;k{p #_&|Vl&7V.bF8h7, oZDFD &Ӣ{筊˿ooy~־5?OnʌMRa\.i=mzJ5e5,;VW[7՞Nۙν*Ad\wڱvS?ٕc:}4]=|\Kjt+mLWE[}3^UXz"{R0bf^GwT'gv,i+o/+f}JC-W(ƂTƂfS,ĽVJSYTpU*u(S^x]1г?_@r|莅骄%Jj')J8[r (z.OPz&O= M5t)y֟TRj~YԙzWRᕍ%r(gV@cW}L$fАoAgfiЌf\ODчR{K%Q) Ҭ(SD6V(%1K\XO`m³ $=(Z$B YK":9<R%l9WK{<&VXIVyx8 EZp Aв}bDXi+I6OR$Dd1Kا6DdY3LDA5c% C &"=NAeXG9`!!qmt` Bmc0v逾$—쐸4u Fj0ӫQ0 Ç8 ohyЁ[! iѼtBMA#ye,p?ab[,1l8rچՇ@%ihʒ\%l BYOr-g":HJMfyF@QjU$^`a-rĉPC!>,3a:N8nr֭p> ۫/^-G36ddCjo#I_{ @D#gjG6?AD#CF{8:QiG68 9ɦֱP8do:ͧM;2woY$z{ю{OaY?DqO\8z% |7rg-V<@?C-˲~e= :GgHُgq=(gOtjr$T#.s)\G;SB&MRoə&=R7rwr4zp/+qܩ~oeb{@׫ z4f牻ZIr*}NOO Sbki2X9*)jXi9sʸuy!gդ;V޹w88q> 4]vW%S??O[z5θ-x>|X4q8y_l ǥB['lJk6k^]vzn]*@)+ܧéکԻFA$}F}uUעme\8JK|.CI6ƨ/kUh^Kȹ92Yh{+ ./cbcy 2qBnÞ]]Ȏ#4mzfI8uXM4L}EPyQ%Kl~$bK=gC;]rk<Ȇt\2@ܔ4j u'bCosT>bC:*Z u~"P{IݢSldBm2蠖6T9cju*789ڨAuP[ Rc)5*l̠tm^}L^ذ-;y_JI mn}nըls(KD/ Yx YZ 9u12SpQk"Ͳ[\R0uou@{to͢f1obu) g&( & 4 y4\uI5 oq4:]4%jeԂ^$c6$mi0<+vU_]ņS6.;2[>pjWV}౯p{em\x4MJ|0{"Ϥ(1l@rSjU6{ʽ:`/U:-lhSCM[Vki%o.7 գisaY[#ry6*sqBvcT<\Q4UΜgVQMFz`#ﴙZsȷ!p5nЋn2`of()lygγ"Ѧer٣EgQH@i_|S/3>s4H~2&|'7fi̫ d@І:7Dm+j.Ahgmhn?, CBK"`Vق- lƇ:. |E᎜cS>*m5㳂ɮz@k|_lŮRFjGYWPʬbe7:.o9gOƄ2;~eٽYv-헢5M5o|x堔v/\kֹkE))OoN$ATTtB.5۵+U}%S3aUUi >ۼiͧG}Y uu{="ܖZ;]vkuVc]bk7{Ue*u,U1^-\L^X/[Xfܳ\og6 W_W7]Oq#nze rDj/UՕ 2 2 2 2*+z%~gɬ>/m֝-~U,~֢DtK{,)Lg6=vV6o]h,nsa9)1m,mpھ=zc|Y7;1WQTM?VN 4j -hJG94 ?-翃^~?X4 ]yc5>DI˿Z2zޛvś0wP\ٱHj7=7;PKȖiM^2(2={9%Sjg槵6v~5棻)0.CYۓi*"|YϞZl5)tٺ5Fe>r:u#GwZlvd%ݞv5YUX7ۗ BܼY崱)Wxteٮu pRWW,fGxEh]5V>45Xs֞ ~Wub;㫖ӊ\;QAtU _`\/OX.cQ\<^2@ݫ3o,&Ɨ3qQZso./ JtOs1*ݜ+Kx۲.Sǹ=o z|s u&d]ڼԿzytZQ]MΝ72 ]r ]2+ ](˃Z΃ !BohTz= ?*EQahFyuT:oTZ]VW\ :Z|f˸6k 㫖ӊjrl\\Ƣ%ڭOY,(vSֹ*eyﴈƗ3qt\./&醟X2nbTz93/?*]N_-_s=s?o~ѾEͻY Mț_Zϛϣӊ+e[yC- >ߍ"X"9;k(wݚbF䓘(Ͽ貥S_Edv3-mFc˖"+J;exH?F7SZEg_q9?E[/6m}ol 8󣴥=EDjWDDZ[z\~7QsѼJr^v̹ZUSSր7E˵yB˷xXˈg|ˎxڍ :"YdFu/.F`KƋ ,k@fuڋW݌H]],є~7 3:3."|2{}8zi錿R fF->QfyY}w*5{( ,\}0ֿlcfmـ eu*58:nJDymRƈ.o2~\"3:躙9HGYtP%ɿ4H`L;z%XWWxȺׯ$.ZT n{k6#0Ռyd O d$|;k{p #_&|Vl&7V.bF8h7, oZDFD &Ӣ{筊˿ooy~־5?OnʌMRa\.i=mzJ5e5,;VW[7՞Nۙν*Ad\wڱvS?ٕc:}4]=|\Kjt+mLWE[}3^UXz"{R0bf^GwT'gv,i+o/+f}JC-W(ƂTƂfS,ĽVJSYTpU*u(S^x]1г?_@r|莅骄%Jj')J8[r (z.OPz&O= M5t)y֟TRj~YԙzWRᕍ%r(gV@cW}L$fАoAgß$ecxy7dtO+19r}?T%'HFYY*)KM2(?5Tp r wrf<Hz<.Xe\UEND?8|%}`yqgϷH=9xϣ,OC|pG=!=p|؜܇?wQ['tWJ8~H pok)*^G{q>ir;šە9_P=2=ؑk)[U?Z&p=Y<@//;d'1|tw9[XD(Nt)(,@3?36Ea#>*g侀]uv }F[qGpP[J{41CY(;St{6kϡ$fY@pE%4$1>Oc E*$qpB 2!iO˴r| `i cq2x$>9jERVJyJEVqK׸L"z KU9Ze!xZ<YNuD/% ĮM2j3d~ĆW9U+ k$< U@PЭ)A -  Rj${9eyIi$=x.BsK2FrzcT"Ƞ: .h ;MurPט UJimHUUP(~"14VH&.6KؤM M( +)I +DDal*q^WIf6xYU&Oj8h}4Sc<-x@^ e V&IdjC La Wܬ0Nrq=e4g93c0t'qB `0Fzk?c$=tJ=`D<~$V5{=t9az_MNzr1jr ?-1N( { p (;HDplk: T *ZT'PA .=Tn4cP=t9KaOKӔN!@ibT 6!@ ZF1D3 J`#|n(9:}-{8Y, 2,Ktlك{^`ˆ#,D)0+H~L{qQы P֢YCNS[]8tlE&l24$IA'.œL0Q[<8QƒN a!*Pnp,YXj 2 Ls.X `5a}C[ArBh1419'@~_C9&"tKpD.3бbf4E*Q`7p6(1hq`#&c!LN¢ V@A ӰqUh. ¡#5jN v„`fXCifABB[5"y]/mxdkɍY->H$A;8Z~'I1bRnElSd0ۮ:qtʞu$O=xD]sxPڠШ+{c)ȞRue-6YnSg/=ט&ޱ]r@ 2({b?& 583M@"J^Y<<9,&ȝr] P0(ιId1EtbWݤ$D :N'=pMu 2IQ вEuR`<-8Dd,Y֍9 f֒\/pt=|Ү9.GbMpR?S\,qB.\ie1:,8?O).q|wV͜9)PeH%rOw7%E!ɒKyԖ-|q1X+sI9 )FJ$WGe=Ahe2-åP%n$$M"0ihQ'!\L77܃ HXblD&עj଄4kt؄@XïyH;4!$MB%C7]: lp.bmn&KG kAfIxS/`0.pZD\f-|L$aY. tOd29r ֡lsD&-L^Jrb-mY1/5 fm7 t 6KMo$Oqk^51U+F41t1Nq\4|l/ Z#:l]3.`-l5bdGb$7 AhQ#\@H er4GZk<)F4%8~(l1qx⮛l:1_N5q{b\TPtnZ fJBKy85!8Pa&6µq֢iNj $J?I[HܖQd^%K"Y Z :IVZ.`bՒIT y,I@gIķ$bN(CJ'hC ^O\Be`xZI|dޠM`Cr.i^ג!f $ fsZL5aRY8aپBlwb;bw:<[^2tDu4q+8~@p":-$Kz6A8 3=AM\&18  Tq;]f,TH(A2C93 8T0SMe2K}bGq# T;lPafbގX"18 m7](n9θ6bbA3ny}q/ r rLm_tg: u(bG (rJҜve0EiQ=\ l"e8ɶAPF-x(yeC& w ~4kT;8oSJϸl̰ǭp^ne.4$+dY=.3r, MS‹y4q!h;< ˬ=iĨE*&Ap3MqO^"^bCP &9vۓ&fzjPXx :ތq1?.YwsTR`)]-؁x?4?!\:8 mVLJ\2-T悃d^`%8[LKFө(j8*^;e|7&m9LS^,LyH[.0ȕH(m-|^,`YEN. ءm\T@l3ܱ/& +0O bK4UCNSJ_΀`IwOs--ЭpV:bn>.[LǥaY]8p'б'h &۲pz"He,6FiCtzCoی74E&45Eb [r ɕnFrjfHRƕ'%3GC2Xs #aMG$b>t#p0M'ŭmmb@d l:y/#H&xĤ8Rtg Ϗ̒ئ{)E#[)%uv^L[|NQ헳?q3 V+ꅌҏAYJ1Ñ~Rrqdl4eyVم\s[;X&$R ޵%.qprn3-N-JBG ;' ~/>ԣܼ+KqO^&h1Aw3ʂGEPq#Pr~l TZHj$Ky::NhRAp51v'o=,D>9'1J ы^Lyen6A)'-/I/=jN)>ƖAoܥNt"tY) G8N'!1)z|ơ\qyZ\op|Iqތ7xIlCs1 @TrA#s~2zX$8Wc ùٻ2ݠ 2{ҢǸr߽iA tZ֚2W׍ x$. /Dg|6 =91/0+t[aMm!.u92䣎s[<< @hЖ;,ibFGH7b@pN+&g j@ +4(!5bnB VSs1rLRuwZ(1c~9˸t__@W C *붽 V;R'pߨ Q V I MH.E%JÙW h@لb3J7c̄[imT<%^QpىMZS a-5{X_D&Ǥd,iDӉ@n6 bT6h-%ซ /W \%! 2Ѕn\6I}^p*@p&vU@ރI1.15UWfK#֤-vpT?foEf-vy^#B7ՅR;ԅ2ܛnB oMx7u|7CPƛ{3ލ](ͽx.ތwS4}qo) %A/̅r!ܗ aL@/gIOŘO\B)/]ǵ-<:&S M\MhKE avB<[Mhg$4ywKqs\d04]-wei08%S/c-?N`=W34I&}& '#E `Z`.E(&vX7S&nokH;,[j'GWD/~#&H",ō̙CVV0ߝ_uWuWu>n;zNʬ|eep'䎖2AȪ]*-2H%$^ǟE~Nt\1rrM>9Y[z]D 1dB)[OYr3c5p2D"dXK˙BAnTv\;ږY\d&h1+TL(GoNBQ 0vY-(1vTEٲ[ c BA4d4=͠":;\"e|(;KHfv+zCmC2GF,,NxUjwN\g騽-mIcjU7eYqRE ^Zlސo|78[]ƍb[63*0f8*ٔp0v{S? ӟz.wMBe+0ܺ@ټBmeXg'#JEK`tjSѺ׏ea _űKfl@5FiѲ1-y;ž;]`eL3D{MF({ T繢OKpmK״J]"2z\Zމ Cq>u!HQF[Ϸ9 E-VZVk>'SIIx]ׁ58gr$OM+HQa\Nhv Dk]*F\+HSR(Vڱt;$N.\>^Ve$059 UfH`XV_N hjod MnJO0>8X ɩ@bƖr0" /dAI!Pd 0Kٚ **q,ߊgi[f@Of/ԛ )`T &8q%=Wr8_q5=Ws# )q!vjo4懲MjclX r;97RPz/\=ESXdAo'Vc M0!ubT2>U||SUhPCxc`-x+GtlM L|VѺ"A]քa)Zߵ]n QZ[A a=WOӳO>i6_0Y'CA/  @+K[nb:a]и#uHm+>E BzЦM kh/ {L#s3`1•Alt9EAlv Ζ]] jR5E0b/60AF.xzm׼C_BZF#\sHd1%(GY6 {?B8I$w=c-g1U\<9/a|(WZ|ܑ/dz׽:_Ryf@7 y*ov٬G[oD-g1Ɂ B{7fQ&( !\@\1&@b )84[cvS͛->MZ\`TWP.f x'G wwh@CUt!9C=FFa`sFρ`SFρ`Ba`R Qt\ĸ_v@vh}k<2 a_o6EKthw%SBj9 EkTg:b0ޘH?sGr?7MTX`!zXС2_t`7DKvxfD #EZFfc3*fDƂ:4Z/ :қSHPN $qM -0bV6bMw#BvFVx/U1 }TLj[z_!><"Nu8x>"n pK! &tf8Kks~߂K*#5O02 @DF(N½~]cA!ŀ;eeAdz7c(3a( (a5lAiuTIU .92*L-G+m(\6G% <"DZ'Z5`eЈAQI%_h@k \PfjY 2Q`~K[תy2|hah`+tSx9_ASFW n"Ha3*ODtٻ .0ƨ  >VJL#)42F׊Iߖ}!B24/}ԧTQ#Kc_}F=#E*o3fDUy> S`W˘qH T@a:GG4g~5sU|PK>rX2}d#xdeg"u(6r nl h 6J G(c, t^  Fx+9`@fz pb&gnX3ah;1x/:&ajV<3ҙ#nͤE(8ya1LnCHp&O xyV l+<{YC ').SyxOMbMv.0k^`羓ОOdmT#9$?(f荭x>s7+j35i]8xD yhH߉✂Nr5lhxL/`"s TkOmx%Lb:( ce28H0<&`I r9Kgeb!ᴴ 2ɓ/Tr)*wf$2x wdVFc9Cl(CIRfq|P8-߀Za`B5.ð Ŕ#0h&5M>Qeq;>M>CJH6 GZ19t*U0kV$$],XNn20V8m|` OS2J3^A[~74M)b+fL-oKfXT PLveM+:ht"}h]Saň8=Á I1"倳E A/ۉFb-(uZ( l3 ɹ,*3+h.%gԐYst-GmR[swX]9Vrhy?b/՛Q[A>ƊVMIU҆*GX ЬdYeyAH[nCgy6ePvưY]9nF0zl_#[3G[s4W-#&#:xcdGo{8BAL[. cQƌbD?NR{k$u}zS_+ MR'0"' jL}a1'PbAD_3n&>/} HTc$-h";{-7kC֑w3ɧW}CEZ/P TWRcNu8N* ,Hcv#w4\H-qJYAH艉NHe"fDj-중+bXqվ,W">zIW YzIJj6*yC7R\Wf[1ds ToDoTe [9A THSz*/r[9]V"Ƒl5d⽶)0MO7bRZVOx%h*]ۉ mQzW XkE'r*kNb6,yTԡ㈷d79x1^AcV Y-A>(>~C!?Ria,EYa]tO@)ˊZ9-?\t@7| WGn{L%0cG]~nT {oJ24%A '@X ?*zv?~Tsqx(HNBTaGAtsdщ2y=Er0*.Q]1 vkL]1VM ?7ݨ79DAxB)9t>\TaXW9#\Ȝ T֏UhE3˽/<Ȏu E yտ\888W^BӉ.AR콠q~Pޛr@"ZKن_'#@]}~,q^ x!T}U+${WWZYM-d҃X|E3~ܠ; i͂ᤑݤ^M w|VDrL/܄s"+&߷MߥH.63oQڰT. No8\rkx@VaRZ<))"f[[y8pwrѺL[˄3M掷_~/m P<n@' xoG3}T ,w%#8y2D>IKiF8vuγgGN0DŽ=MZ^(j\QګVRA$ CDdưiI+y@$)|p4{{ $AM,ޏghWMSexc;5--mn5> `!KNyTp2Q)5:ՂHaM(O\yuS \|T:K75,BT4uXUe .`ɲǀ pJ4tA+۲&}C{ӣR$8F ގJ#u*;,VNpI~̍=EAv̍p|g l#Hk}5IBE ޶r2dlK)zFr38ٝSI>;1ed\0bfE3&0ȡg $9fbfGڌbL1@Grj3dϑBNmF`7.tl-Ǝ`rN>Hrd+>WB bQ^)֐Ɛ}چ̽f ۧ1$t\@C1$M 1 &z, Ԇ$$" xnϦIN-V9(z(=_eA^l5C',tȌrk]}hUr犔wBb`5"VM͆m@H5XtAHGz |VQc[G ˛ Ywc0!\{ ߪBomjL FWFg qEI( H+*S A%lYD%23 mu୕GcnUi2EA(QQ}x8]ޅ3X1M_΅6_Z-)EguaϺ1#wKV̡b>!a(>_iAbz96ƉL ׁs)ɢ'V`<˯ 礲nL]Q;y3B4`Eo^M3&aFL0 1X<+]w^6eUs$C`FU+EQZu[vԚ\=ӰhNbu?jҦYJ$6Q#&HZAOL&Or #xHr<G1?qU 4`'04Oa9g\X 5C-v1: Ak0\0WiC{adi[@NU9:k񳖼MGĠY[~ccet$zpzyZ0r A1ԙ5 V'H`  CX*[C- }A"Ӏr:a# l|_O -8 6@`z@ Jg }DPX |,x1Azx1yFB%$lYeF=u 6)i'X~gH`8WUx# dv"@O'~EEK uOHEkۓXj;4I Fi( (`FF"YEagkNC5P` =`GCf_{tPh(H\%.CsVܖnXlNZxLP`T 󥀻<5 \9 \8 \8 p dE>ow⬍|3` P5z( =Z[c|` dU+Ώ!s\kd|p\b}OEK:Z$6PZ,FGLj 9g!.Y"FL(< ɬXck&ָXyLipif17bLMát YiÈwyBy5iamt +MVIǡ8:[xFz% u+ OA EP1 B656^!w-;>91PP^UJׅ05Ԙh.VPW_:%i@E%p륭A0i %hiK7N}}cW?n~q5{~9O?X[`ʿq2zT!M GP$}zfZф~w ZU tjX\1|,A F'Q& @\) 1ѿ˳+hGW><5VV}F?E|vΒ܍~COr>}M}"; OzrG=Ʊq󍢾yW>l۝BN٥Z%yiM\B`tѐxte=h.tlĸF#eX2±>!+>*Y&99р^!Il{M)-j Bd'& Ƈ0|Vr?& ס3:YQ. 9mO"$Ƌolk_:HIe<ɩB2DZC)tÂ$hMs&'VwV<АQd/)x.-4!dɸԌ@r]:f0E{6#?,U4&!yi N;@AMg*@ǾvF*8pSٮK0x/Wzw7T@-Q]7r:BS>Mu!magާG*Y]:B= t,nrt3Nv9 W0MvW;XҙgДgf0xQ)ZB\'G]G^/F^ևF3_N6g~v&bᄐ}_}*o_ďL^^^CbR~.~K|>/}5뗽@_˯O4EcګTV}ʩJQ3Y*@Y*^e࿞?\=^K??/Si7ܤk ӏ/_>*^@WT~:<~/߼gpueo;ot_u'gx`U݋o? ŷ:Ql+EzԿ*GBm'΋13S=`i< 9qTƞNDus|\kQOa^&7|G xM; y{Q EuaVH70,RAʇ<|TB:#=yB,llڄl HZy]_nokMCt;}E@$8tAަe5 O9ssp')U6u*;T>z2ZO b%˲+fZ\dqoz~+>4adɰ``HK~r7HqFD6;CzXMM%ʊߓܗ7oSV;|u/ߦT1)ɷoW6_]  IpHջ loG+EO׋Vsd iNs}Xh1 :PjikPyt|B*wp~~Z,S+!_ ~HWO@Sc/$~o|gxo?#>^?{2V7~^ߥ='I{(mהmoҾ[y*},Wo5LŻ/jd^}~՛ݏzG۹'C{woҶ1oL %}V/t|IM.7m\ovK^=xޔx^'30I |>Sus\iܔ+k.zk LD*em?&SHE>}Leq<)BMɕE#bhaygyA۽j-z~P81]L>Aӝ&ClvF_6sƧ:=*^ tp=>}/|w˥?\WWZfk뿡VK_ܕأX V]5)s@W_өշ{&oX'w:яvI?JqU1Njf;'_l@>2J8_ڝA'Fׯ7Jc]]ϝ{EJG^Նv֪3cQj$jEN,P6Ś.*'zHz=OV\dyVϒ7;y_+0z5 ݲ>ױ~/@bˉsz _t' Ufbkf7p3sizJ<ӏ>g+@*ǔ6];__x=ES[z!o^\Yr^#F"O'ϧ>%C: `>|ګ||fzk8;\6VY!hit_TgaLIV$4lo(6#rs9ٌ>W`F;cߩٗmoBdkc{{{oCC>q!|F볾1$;e!=@& 3JTԑ1d+ kkns*b1eb"Y/x NtbuҢTڮRiM F39* Lr&dwvПxFb)KKe~G@[eX[iw+]A+^4.Jk/^{Ӫ?R=̃Ww>0x4942#]3IP 4y--⦻_Z*+nk,Rޭ65CB`f>nEՀ2~^ߴWwBK4 }>؊P k S7 $ 0]$ӣ^\#+n58$O*iQ y4 j]Z1lCO6I9Zf]^^,+oMӗ5]Fi3cM!0"ж?ۘ/.i鋚ui鐧iWS> y7d\0&ȗː>,N]l[2H nHUhU}tVхT dt.ʦEcƩ4'3}q2?iX*+nSMWQ Z -]|t-oXFF`/4%_Z㛎_Bǟt-ZTF%|q)J7%ߔd%ʫv$mio2i ].Wn+7@2U}x2B T.;#X^,+)ʊ;H+wT&v4Jq4ԟs<&]tkGU :֌^ƿ318㙫Qkڱs{>jllUr}WON|I/K=̫;=^8h'ߒb9)&w$ aS-uWɏO#ld_7o7i2$mOz2%ȎGC9IYŵ2-avRYqnul3 4K1 T>k"i@E6gNtsai4l^PJk津4diG+ϕ,B"gHp&Lfj y2OsW A “7x,v+ 7T=^fVyAy 008oSS RP}ɗ]WX^E n.f!>lz [C z >q{kr.cX+ -Cl aUO8^0q{8މDZߘ[$^0X;7NJðXV@/{kw9=_{q[/hɌqeywFɝ qhNŕf|ѣ=vb3h >8WRtعYPDH5vv&QeD.4NvfhG;$"6sFfQlԯ٩_O_3jP?v; sG$ᤘR[̇ fN19eg*[p\hɵF7gC 9⻣`S[/ndMDY/s{<~<_gU3ƾ[3S[ŭ[#tey)f(v9ocU/ﬠH(.TNi%^qL{:)bixF=_JZElj9&w1' SKG81e>2 Ϲ@Yb]躮 5Z`r3 ãw=6C,t aP짧6IB5{=1}eꘝYGFM5?j 3 KU`0:rttZZqD>Iifr T,bNP3IZsb" V?kdmzecgjҗ{[zM?ދ{s:X罚.P`{HZ:;GzXmM[6+YZ}ך/l{V@oV` ǽrV@B̚P#1"1ܼEhVyT&`iU6['+lMOU<):u T; =Ol]Z4LRk-a,-jR<5 %N(zة>#Hz֙V㛱"V#\V_o 4ߤȼ\cv.$>O?Y2"r#&7{Xq3b-$h+-g 9kjhdNĝsʰME"\1 k8*1b,I& L̉:l%c1'7+Yz?>N-) l cv/3K'%s2=$ Woq 8C΢ʊ;ٻ{D2OTNI=knrwA0) JN "Zu 7/4RgQC]6$X j* c^WLŁŲ6se.9ũƼƩÉKًAE⎷HZ|ګ /G1;Em{>ЗHVD"<{*t+Vt?TVf6+p0<>m+:>j0v]!t3Dnbbᄂݗow^~;ͯH^oB4.^fKi Ȑ> qM4髆/J+?vxH'5c|xC{_w߿E۫uȓ~ dpt톮яgi  F>##'/u%g$}~7V|3x6(]]3mkBSx]0{5|Q_V ؄&MfЃ{&;;ujh>u5z/LcFT яjVc{AZڔ^p u9ZҀ ن؛=ؓmOM6s[ YN\f_sgzUN wRm#%+% cVC~:LBL :\:mmkBT}rxԱ 0 [~JPAѐV^N!dno;zMK@? mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 %mkBT4xԱ 0Ќ6{P$ }6aF3gɖPzɧ]Rؼ<U]#7ECQ{_o^1#,!9oT27%{uhK;;0~-{؞wͱjYmkBT%x@`:v!{q^9e%@ H.# :XJ 0AHJ|Ogȿ=σie0< ~&Ѷ/J}+oئQsoJE]ilnc6Ȧe1~]o8n[}p],gel.ٴ?D-ѯ{ZC,I.z(ZQg) Mb_G.~3?y;~KeqbT쳎rZ}&Gksy;tvLs\?lƱs_U?16߱_"\9b]yOa<ȳ/r2Il2׫.2f} wZ~CZh;{byz\۾)9'qyq^dzŠ~}GQ?? >M-mkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxA 7Ͳ4:8d]~_?P(Z!ymkBTxmA`w@kNQ tt`:R\; ;i8HAQç7ft:smw.o9qh/Wu.Kk3[JL_y\.9|Uo"e}䯔uCyS9}܅UܓeX6)r܇_2:bKgv}*׶z^O74K콏S[yųmc[cUiW/1cGۑ1VXmPr%Bǵ$i,?zF0)祾>ŻG|.}܁sEHykZƐK_|{8LGNyxn.23)'}I:I!wͷ㘥EZc1tM:O{"!TnǷչ^#Wo5w{ (mkBTKx1nA`n\@$=ERlOn`vKe7(ehMC #ѓ#$n`vS%$ n捭}3ϛg<⩿3sw{!erO7Ewg[3U%(}KSƛOOb:绘6_5JOEe̮ uL$c|-cf^'guX$'ME}E$~Xϓ7ɺ/yxq|9q2M]&\qϛf'~>f~幼޼Ղ㘲͞}<뾰ŴabVhSMuM9̳U9dzڟ%/Gnx&9f͟]?[+ϻ__a7w[[=xmorQ Q&mkBTNx=nG`߀zTU%% HI`N'݀jFs87wwF@"<ŃݝoVoo;c?GG(_Lr0rr<ގ9ߧԹH{l]n:Μ\m>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)Pr*gڰFG 4%ms?P^@=/;B/f_C mkBTnxұ P El@6hR0BJ 5lQH.2>ő>Pe~v>6E{X71F-LWKFlܟ~]'# mkBTx1@E8 pD t i$ HvfU8PJe{E뮱o?֟8ĭu_5zһ~fqԿ+SN?+ھ`rmkBTxԱMPll67p z{6 lh_:K/EO{3M*avmM~#V}2w!be}yX}H9i"P?W_wLi6ζ)5OC_iS>oGOw|lS@YwvL߈ws[Jmo)}N}b}3{uUMspv'ʢ6mkBTxԱ 0EQ6 d6IOS]$w)Yrײwae若E3?>L|Y-6&~GmkBT#xӡ0EB'<-0h 3DV/Rмc>,a W&To|wֿY}n`r/.לk*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw) IDATxׁ2G?,kkԳq\Ґr)ETٽURAWW@6rE󢹢X]Z;WmRnU4uq~4"sqʿ"^ѯHY5;p~!g ߼y]-ޛ7 0@DDDD #,"""0dE!( YDDDD`""""CQ"EDDD,"""0dE!( YDDDD`""""CQ"EDDD,"""0dE!( YDDDD`""""CQ"EDDD,"""0dE!( YDDC^CUU̠V9DҦ~7¡:* 4MCZm?p@[E~1d%TUeрa""JRM "rN9wKX<4] 0,G`tG wZ7-ݙžؖ%wDDD4Йs+8y~<Μ[z[` T| ז.Աtjkoʬֽ-u 5z;xwA?""$`gέW.`BO.%|۵ҸʋE_ sLۃG?;A""aJӯ\׿"Nrޒ X.+cpgW(NVŷ5>a .\š}O}(Q1d%ԟ}Y^S<([%""J$&׿NV_oi XA X'_=ѷKDDHJ <,`,p_'f+LDDDJbFw܆~غy.6s/KCye pB< _O[~!+a&w=wo!lmmQ={5| !ȥ hQ]O<_<;'JzI Ȕe2(A>w-i4Mþ}Nlfl8\ Kk`Y6u,GG8 Y-,A X:M%(xDZEjÆ mu9J%Z< :d%ȹ6 h^׀eߏVqhB{7}nPU7mugllm\.w3Zb>vdڍg!w/[_\jNUSy/cȢPQVQTPVW(38yyepa99B-\G2 `~]DlaaRH,ː$ u* BZ.$Me**//#e8<~CzU(j۾j Y;݊!+q֍=N~ȓ"X2ʻL Xkh- DDԛ\.UUQ[TU0Ia3.'z%|vj 2@g- zJshNx4!69Ͻ??҅VgqʵMZoeض-u:3o><3 Յ //M!%t|- F,"u/h.>z)>|- CVtqg_k>x{c"""Ӂp HYݜp0aDV(k@眪p(e  |>X=Y N[|>EQBhYMZm0(z>dd5<_Y9y܇0Iq&' [a}l<;>lބ4 kEQ011cv- ,)ZXX(=rZ0TUorb80rX0dE+g|cbr`r4H {]cH"M077yO|GEX /RZ] ]Q.C6?ߏRzǏGcFw똞{^0dEʵNV^ 1%^wk} X;Tw̃߾ Uo:PZ B+J4 b/uBUαELNNFJ4M#qjÇG:,w{0dEʵpc9wmrv 69AV=Y\rwv%CAkxcnn.`R0==ɇ}-5{{:똝ER |[[ jQ~ X!(;˯C|GQ.Е$PVpqI",Y[fvW!uLOO:4r9dY({u"* TU }4{GWoBk:Cc0) 0mr-,KQyP6RUCT* M .d2bbbqK %tui,Z5!\gW4,,,`I X\9Nӭ#z zoe@zkޏ$I( ( HRd2d2Ϩf_<B4_XXˣ0 ݇0^Mh poK[RЬ)pOMk/^Ġ\,#I$>|X|PG9E^sN8h%s5¢F/Y͵zwc NV^S-`K1\hЦ'Y?MOtZx^P/4x$IBX "Ţps=~Mӄ%:Br9,dU a O4,þPSϐc"(/&[RS@׶2vtdn_>ZS(BVUYC"رc>%J^ `"&wK$6;;&ǐWOWi=!>ECx p|sǶr#`R2bvA:t(y=v|^)臵Hȗ$ ¼׏D7ׂÈ!GWX]a-=% eBs4[ht,k;ˡNy[j#ڣ8n,:Qܶ;MCڶMܱm_vXn\M+I(35%":\'4M*{@˳i+m$\/Z Y=:tymrBY>w|йdf]p X>{=^Y;_̥%"DzPLho҆ףB&~<%ฅ"zBo@lkUCX5d5<&e02#@`I{_~5uksͫSl_/nG\"w/V Ydb҅4,þPwxNu,ãm>au{ڛ=`ksޫSj5nfp^ @h89L݀$Ҹzp 1>U]6㱏 |{֠r9VwBQո<1d}N֞BLh=̅sVf>лァ `cy\[]8qa !RlMl;{we86Wp V@sҿ t$)<|M8 ?81d¯S=H#6p3.R o_E+`=2!ghaNl}]xcGz{QyX.- ZLJ$2uSyR3x={,`\u A~xxE`vbOV j( YyNSy㷥nŞ{vR}x:dKbвDc1 Y=ܽ +x@s XF𜽾avp#$zA J 4z5u3d@ X=,[g!fكU > dx!khz@kD{E%DM?kqer YdbAsp  k{疵_[ l!{@rlek;ltPyNCAiiB zg V#^n.dzz|=x $~8k5pXD?vqKY{ iAB- Y={;,`95z玵jKHC.^ Tqi}Z#dZRV O dq.yY"c/Y1d(s'!>ny۞)5 jǰRv> Z#!%IRLxi"қzla]uBCk()z;yi:ubK)`=:,$ZqA;:m,ѐ%:|$bZDzԩSRIӜNmzԼ Xx,2Z ^ A{,~A(H ^;ujU}-AP%K*B>-j ;ρbh % CV)ن ֞[^,km~H?VX jHӁcx-h-h4"ZKJ$ i}a A!?l],Y6UO?VX< lj똟*_"WWz!HIBÆr9{e2B+ Y!x6eyLXxѠU }!׵R$4ۿE1N"YtpZ311[Fu?4 e4SNB @+`֪C q 9F3&RϝjdCUU᡺::fggiB{%uiaùпz333<yix0d3}3cn,å_=a~NǁwlETUt(izrG}ΗȇmTZîX,FQ^GT ,9!+DS,z za+k_^A0Ä _>rDu<zb4MѣGzR 177ח^"K]hZ3\dY Z _^"D4LOO8JbiS0Lr܉G?x?N^Ӑukit2^=;c޴ oâh`nnssseL`mWuj5j5x,χv)4Mf( dYv4 C4TՁ@d|瀕ej5|ӟZ,Ţ fV&E JEx$I£!+d_pX|eձO " u+ lJ\,QfZXX|>0ÇO{j5"4M }JJ|l#,3ELOOVbw EXyb B[pGqh;X|mu!̈,[)`e {xs@H*EQb_=4'9r333bT*ILOO 6w,N#5|$T* }!K,9Yz-8~>`ˀCذq@b XC;W'{p||SSSM,>a`K%E&$jC(J܃d28vTUJBt: EQ(J*PT b(3A󡬋o߾$`n3i\o忎'`s@z_g:r/jںjOEV Un RNQHԚ,˲P@z<_^뾕 IDAThgayy1d*I!oI۲,ң7(m1_ig yEQZJT%6 m0lv=YI l6۶KVzJ4FFFJ*Tɲq:/^|!JwanҖt:c}n?t/Wu `CV%9,,}CV0',m8mvY߻oJ5MDacJ8 /^Ov p`JЂW)̠fu˶h1d%NxS]c=],f,"(0d%J?UE~ޤ$A +` Hrxd,"q XA&Ȱ^wz;QzFFFbj ' YhV/ٖGo൯O1ٓEDQ`JA|_s(pϟ4/Cj[ UQ6@[oꎸa+Ϲy^?aT*+SKhaJ~,{}wm{[G7QoOV.5D0d%-Gޓwlkh96,Ή'Z7vc6Q8'+InVC}.uIUUߡ|>t:ShaOV|Ɋ:`P_o[+l'*JoZCDCVlۿ$,z yzxDpb/EÅIKϬnXŠc@2u& ~x+_tnEܮ;ǚK=@4΢VUccc13| "`l; X> :pA`#o.o>\m jnnwQ,8|pĭ""bJ? xRW^??^,ꚦiT*V̱eGA*uDDM YIq ρ \{d-Y.?f*$it]ٳgQհ(p|>2`Ql ZO+jm,,raPj5ʍ$I(\bǐd&\*? n@3`m=]Bh !k.Ms_Xנy9W$ J|>B%;fZ+ds觸BI|W#_RdYZNe r9\@`6 Zw1ppnA`燛Wffh4PUN*T +EQ$AQ(l6^+"8 #M(/W*Jh,oCOAifھ7w2t]o.]qd2sZZL۾t:Ȳd2ȲܧaZ޾ 44 O+[r)"ͣ쩊PV$IghV!(A4Q" ׃Dž7 1h00d%as1ѰbJ 0%⾾jae _DD` Y5*^*Kj<}""$)< *o>"“k}͝MDDo Y$B_jrl*y)ߠe-p} \DD4`KDCV빽>۶W:@}bo \ne8m큲s۶ A\d( Y$L ەnH4\ٷ""u0meH Eaƍ;g-gg=52Y_־ՅP-DDDyXn&['Hyk30Ctn޼7m$ZEDD1cȊ}n>#\nCc%r{+dg"nXN܆:q\ XDD3(m)t9.{ydkn[{3ݼySx|,ׄ\ r:'<'G~5!M~>n/ r5aW.jC% CVLhrԂ^~R'\57lʼn!+N^-^nHYc Y}HpsCV? !l}Ͷ(ϞNm;!"">;0n><dRp Vm"""CqV.c ? ~=FWs,|w aTNk?C߷8Aʸ$ġ@""J$X)+nx""Aǐ5 DÓyaM OhHqaa0Ah1d'f"""" !( YDDDD`""""CQ"EDDD,"""0dE!( YDDDD`""""CQ"EDDD,"""0dE!( YDDDD`""""CQ"EDDD,"""0dE!( YDDDD`""""CQ"ŅwIIENDB`bitstring-bitstring-4.2.3/doc/quick_reference.rst000066400000000000000000000575111462444752600222100ustar00rootroot00000000000000.. currentmodule:: bitstring .. _quick_reference: ############### Quick Reference ############### This section gives a summary of the bitstring module's classes, functions and attributes. There are four main classes that are bit containers, so that each element is a single bit. They differ based on whether they can be modified after creation and on whether they have the concept of a current bit position. .. |nbsp| unicode:: 0xa0 :trim: .. list-table:: :widths: 20 15 15 50 :header-rows: 1 * - Class - Mutable? - Streaming methods? - * - :ref:`bits_quick_reference` - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✘ - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✘ - An efficient, immutable container of bits. * - :ref:`bitarray_quick_reference` - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✔ - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✘ - Like ``Bits`` but it can be changed after creation. * - :ref:`constbitstream_quick_reference` - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✘ - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✔ - Immutable like ``Bits`` but with a bit position and reading methods. * - :ref:`bitstream_quick_reference` - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✔ - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✔ - Mutable like ``BitArray`` but with a bit position and reading methods. The final class is a flexible container whose elements are fixed-length bitstrings. .. list-table:: :widths: 20 15 15 50 * - :ref:`array_quick_reference` - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✔ - |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| |nbsp| ✘ - An efficient list-like container where each item has a fixed-length binary format. ---- .. _bits_quick_reference: Bits ---- :class:`Bits` is the most basic class and is just a container of bits. It is immutable, so once created its value cannot change. ``Bits(auto, /, length: Optional[int], offset: Optional[int], **kwargs)`` The first parameter (usually referred to as `auto`) can be many different types, including parsable strings, a file handle, a bytes or bytearray object, an integer or an iterable. A single initialiser from `kwargs` can be used instead of `auto`, including ``bin``, ``hex``, ``oct``, ``bool``, ``uint``, ``int``, ``float``, ``bytes`` and ``filename``. Examples:: Bits('0xef') Bits(float=-50.5, length=32) Bits('uint10=99') Bits(uint=99, length=10) Methods ^^^^^^^ * :meth:`~Bits.all` -- Check if all specified bits are set to 1 or 0. * :meth:`~Bits.any` -- Check if any of specified bits are set to 1 or 0. * :meth:`~Bits.copy` -- Return a copy of the bitstring. * :meth:`~Bits.count` -- Count the number of bits set to 1 or 0. * :meth:`~Bits.cut` -- Create generator of constant sized chunks. * :meth:`~Bits.endswith` -- Return whether the bitstring ends with a sub-bitstring. * :meth:`~Bits.find` -- Find a sub-bitstring in the current bitstring. * :meth:`~Bits.findall` -- Find all occurrences of a sub-bitstring in the current bitstring. * :meth:`~Bits.fromstring` -- Create a bitstring from a formatted string. * :meth:`~Bits.join` -- Join bitstrings together using current bitstring. * :meth:`~Bits.pp` -- Pretty print the bitstring. * :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. Special methods ^^^^^^^^^^^^^^^ Also available are operators that will return a new bitstring (or check for equality): * :meth:`== ` / :meth:`\!= ` -- Equality tests. * :meth:`[] ` -- Get an element or slice. * :meth:`+ ` -- Concatenate with another bitstring. * :meth:`* ` -- Concatenate multiple copies of the current bitstring. * :meth:`~ ` -- Invert every bit of the bitstring. * :meth:`\<\< ` -- Shift bits to the left. * :meth:`>> ` -- Shift bits to the right. * :meth:`& ` -- Bit-wise AND between two bitstrings. * :meth:`| ` -- Bit-wise OR between two bitstrings. * :meth:`^ ` -- Bit-wise XOR between two bitstrings. Properties ^^^^^^^^^^ These read-only properties of the ``Bits`` object are interpretations of the binary data and are calculated as required. Many require the bitstring to be specific lengths. * :attr:`~Bits.bin` / ``b`` -- The bitstring as a binary string. * :attr:`~Bits.bool` -- For single bit bitstrings, interpret as True or False. * :attr:`~Bits.bytes` -- The bitstring as a bytes object. * :attr:`~Bits.float` / ``floatbe`` / ``f`` -- Interpret as a big-endian floating point number. * :attr:`~Bits.floatle` -- Interpret as a little-endian floating point number. * :attr:`~Bits.floatne` -- Interpret as a native-endian floating point number. * :attr:`~Bits.hex` / ``h`` -- The bitstring as a hexadecimal string. * :attr:`~Bits.int` / ``i`` -- Interpret as a two's complement signed integer. * :attr:`~Bits.intbe` -- Interpret as a big-endian signed integer. * :attr:`~Bits.intle` -- Interpret as a little-endian signed integer. * :attr:`~Bits.intne` -- Interpret as a native-endian signed integer. * :attr:`~Bits.len` -- Length of the bitstring in bits. * :attr:`~Bits.oct` / ``o`` -- The bitstring as an octal string. * :attr:`~Bits.uint` / ``u`` -- Interpret as a two's complement unsigned integer. * :attr:`~Bits.uintbe` -- Interpret as a big-endian unsigned integer. * :attr:`~Bits.uintle` -- Interpret as a little-endian unsigned integer. * :attr:`~Bits.uintne` -- Interpret as a native-endian unsigned integer. There are also various other flavours of 16-bit, 8-bit and smaller floating point types (see :ref:`Exotic floats`) and exponential-Golomb integer types (see :ref:`exp-golomb`) that are not listed here for brevity. ---- .. _bitarray_quick_reference: BitArray -------- ``Bits`` ⟶ ``BitArray`` :class:`BitArray` adds mutating methods to ``Bits``. The constructor is the same as for ``Bits``. Additional methods ^^^^^^^^^^^^^^^^^^ All of the methods listed above for the ``Bits`` class are available, plus: * :meth:`~BitArray.append` -- Append a bitstring. * :meth:`~BitArray.byteswap` -- Change byte endianness in-place. * :meth:`~BitArray.clear` -- Remove all bits from the bitstring. * :meth:`~BitArray.insert` -- Insert a bitstring. * :meth:`~BitArray.invert` -- Flip bit(s) between one and zero. * :meth:`~BitArray.overwrite` -- Overwrite a section with a new bitstring. * :meth:`~BitArray.prepend` -- Prepend a bitstring. * :meth:`~BitArray.replace` -- Replace occurrences of one bitstring with another. * :meth:`~BitArray.reverse` -- Reverse bits in-place. * :meth:`~BitArray.rol` -- Rotate bits to the left. * :meth:`~BitArray.ror` -- Rotate bits to the right. * :meth:`~BitArray.set` -- Set bit(s) to 1 or 0. Additional special methods ^^^^^^^^^^^^^^^^^^^^^^^^^^ The special methods available for the ``Bits`` class are all available, plus some which will modify the bitstring: * :meth:`[] ` -- Set an element or slice. * :meth:`del ` -- Delete an element or slice. * :meth:`+= ` -- Append bitstring to the current bitstring. * :meth:`*= ` -- Concatenate multiple copies of the current bitstring. * :meth:`\<\<= ` -- Shift bits in-place to the left. * :meth:`>>= ` -- Shift bits in-place to the right. * :meth:`&= ` -- In-place bit-wise AND between two bitstrings. * :meth:`|= ` -- In-place bit-wise OR between two bitstrings. * :meth:`^= ` -- In-place bit-wise XOR between two bitstrings. ``BitArray`` objects have the same properties as ``Bits``, except that they are all (with the exception of ``len``) writable as well as readable. ---- .. _constbitstream_quick_reference: ConstBitStream -------------- ``Bits`` ⟶ ``ConstBitStream`` :class:`ConstBitStream` adds a bit position and methods to read and navigate in an immutable bitstream. If you wish to use streaming methods on a large file without changing it then this is often the best class to use. The constructor is the same as for ``Bits`` / ``BitArray`` but with an optional current bit position. ``ConstBitStream(auto, length: Optional[int], offset: Optional[int], pos: int = 0, **kwargs)`` All of the methods, special methods and properties listed above for the ``Bits`` class are available, plus: Additional methods ^^^^^^^^^^^^^^^^^^ * :meth:`~ConstBitStream.bytealign` -- Align to next byte boundary. * :meth:`~ConstBitStream.peek` -- Peek at and interpret next bits as a single item. * :meth:`~ConstBitStream.peeklist` -- Peek at and interpret next bits as a list of items. * :meth:`~ConstBitStream.read` -- Read and interpret next bits as a single item. * :meth:`~ConstBitStream.readlist` -- Read and interpret next bits as a list of items. * :meth:`~ConstBitStream.readto` -- Read up to and including next occurrence of a bitstring. Additional properties ^^^^^^^^^^^^^^^^^^^^^ * :attr:`~ConstBitStream.bytepos` -- The current byte position in the bitstring. * :attr:`~ConstBitStream.pos` -- The current bit position in the bitstring. ---- .. _bitstream_quick_reference: BitStream --------- ``Bits`` ⟶ ``BitArray / ConstBitStream`` ⟶ ``BitStream`` :class:`BitStream` contains all of the 'stream' elements of ``ConstBitStream`` and adds all of the mutating methods of ``BitArray``. The constructor is the same as for ``ConstBitStream``. It has all the methods, special methods and properties of the ``Bits``, ``BitArray`` and ``ConstBitArray`` classes. It is the most general of the four classes, but it is usually best to choose the simplest class for your use case. ---- .. _array_quick_reference: Array ----- A bitstring :class:`Array` is a contiguously allocated sequence of bitstrings of the same type. It is similar to the ``array`` type in the `array `_ module, except that it is far more flexible. ``Array(dtype: str | Dtype, initializer, trailing_bits)`` The `dtype` can any single fixed-length token as described in :ref:`format_tokens` and :ref:`compact_format`. The `inititalizer` will typically be an iterable such as a list, but can also be many other things including an open binary file, a bytes or bytearray object, another ``bitstring.Array`` or an ``array.array``. It can also be an integer, in which case the ``Array`` will be zero-initialised with that many items. The `trailing_bits` typically isn't used in construction, and specifies bits left over after interpreting the stored binary data according to the data type `dtype`. Both the dtype and the underlying bit data (stored as a :class:`BitArray`) can be freely modified after creation, and element-wise operations can be used on the ``Array``. Modifying the data or format after creation may cause the :attr:`~Array.trailing_bits` to not be empty. Initialization examples:: Array('>H', [1, 10, 20]) Array('float16', a_file_object) Array('int4', stored_bytes) Methods ^^^^^^^ * :meth:`~Array.append` -- Append a single item to the end of the Array. * :meth:`~Array.astype` -- Cast the Array to a new dtype. * :meth:`~Array.byteswap` -- Change byte endianness of all items. * :meth:`~Array.count` -- Count the number of occurrences of a value. * :meth:`~Array.equals` -- Compare with another Array for exact equality. * :meth:`~Array.extend` -- Append multiple items to the end of the Array from an iterable. * :meth:`~Array.fromfile` -- Append items read from a file object. * :meth:`~Array.insert` -- Insert an item at a given position. * :meth:`~Array.pop` -- Return and remove an item. * :meth:`~Array.pp` -- Pretty print the Array. * :meth:`~Array.reverse` -- Reverse the order of all items. * :meth:`~Array.tobytes` -- Return Array data as bytes object, padding with zero bits at the end if needed. * :meth:`~Array.tofile` -- Write Array data to a file, padding with zero bits at the end if needed. * :meth:`~Array.tolist` -- Return Array items as a list. Special methods ^^^^^^^^^^^^^^^ These non-mutating special methods are available. Where appropriate they return a new ``Array``. * :meth:`[] ` -- Get an element or slice. * :meth:`+ ` -- Add value to each element. * :meth:`- ` -- Subtract value from each element. * :meth:`* ` -- Multiply each element by a value. * :meth:`/ ` -- Divide each element by a value. * :meth:`// ` -- Floor divide each element by a value. * :meth:`% ` -- Take modulus of each element with a value. * :meth:`\<\< ` -- Shift value of each element to the left. * :meth:`>> ` -- Shift value of each element to the right. * :meth:`& ` -- Bit-wise AND of each element. * :meth:`| ` -- Bit-wise OR of each element. * :meth:`^ ` -- Bit-wise XOR of each element. * :meth:`- ` -- Unary minus of each element. * :meth:`abs() ` -- Absolute value of each element. For example:: >>> b = Array('i6', [30, -10, 1, 0]) >>> b >> 2 Array('i6', [7, -3, 0, 0]) >>> b + 1 Array('i6', [31, -9, 2, 1]) >>> b + b Array('i6', [60, -20, 2, 0]) Comparison operators will output an ``Array`` with a ``dtype`` of ``'bool'``. * :meth:`== ` / :meth:`\!= ` -- Equality tests. * :meth:`\< ` -- Less than comparison. * :meth:`\<= ` -- Less than or equal comparison. * :meth:`> ` -- Greater than comparison. * :meth:`>= ` -- Greater than or equal comparison. Mutating versions of many of the methods are also available. * :meth:`[] ` -- Set an element or slice. * :meth:`del ` -- Delete an element or slice. * :meth:`+= ` -- Add value to each element in-place. * :meth:`-= ` -- Subtract value from each element in-place. * :meth:`*= ` -- Multiply each element by a value in-place. * :meth:`/= ` -- Divide each element by a value in-place. * :meth:`//= ` -- Floor divide each element by a value in-place. * :meth:`%= ` -- Take modulus of each element with a value in-place. * :meth:`\<\<= ` -- Shift bits of each element to the left in-place. * :meth:`>>= ` -- Shift bits of each element to the right in-place. * :meth:`&= ` -- In-place bit-wise AND of each element. * :meth:`|= ` -- In-place bit-wise OR of each element. * :meth:`^= ` -- In-place bit-wise XOR of each element. Example:: >>> a = Array('float16', [1.5, 2.5, 7, 1000]) >>> a[::2] *= 3.0 # Multiply every other float16 value in-place >>> a Array('float16', [4.5, 2.5, 21.0, 1000.0]) The bit-wise logical operations (``&``, ``|``, ``^``) are performed on each element with a ``Bits`` object, which must have the same length as the ``Array`` elements. The other element-wise operations are performed on the interpreted data, not on the bit-data. For example this means that the shift operations won't work on floating point formats. Properties ^^^^^^^^^^ * :attr:`~Array.data` -- The complete binary data in a ``BitArray`` object. Can be freely modified. * :attr:`~Array.dtype` -- The data type or typecode. Can be freely modified. * :attr:`~Array.itemsize` -- The length *in bits* of a single item. Read only. * :attr:`~Array.trailing_bits` -- If the data length is not a multiple of the `dtype` length, this ``BitArray`` gives the leftovers at the end of the data. ---- .. _dtype_quick_reference: Dtype ----- A data type (or 'dtype') concept is used in the bitstring module to encapsulate how to create, parse and present different bit interpretations. ``Dtype(token: str, /, length: int | None, scale: int | float | None = None)`` Creates a :class:`Dtype` object. Dtypes are immutable and cannot be changed after creation. The first parameter is a format token string that can optionally include a length. If appropriate, the `length` parameter can be used to specify the length of the bitstring. The `scale` parameter can be used to specify a multiplicative scaling factor for the interpretation of the data. Methods ^^^^^^^ * :meth:`~Dtype.build` -- Create a bitstring from a value. * :meth:`~Dtype.parse` -- Parse a bitstring to find its value. Properties ^^^^^^^^^^ All properties are read-only. * :attr:`~Dtype.bitlength` -- The number of bits needed to represent a single instance of the data type. * :attr:`~Dtype.bits_per_item` -- The number of bits for each unit of length. Usually 1, but equals 8 for `bytes` type. * :attr:`~Dtype.get_fn` -- A function to get the value of the data type. * :attr:`~Dtype.is_signed` -- If True then the data type represents a signed quantity. * :attr:`~Dtype.length` -- The length of the data type in units of `bits_per_item`. * :attr:`~Dtype.name` -- A string giving the name of the data type. * :attr:`~Dtype.read_fn` -- A function to read the value of the data type. * :attr:`~Dtype.return_type` -- The type of the value returned by the `parse` method. * :attr:`~Dtype.scale` -- The multiplicative scale applied when interpreting the data. * :attr:`~Dtype.set_fn` -- A function to set the value of the data type. * :attr:`~Dtype.variable_length` -- If True then the length of the data type varies, and shouldn't be specified. General Information ------------------- .. _format_tokens: Format tokens ^^^^^^^^^^^^^ Format strings are used when constructing bitstrings, as well as reading, packing and unpacking them, as well as giving the format for :class:`Array` objects. They can also be auto promoted to bitstring when appropriate - see :ref:`auto_init`. =================== =============================================================================== ``'int:n'`` ``n`` bits as a signed integer. ``'uint:n'`` ``n`` bits as an unsigned integer. ``'intbe:n'`` ``n`` bits as a byte-wise big-endian signed integer. ``'uintbe:n'`` ``n`` bits as a byte-wise big-endian unsigned integer. ``'intle:n'`` ``n`` bits as a byte-wise little-endian signed integer. ``'uintle:n'`` ``n`` bits as a byte-wise little-endian unsigned integer. ``'intne:n'`` ``n`` bits as a byte-wise native-endian signed integer. ``'uintne:n'`` ``n`` bits as a byte-wise native-endian unsigned integer. ``'float:n'`` ``n`` bits as a big-endian floating point number (same as ``floatbe``). ``'floatbe:n'`` ``n`` bits as a big-endian floating point number (same as ``float``). ``'floatle:n'`` ``n`` bits as a little-endian floating point number. ``'floatne:n'`` ``n`` bits as a native-endian floating point number. ``'hex:n'`` ``n`` bits as a hexadecimal string. ``'oct:n'`` ``n`` bits as an octal string. ``'bin:n'`` ``n`` bits as a binary string. ``'bits:n'`` ``n`` bits as a new bitstring. ``'bytes:n'`` ``n`` bytes as a ``bytes`` object. ``'bool[:1]'`` next bit as a boolean (True or False). ``'pad:n'`` next ``n`` bits will be ignored (padding). Only applicable when reading, not creating. =================== =============================================================================== The ``':'`` before the length is optional, and is mostly omitted in the documentation, except where it improves readability. The ``hex``, ``bin``, ``oct``, ``int``, ``uint`` and ``float`` properties can all be shortened to just their initial letter. See also :ref:`Exotic floats` and :ref:`exp-golomb` for other types that can be used in format token strings. Bitstring literals ^^^^^^^^^^^^^^^^^^ To make a literal quantity (one that directly represents a sequence of bits) you can use any of the format tokens above followed by an ``'='`` and a value to initialise with. For example:: s = BitArray('float32=10.125, int7=-9') s.append('hex:abc') You can also create binary, octal and hexadecimal literals by starting a string with ``'0b'``, ``'0o'`` and ``'0x'`` respectively:: t = BitArray('0b101') t += '0x001f' .. _compact_format: Compact format strings ^^^^^^^^^^^^^^^^^^^^^^ Another option is to use a format specifier similar to those used in the ``struct`` and ``array`` modules. These consist of a character to give the endianness, followed by more single characters to give the format. The endianness character must start the format string: ======= ============= ``'>'`` Big-endian ``'<'`` Little-endian ``'='`` Native-endian ======= ============= .. note:: * For native-endian ``'@'`` and ``'='`` can both be used and are equivalent. The ``'@'`` character was required for native-endianness prior to version 4.1 of bitstring. * For 'network' endianness use ``'>'`` as network and big-endian are equivalent. This is followed by at least one of these format characters: ======= =============================== ``'b'`` 8 bit signed integer ``'B'`` 8 bit unsigned integer ``'h'`` 16 bit signed integer ``'H'`` 16 bit unsigned integer ``'l'`` 32 bit signed integer ``'L'`` 32 bit unsigned integer ``'q'`` 64 bit signed integer ``'Q'`` 64 bit unsigned integer ``'e'`` 16 bit floating point number ``'f'`` 32 bit floating point number ``'d'`` 64 bit floating point number ======= =============================== The exact type is determined by combining the endianness character with the format character, but rather than give an exhaustive list a single example should explain: ======== ====================================== =========== ``'>h'`` Big-endian 16 bit signed integer ``intbe16`` ``'h'`` on big-endian systems, and equal the little-endian ``'\n", "Note:\n", "\n", "Be sure to remember the quotes around the hex and binary strings.\n", "If you forget them you would just have an ordinary Python integer, which would instead create a bitstring of that many '0' bits.\n", "For example `0xff01` is the same as the base-10 number 65281, so `BitArray(0xff01)` would consist of 65281 zero bits!\n", "\n", "\n", "There are lots of things we can do with our new bitstrings, the simplest of which is just to print them:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0xff01\n", "0b110\n" ] } ], "source": [ "print(a)\n", "print(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now you would be forgiven for thinking that the strings that we used to create the two bitstrings had just been stored to be given back when printed, but that's not the case.\n", "Every bitstring should be considered just as a sequence of bits.\n", "As we'll see there are lots of ways to create and manipulate them, but they have no memory of how they were created.\n", "When they are printed they just pick the simplest hex or binary representation of themselves.\n", "If you prefer you can pick the representation that you want:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1111111100000001\n", "6\n", "-2\n", "b'\\xff\\x01'\n" ] } ], "source": [ "print(a.bin)\n", "print(b.oct)\n", "print(b.int)\n", "print(a.bytes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "There are a few things to note here:\n", "\n", "* To get the different interpretations of the binary data we use properties such as `bin`, `hex`, `oct`, `int` and `bytes`. You can probably guess what these all mean, but you don't need to know quite yet. The properties are calculated when you ask for them rather than being stored as part of the object itself.\n", "* Many of the interpretations have single letter aliases, and interpretations can also have bit lengths appended to them. This allows expressions such as `a.u32 = 900` which will set `a` to the 32 bit representation of the unsigned integer `900`. You're not restricted to the usual bit lengths, so something like `a.i5 = -8` will work as well.\n", "\n", "Great - let's try some more:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cannot convert to hex unambiguously - not a multiple of 4 bits long.\n" ] } ], "source": [ "try:\n", " b.hex\n", "except bitstring.InterpretError as e:\n", " print(e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Oh dear - this throws a `bitstring.InterpretError` error.\n", "The problem we have here is that `b` is 3 bits long, whereas each hex digit represents 4 bits.\n", "This means that there is no unambiguous way to represent it in hexadecimal.\n", "There are similar restrictions on other interpretations (octal must be a multiple of 3 bits, bytes a multiple of 8 bits etc.)\n", "\n", "An exception is raised rather than trying to guess the best hex representation as there are a multitude of ways to convert to hex.\n", "I occasionally get asked why it doesn't just do the 'obvious' conversion, which is invariably what that person expects from his own field of work.\n", "This could be truncating bits at the start or end, or padding at the start or end with either zeros or ones.\n", "Rather than try to guess what is meant we just raise an exception - if you want a particular behaviour then write it explicitly:\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "c\n", "6\n" ] } ], "source": [ "print((b + [0]).hex)\n", "print(([0] + b).hex)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Here we've added a zero bit first to the end and then to the start.\n", "Don't worry too much about how it all works, but just to give you a taster the zero bit `[0]` could also have been written as `BitArray([0])`, `BitArray('0b0')`, `BitArray(bin='0')`, `'0b0'` or just `1` (this final option isn't a typo, it means construct a bitstring of length one, with all the bits initialised to zero - it does look a bit confusing though which is why I prefer `[0]` and `[1]` to represent single bits).\n", "Take a look at [the auto initialiser](https://bitstring.readthedocs.io/en/stable/creation.html#the-auto-initialiser) for more details." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modifying bitstrings\n", "\n", "A `BitArray` can be treated just like a list of bits. You can slice it, delete sections, insert new bits and more using standard index notation:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0b111110\n", "0b1111111100\n" ] } ], "source": [ "print(a[3:9])\n", "del a[-6:]\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "The slicing works just as it does for other containers, so the deletion above removes the final six bits.\n", "\n", "If you ask for a single item, rather than a slice, a boolean is returned. Naturally enough `1` bits are `True` whereas `0` bits are `False`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n" ] } ], "source": [ "print(a[0])\n", "print(a[-1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "To join together bitstrings you can use a variety of methods, including `append`, `prepend`, `insert`, and plain `+` or `+=` operations::" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "a.prepend('0b01')\n", "a.append('0o7')\n", "a += '0x06'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Here we first put two bits at the start of `a`, then three bits on the end (a single octal digit) and finally another byte (two hex digits) on the end.\n", "\n", "Note how we are just using ordinary strings to specify the new bitstrings we are adding.\n", "These get converted automatically to the right sequence of bits.\n", "\n", "
\n", "Note:\n", " The length in bits of bitstrings specified with strings depends on the number of characters, including leading zeros.\n", " So each hex character is four bits, each octal character three bits and each binary character one bit.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Finding and replacing\n", "\n", "A `find` is provided to search for bit patterns within a bitstring.\n", "You can choose whether to search only on byte boundaries or at any bit position:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": "(3,)" }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = BitArray('0xa9f')\n", "a.find('0x4f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we have found the `0x4f` byte in our bitstring, though it wasn't obvious from the hexadecimal as it was at bit position 3.\n", "To see this clearer consider this equality:\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": "True" }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a == '0b101, 0x4f, 0b1'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "in which we've broken the bitstring into three parts to show the found byte.\n", "This also illustrates using commas to join bitstring sections." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constructing a bitstring\n", "\n", "Let's say you have a specification for a binary file type (or maybe a packet specification etc.) and you want to create a bitstring quickly and easily in Python.\n", "For this example I'm going to use a header from the MPEG-2 video standard.\n", "Here's how the header is described in the standard:\n", "\n", "|sequence_header() | No. of bits | Mnemonic|\n", "|-----------------------------------|--------------|----------|\n", "|sequence_header_code | 32 | bslbf |\n", "|horizontal_size_value | 12 | uimsbf |\n", "|vertical_size_value | 12 | uimsbf |\n", "|aspect_ratio_information | 4 | uimsbf |\n", "|frame_rate_code | 4 | uimsbf |\n", "|bit_rate_value | 18 | uimsbf |\n", "|marker_bit | 1 | bslbf |\n", "|vbv_buffer_size_value | 10 | uimsbf |\n", "|constrained_parameters_flag | 1 | bslbf |\n", "|load_intra_quantiser_matrix | 1 | uimsbf |\n", "|if (load_intra_quantiser_matrix) |\n", "|{ intra_quantiser_matrix[64] } | 8*64 | uimsbf |\n", "|load_non_intra_quantiser_matrix | 1 | uimsbf |\n", "|if (load_non_intra_quantiser_matrix) |\n", "|{ non_intra_quantiser_matrix[64] } | 8*64 | uimsbf |\n", "|next_start_code() | | |\n", "\n", "The mnemonics mean things like uimsbf = 'Unsigned integer, most significant bit first'.\n", "\n", "So to create a sequence_header for your particular stream with width of 352 and height of 288 you could start like this:\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "000001b3160120\n" ] } ], "source": [ "s = BitArray()\n", "s.append('0x000001b3') # the sequence_header_code\n", "s.append('uint12=352') # 12 bit unsigned integer\n", "s.append('uint12=288')\n", "print(s.hex)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is fine, but if you wanted to be a bit more concise you could just write" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "s = BitArray('0x000001b3, uint12=352, uint12=288')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is better, but it might not be a good idea to have the width and height hard-wired in like that.\n", "We can make it more flexible by using a format string and the `pack` function:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "width, height = 352, 288\n", "s = bitstring.pack('0x000001b3, 2*u12', width, height)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where we have also used `2*u12` as shorthand for `uint12, uint12`.\n", "\n", "The [`pack`](https://bitstring.readthedocs.io/en/stable/functions.html#bitstring.pack) function can also take a dictionary as a parameter which can replace the tokens in the format string.\n", "For example:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": "\"BitStream('0x000001b31601201')\"" }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fmt = 'sequence_header_code, \\\n", " uint12=horizontal_size_value, \\\n", " uint12=vertical_size_value, \\\n", " uint4=aspect_ratio_information, '\n", " # ...\n", "d = {'sequence_header_code': '0x000001b3',\n", " 'horizontal_size_value': 352,\n", " 'vertical_size_value': 288,\n", " 'aspect_ratio_information': 1,\n", " # ...\n", " }\n", "\n", "s = bitstring.pack(fmt, **d)\n", "repr(s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parsing bitstreams\n", "\n", "You might have noticed that `pack` returned a `BitStream` rather than a `BitArray`.\n", "This isn't a problem as the `BitStream` class just adds a few stream-like qualities to `BitArray` which we'll take a quick look at here.\n", "\n", "The stream-ness of this object is via its bit position, and various reading and peeking methods.\n", "First let's try a read or two, and see how this affects the bit position:\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "BitStream('0x000001')\n", "24\n", "b3\n", "32\n" ] } ], "source": [ "print(s.pos)\n", "print(repr(s.read(24)))\n", "print(s.pos)\n", "print(s.read('hex8'))\n", "print(s.pos)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we read 24 bits, which returned a new `BitStream` object, then we used a format string to read 8 bits interpreted as a hexadecimal string.\n", "We know that the next two sets of 12 bits were created from integers, so to read them back we can say" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": "[352, 288]" }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.readlist('2*u12')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "If you don't want to use a bitstream then you can always use [`unpack`](https://bitstring.readthedocs.io/en/stable/bits.html#bitstring.Bits.unpack).\n", "This takes much the same form as [`readlist`](https://bitstring.readthedocs.io/en/stable/constbitstream.html#bitstring.ConstBitStream.readlist) except it just unpacks from the start of the bitstring.\n", "For example:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": "[b'\\x00\\x00\\x01\\xb3', 352, 288, 1]" }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s.unpack('bytes4, 2*u12, uint4')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Worked Examples\n", "\n", "Below are a few examples of using the bitstring module, as I always find that a good example can help more than a lengthy reference manual." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hamming distance\n", "\n", "The Hamming distance between two bitstrings is the number of bit positions in which the two bitstrings differ.\n", "So for example the distance between `0b00110` and `0b01100` is 2 as the second and fourth bits are different.\n", "\n", "Let's write a function that calculates the Hamming weight of two bitstrings.\n" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": "2" }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def hamming_weight(a, b):\n", " return (BitArray(a)^b).count(True)\n", "\n", "hamming_weight('0b00110', '0b01100')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Er, that's it. The `^` is a bit-wise exclusive or, which means that the bits in `a^b` are only set if they differ in `a` and `b`.\n", "The `count` method just counts the number of 1 (or True) bits." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sieve of Eratosthenes\n", "\n", "The sieve of Eratosthenes is an ancient (and _very_ inefficient) method of finding prime numbers.\n", "The algorithm starts with the number 2 (which is prime) and marks all of its multiples as not prime, it then continues with the next unmarked integer (which will also be prime) and marks all of its multiples as not prime.\n", "\n", "[![Sieve animation](https://upload.wikimedia.org/wikipedia/commons/b/b9/Sieve_of_Eratosthenes_animation.gif)](https://commons.wikimedia.org/wiki/File:Sieve_of_Eratosthenes_animation.svg)\n", "\n", "So to find all primes under a hundred million you could write:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "There are 5761455 primes less than 100000000,\n", "the largest one of which is 99999989\n", "and there are 440312 twin primes.\n" ] } ], "source": [ "# Create a BitArray with a hundred million 'one' bits\n", "limit = 100_000_000\n", "is_prime = BitArray(limit)\n", "is_prime.set(True)\n", "# Manually set 0 and 1 to be not prime.\n", "is_prime.set(False, [0, 1])\n", "# For every other integer, if it's set as prime then unset all of its multiples\n", "for i in range(2, math.ceil(math.sqrt(limit))):\n", " if is_prime[i]:\n", " is_prime.set(False, range(i*i, limit, i))\n", "\n", "print(f\"There are {is_prime.count(True)} primes less than {limit},\")\n", "print(f\"the largest one of which is {is_prime.rfind('0b1')[0]}\")\n", "print(f\"and there are {len(list(is_prime.findall('0b101')))} twin primes.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We find the largest prime with a reverse find ([`rfind`](https://bitstring.readthedocs.io/en/stable/bits.html#bitstring.Bits.rfind)) looking for a single set bit.\n", "For twin primes (primes which differ by 2) we use [`findall`](https://bitstring.readthedocs.io/en/stable/bits.html#bitstring.Bits.findall) to look for the binary sequence `101` which returns a generator for the bit positions.\n", "\n", "To see the pattern of the primes we could use the [pretty print](https://bitstring.readthedocs.io/en/stable/bits.html#bitstring.Bits.pp) method:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0: 00110101 00010100 01010001 00000101 00000100 01010001 35 14 51 05 04 51\n", " 48: 00000100 00010100 00010001 01000001 00010000 01000000 04 14 11 41 10 40\n", " 96: 01000101 00010100 01000000 00000001 00010000 01010000 45 14 40 01 10 50\n", " 144: 00000101 00000100 00010001 00000100 00010100 00000001 05 04 11 04 14 01\n", " 192: 01000101 00000000 00010000 00000001 00010100 01000001 45 00 10 01 14 41\n", " 240: 01000000 00010000 01000001 00000101 00000100 01010000 40 10 41 05 04 50\n", " 288: 00000100 00000000 00010001 01000100 00000000 00010000 04 00 11 44 00 10\n", " 336: 01000000 00010100 01000001 00000001 00000100 00010001 40 14 41 01 04 11\n", " 384: 00000100 00000100 01000000 01000000 00010100 00000001 04 04 40 40 14 01\n", " 432: 01000001 00010000 01000000 01000101 00010000 00000001 41 10 40 45 10 01\n", " 480: 00000001 00010000 00010001 00000100 00000000 01010000 01 10 11 04 00 50\n", " 528: 00000000 00000100 00010000 00000100 00010000 01010000 00 04 10 04 10 50\n", " 576: 01000000 00010000 01000001 01000001 00000100 01010000 40 10 41 41 04 50\n", " 624: 00000001 00000000 01010001 00000100 00010100 00000000 01 00 51 04 14 00\n", " 672: 01000100 00010000 00010000 00000100 00000100 00000001 44 10 10 04 04 01\n", " 720: 00000001 00000100 00010001 00000001 00000100 01000000 01 04 11 01 04 40\n", " 768: 01000100 00000000 00010000 00000100 00000000 01010000 44 00 10 04 00 50\n", " 816: 00000101 00010100 00000001 00000000 00000100 01010001 05 14 01 00 04 51\n", " 864: 00000000 00000100 01010001 00000000 00000000 00010001 00 04 51 00 00 11\n", " 912: 00000001 00000000 01000000 01000100 00010000 01000000 01 00 40 44 10 40\n", " 960: 00000001 00010000 01000001 00000001 00000100 01 10 41 01 04 \n" ] } ], "source": [ "is_prime[0:1000].pp('bin, hex', width=88)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I'll leave optimising the algorithm as an exercise for the reader, but it illustrates both bit checking and setting.\n", "One reason you might want to use a bitstring for this purpose (instead of a plain list for example) is that the million bits only take up a million bits in memory, whereas for a list of integers it would be much more." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Next Steps\n", "\n", "There is fairly extensive [documentation](https://bitstring.readthedocs.io/en/stable/) available.\n", "The [Quick Reference](https://bitstring.readthedocs.io/en/stable/quick_reference.html) is a good place to quickly see what's available.\n", "\n", "For any discussions / bug reports / feature requests see the homepage on [GitHub](https://github.com/scott-griffiths/bitstring/)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 1 } bitstring-bitstring-4.2.3/generate_luts.py000066400000000000000000000046411462444752600207660ustar00rootroot00000000000000""" This script generates the LUTs used for the various exotic floating point formats. Its output is the luts.py file which is then used by the bitstring package. """ import pprint import zlib import shutil from bitstring.mxfp import MXFPFormat from bitstring.fp8 import Binary8Format if __name__ == '__main__': mxfps = [MXFPFormat(exp_bits=2, mantissa_bits=1, bias=1, mxfp_overflow='saturate'), MXFPFormat(exp_bits=2, mantissa_bits=3, bias=1, mxfp_overflow='saturate'), MXFPFormat(exp_bits=3, mantissa_bits=2, bias=3, mxfp_overflow='saturate'), MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='saturate'), MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='saturate'), MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='overflow'), MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='overflow') ] binary8s = [Binary8Format(exp_bits=4, bias=8), Binary8Format(exp_bits=5, bias=16) ] with open('./bitstring/luts_temp.py', 'w') as f: f.write("#\n# This file is generated by generate_luts.py. DO NOT EDIT.\n#\n\n") mxfp_luts_compressed = {} for mxfp in mxfps: print(f"generating LUT for {mxfp}") mxfp.create_luts() lut_int_to_float_compressed = zlib.compress(mxfp.lut_int_to_float, 1) lut_float16_to_mxfp_compressed = zlib.compress(mxfp.lut_float16_to_mxfp, 1) mxfp_luts_compressed[(mxfp.exp_bits, mxfp.mantissa_bits, mxfp.bias, mxfp.mxfp_overflow)] = (lut_int_to_float_compressed, lut_float16_to_mxfp_compressed) f.write("mxfp_luts_compressed = \\\n") pprint.pp(mxfp_luts_compressed, width=120, stream=f) binary8_luts_compressed = {} for binary8 in binary8s: print(f"generating LUT for {binary8}") binary8.create_luts() lut_binary8_to_float_compressed = zlib.compress(binary8.lut_binary8_to_float, 1) lut_float16_to_binary8_compressed = zlib.compress(binary8.lut_float16_to_binary8, 1) binary8_luts_compressed[(binary8.exp_bits, binary8.bias)] = (lut_binary8_to_float_compressed, lut_float16_to_binary8_compressed) f.write("\n\nbinary8_luts_compressed = \\\n") pprint.pp(binary8_luts_compressed, width=120, stream=f) shutil.move('./bitstring/luts_temp.py', './bitstring/luts.py')bitstring-bitstring-4.2.3/pyproject.toml000066400000000000000000000022031462444752600204570ustar00rootroot00000000000000[build-system] requires = ["setuptools>=61"] build-backend = "setuptools.build_meta" [project] name = "bitstring" version = "4.2.3" authors = [ { name="Scott Griffiths", email="dr.scottgriffiths@gmail.com" }, ] description = "Simple construction, analysis and modification of binary data." readme = "README.md" requires-python = ">=3.8" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Libraries :: Python Modules", ] keywords = ["binary", "bitarray", "bitvector", "bitfield"] dependencies = [ "bitarray >= 2.9.0, < 3.0.0", ] [project.urls] homepage = "https://github.com/scott-griffiths/bitstring" documentation = "https://bitstring.readthedocs.io/" [tool.setuptools] packages = ["bitstring"] bitstring-bitstring-4.2.3/release_notes.md000066400000000000000000002014531462444752600207250ustar00rootroot00000000000000 # Release Notes ### May 2024: version 4.2.3 #### More small bug fixes related to some of the new (beta) float formats. * Some codes representing exotic float negative zero converted to positive zero. Bug #333. * Auto-scaling rounding the wrong way on occasion. Bug #334. ### May 2024: version 4.2.2 #### A couple more minor bug fixes. * Sometimes a `ValueError` was being raised instead of a `ReadError`. Bug #325. * Initialising a bitstring from `None` now raises a `TypeError` rather than generating an empty bitstring. Bug #323. * Fixed performance regression for `find`/`findall` in some situations. Bug #326. * Fix for `AttributeError` bug when combining `Bits` with `BitStream`. Bug #329. ### April 2024: version 4.2.1 #### Fixing a few regressions introduced in 4.2.0. * Module crashes on import with 32-bit Python. Bug #317. * Lists of integers not converted to bytes when using the bytes constructor. Bug #318. * Empty comma separated tokens not handled correctly. Bug #319. * Crash on import when docstrings not present due to optimize flag. Bug #321. ## April 2024: version 4.2.0 This release contains a fairly large refactor of how different types are managed. This shouldn't affect the end user, and the main noticeable change should be the new Dtype class, which is optional to use. Support for 8-bit and smaller floats has been reworked and expanded. These are still a 'beta' feature. #### Backwardly incompatible changes * Dropped support for Python 3.7. Minimum version is now 3.8. * For tokens that use a non-numeric length, a `':'` is now compulsory rather than recommended. For example use `'uint:foo'` instead of `'uintfoo'`. * The previous `e4m3float` and `e5m2float` formats have become the slightly modified `p4binary8` and `p3binary8` formats. * Some parameters are now enforced as positional only, such as `auto` in constructors. #### Other changes * The `Array` class is no longer 'beta'. * A new `Dtype` class can be optionally used to specify types. * The `bitstring.options` object is now the preferred method for changing module options. The `bitstring.lsb0` and `bitstring.bytealigned` variables are now deprecated, use `bitstring.options.lsb0` and `bitstring.options.bytealigned` instead. * New `fromstring` method as another way to create bitstrings from formatted strings. Instead of relying on the `auto` parameter you can now optionally use `fromstring`. ```pycon >>> s1 = BitArray('u24=1000') # This is still fine. >>> s2 = BitArray.fromstring('u24=1000') # This may be clearer and more efficient. ``` * More types can now be pretty printed. For example integer and float formats can be used. ```pycon >>> s.pp('u15, bin') ``` * Pretty printing is now prettier - optional terminal colours added. * A range of 8-bit, 6-bit and even 4-bit float formats added (beta): * `p3binary8`: IEEE 8-bit floating point with 3 bit precision. * `p4binary8`: IEEE 8-bit floating point with 4 bit precision. * `e5m2mxfp`: OCP 8-bit floating point with 3 bit precision. * `e4m3mxfp`: OCP 8-bit floating point with 4 bit precision. * `e2m3mxfp`: OCP 6-bit floating point with 4 bit precision. * `e3m2mxfp`: OCP 6-bit floating point with 3 bit precision. * `e2m1mxfp`: OCP 4-bit floating point with 2 bit precision. * `e8m0mxfp`: OCP 8-bit unsigned floating point designed to scale the other formats. * `mxint`: OCP 8-bit floating point that is a scaled integer representation. * Performance improvements. ### November 2023: version 4.1.4 Fixing a regression introduced in 4.1.3 * `'bytes'` token can't be used without explicit length. Bug #303. ### November 2023: version 4.1.3 A maintenance release, with some changes to the beta features introduced in 4.1. * Removed a couple of files that accidentally got included in the previous release. Bug #293. * The 8-bit float formats have been renamed `e4m3float` and `e5m2float`. * Some refactoring and performance optimizations. ### September 2023: version 4.1.2 released Another maintenance release. Once again some small changes to the 'beta' `Array` class, plus new Array functionality. * Fix for the module command-line usage. Bug #290. * Fix for when creating bitstrings from `memoryview` objects. * Renamed the `fmt` parameter for Arrays to `dtype`. * More Array operator coverage. * Added operators that act on two Arrays of the same size. * Added comparison operators for Arrays that return an Array of bools. * Added `Array.equals` method as `==` will now return an `Array` (see above item). * Added `astype()` method for Arrays to easily cast to a new dtype. ### August 2023: version 4.1.1 released A maintenance release, with some changes to the Array class which is still in beta. * bitarray dependency now pinned to `">=2.8.0, <3.0.0"` rather than a specific version. Bug #283. * Fix for using numpy integers as integer parameters. Bug #286. * Removed ability to extend an Array with the `+` operator. Use the `extend` method instead. * Improvements when pretty-printing the Array. * `Array.count()` can now count `float('nan')` values for floating point types. ## August 2023: version 4.1.0 released This has turned into a surprisingly big release, with a major refactor and a brand-new class (the first for 12 years!) There are also a couple of small possibly breaking changes detailed below, in particular 'auto' initialising bitstrings from integers is now disallowed. #### Speed increased with bitarray dependency. The major weakness of bitstring has been its poor performance for computationally intensive tasks relative to lower level alternatives. This was principally due to relying on pure Python code to achieve things that the base language often didn't have fast ways of doing. This release starts to address that problem with a fairly extensive rewrite to replace much of the pure Python low-level bit operations with methods from the bitarray package. This is a package that does many of the same things as bitstring, and the two packages have co-existed for a long time. While bitarray doesn't have all of the options and facilities of bitstring it has the advantage of being very fast as it is implemented in C. By replacing the internal datatypes I can speed up bitstring's operations while keeping the same API. Huge kudos to Ilan Schnell for all his work on bitarray. #### New Array class for homogeneous data (beta) If your data is all of a single type you can make use of the new `Array` class, which mirrors much of the functionality of the standard `array.array` type, but doesn't restrict you to just a dozen formats. ```pycon >>> from bitstring import Array >>> a = Array('uint7', [9, 100, 3, 1]) >>> a.data BitArray('0x1390181') >>> b = Array('float16', a.tolist()) >>> b.append(0.25) >>> b.tobytes() b'H\x80V@B\x00<\x004\x00' >>> b.tolist() [9.0, 100.0, 3.0, 1.0, 0.25] ``` The data is stored efficiently in a `BitArray` object, and you can manipulate both the data and the `Array` format freely. See the main documentation for more details. Note that this feature carries the 'beta' flag so may change in future point versions. #### Other changes * Added two new floating point interpretations: `float8_143` and `float8_152`. These are 8-bit floating point formats, with very limited range and precision, but useful in some fields, particularly machine learning. This is an experimental feature - the formats haven't even been standardised yet. ```pycon >>> a = Bits(float8_143=16.5) >>> a.bin '01100000' >>> a.float8_143 16.0 ``` * Auto initialization from ints has been removed and now raises a `TypeError`. Creating a bitstring from an int still creates a zeroed bitstring of that length but ints won't be promoted to bitstrings as that has been a constant source of errors and confusion. ```pycon >>> a = BitArray(100) # Fine - create with 100 zeroed bits >>> a += 0xff # TypeError - previously this would have appended 0xff (=255) zero bits. >>> a += '0xff' # Probably what was meant - append eight '1' bits. >>> a += Bits(255) # Fine, append 255 zero bits. ``` This is a breaking change, but it breaks loudly with an exception, it is easily recoded, and it removes a confusing wrinkle. * Explicitly specifying the `auto` parameter is now disallowed rather than discouraged. It was always meant to be a positional-only parameter (and will be once I can drop Python 3.7 support) but for now it's renamed to `__auto`. In the unlikely event this breaks code, the fix should be just to delete the `auto=` if it's already the first parameter. ```pycon >>> s = Bits(auto='0xff') # Now raises a CreationError >>> s = Bits('0xff') # Fine, as always ``` * Deleting, replacing or inserting into a bitstring resets the bit position to 0 if the bitstring's length has been changed. Previously the bit position was adjusted but this was not well-defined. * Only empty bitstring are now considered false in a boolean sense. Previously `s` was `False` if no bits in `s` were set to `1`, but this goes against what it means to be a container in Python, so I consider this to be a bug, even if it was documented. I'm guessing it's related to `__nonzero__` in Python 2 becoming `__bool__` in Python 3, and it's never been fixed before now. * Casting to `bytes` now behaves as expected, so that `bytes(s)` gives the same result as `s.tobytes()`. Previously it created a byte per bit. * Pretty printing with the `'bytes'` format now uses characters from the 'Latin Extended-A' unicode block for non-ASCII and unprintable characters instead of replacing them with `'.'` * When using struct-like codes you can now use `'='` instead of `'@'` to signify native- endianness. They behave identically, but the new `'='` is now preferred. * More fixes for LSB0 mode. There are now no known issues with this feature. ### April 2023: version 4.0.2 released #### A maintenance release * Added `py.typed` file and converted the module to a package to let mypy find type annotations. Bug 248. * Fix to shifting operations when using LSB0 mode. Bug 251. * A few more fixes for LSB0 mode. * Improved LSB0 documentation. * Added build-system section to `pyproject.toml`. Bug 243. * Rewrote the walkthrough documentation as a jupyter notebook. * Updated the project's logo. ## November 2022: version 4.0 released This is a major release which drops support for Python 2.7 and has a new minimum requirement of Python 3.7. Around 95% of downloads satisfy this - users of older versions can continue to use bitstring 3.1, which will still be supported with fixes, but no new features. Other breaking changes are minimal, and there are a few cool features added. #### Breaking changes * Minimum supported Python version is now Python 3.7. * Removed `ConstBitArray` and `BitString` class aliases. Use `Bits` and `BitStream` instead. * The `cut()` method will now also yield the final bits of a bitstring, even if they are shorter than the requested cut size. * Removed default `uint` interpretation. This wasn't being applied uniformly - the default is now always to return a bitstring object of the given length and not to interpret it as a `uint`. Bug 220. * If an overwrite goes beyond the end of the bitstring it will now extend the bitstring rather than raise an exception. Bug 148. #### New features and improvements * Type hints added throughout the code. * Underscores are now allowed in strings representing number literals. * The `copy()` method now works on `Bits` as well as `BitArray` objects. * The experimental command-line feature is now official. Command-line parameters are concatenated and a bitstring created from them. If the final parameter is either an interpretation string or ends with a `'.'` followed by an interpretation string then that interpretation of the bitstring will be used when printing it. ```pycon $ python -m bitstring int:16=-400 0xfe70 $ python -m bitstring float:32=0.2 bin 00111110010011001100110011001101 ``` * New `pp()` method that pretty-prints the bitstring in various formats - useful especially in interactive sessions. Thanks to Omer Barak for the suggestion and discussion. ```pycon >>> s.pp() 0: 10001000 01110110 10001110 01110110 11111000 01110110 10000111 00101000 64: 01110010 11111001 10000111 10011000 11110111 10011110 10000111 11111101 128: 11111001 10001100 01111111 10111100 10111111 11011011 11101011 11111011 192: 1100 >>> s.pp('bin, hex') 0: 10001000 01110110 10001110 01110110 11111000 01110110 88 76 8e 76 f8 76 48: 10000111 00101000 01110010 11111001 10000111 10011000 87 28 72 f9 87 98 96: 11110111 10011110 10000111 11111101 11111001 10001100 f7 9e 87 fd f9 8c 144: 01111111 10111100 10111111 11011011 11101011 11111011 7f bc bf db eb fb 192: 1100 c ``` * Shorter and more versatile properties. The `bin`, `oct`, `hex`, `float`, `uint` and `int` properties can now be shortened to just their first letter. They can also have a length in bits after them - allowing Rust-like data types. ```pycon >>> s = BitArray('0x44961000') >>> s.h '44961000' >>> s.f32 1200.5 >>> s.u 1150685184 >>> s.i7 = -60 >>> s.b '1000100' >>> t = Bits('u12=160, u12=120, b=100') ``` * Other types with bit lengths can also be used as properties: ```pycon >>> s.floatle64 = 10.511 ``` * A colon is no longer required in format strings before a bit length. So for example `Bits('int:15=-101')` could be written as `Bits('int15=-101')`. This is now the preferred usage in the documentation except where the colon improves readability. * Support for IEEE 16 bit floats. Floating point types can now be 16 bits long as well as 32 and 64 bits. This is using the `'e'` format from the struct module. * Support for bfloats. This is a specialised 16-bit floating point format mostly used in machine learning. It's essentially a truncated IEEE 32-bit format that keeps its range but only has a couple of significant figures of accuracy. ### July 20th 2021: version 3.1.9 released (version 3.1.8 was pulled due to serious issues) Another maintenance release. * Fixed a couple of outdated results in the readme (Issue 214). * Some more documentation tidying. * Turned off some debug code by default. * Fixed a couple of failing tests in different Python versions. * Fix for consistent pos initialisation semantics for different types. * Change to allow wheels to be uploaded to PyPI. * More work for LSB0 mode, but still not finished or documented (sorry). ### May 5th 2020: version 3.1.7 released This is a maintenance release with a few bug fixes plus an experimental feature to allow bits to be indexed in the opposite direction. * Fixing del not working correctly when stop value negative (Issue 201) * Removed deprecated direct import of ABC from collections module (Issue 196) * Tested and added explicit support for Python 3.7 and 3.8. (Issue 193) * Fixing a few stale links to documentation. (Issue 194) * Allowing initialisation with an io.BytesIO object. (Issue 189) #### Experimental LSB0 mode This feature allows bitstring to use Least Significant Bit Zero (LSB0) bit numbering; that is the final bit in the bitstring will be bit `0`, and the first bit will be bit `(n-1)`, rather than the other way around. LSB0 is a more natural numbering system in many fields, but is the opposite to Most Significant Bit Zero (MSB0) numbering which is the natural option when thinking of bitstrings as standard Python containers. To switch from the default MSB0, use the module level function ```pycon >>> bitstring.set_lsb0(True) ``` Getting and setting bits should work in this release, as will some other methods. Many other methods are not tested yet and might not work as expected. This is mostly a release to get feedback before finalising the interface. Slicing is still done with the start bit smaller than the end bit. For example: ```pycon >>> s = Bits('0b000000111') >>> s[0:5] Bits('0b00111') >>> s[0] True ``` Negative indices work as (hopefully) you'd expect, with the first stored bit being `s[-1]` and the final stored bit being `s[-n]`. See https://github.com/scott-griffiths/bitstring/issues/156 for discussions and to add any further comments. ### July 9th 2019: version 3.1.6 released A long overdue maintenance release with some fixes. * Fixed immutability bug. Bug 176. * Fixed failure of `__contains__` in some circumstances. Bug 180. * Better handling of open files. Bug 186. * Better Python 2/3 check. * Making unit tests easier to run. * Allowing length of 1 to be specified for bools. (Thanks to LemonPi) ### May 17th 2016: version 3.1.5 released * Support initialisation from an array. * Added a separate LICENSE file. ### March 19th 2016: version 3.1.4 released This is another bug fix release. * Fix for bitstring types when created directly from other bitstring types. * Updating contact, website details. ### March 4th 2014: version 3.1.3 released This is another bug fix release. * Fix for problem with prepend for bitstrings with byte offsets in their data store. ### April 18th 2013: version 3.1.2 released This is another bug fix release. * Fix for problem where unpacking bytes would by eight times too long ### March 21st 2013: version 3.1.1 released This is a bug fix release. * Fix for problem where concatenating bitstrings sometimes modified method's arguments ## February 26th 2013: version 3.1.0 released This is a minor release with a couple of new features and some bug fixes. #### New 'pad' token This token can be used in reads and when packing/unpacking to indicate that you don't care about the contents of these bits. Any padding bits will just be skipped over when reading/unpacking or zero-filled when packing. ```pycon >>> a, b = s.readlist('pad:5, uint:3, pad:1, uint:3') ``` Here only two items are returned in the list - the padding bits are ignored. #### New clear and copy convenience methods These methods have been introduced in Python 3.3 for lists and bytearrays, as more obvious ways of clearing and copying, and we mirror that change here. `t = s.copy()` is equivalent to `t = s[:]`, and `s.clear()` is equivalent to `del s[:]`. #### Other changes * Some bug fixes. ### February 7th 2012: version 3.0.2 released This is a minor update that fixes a few bugs. * Fix for subclasses of bitstring classes behaving strangely (Issue 121). * Fix for excessive memory usage in rare cases (Issue 120). * Fixes for slicing edge cases. There has also been a reorganisation of the code to return it to a single `bitstring.py` file rather than the package that has been used for the past several releases. This change shouldn't affect users directly. ### November 21st 2011: version 3.0.1 released This release fixed a small but very visible bug in bitstring printing. ## November 21st 2011: version 3.0.0 released This is a major release which breaks backward compatibility in a few places. ### Backwardly incompatible changes #### Hex, oct and bin properties don't have leading 0x, 0o and 0b If you ask for the hex, octal or binary representations of a bitstring then they will no longer be prefixed with `'0x'`, `'0o'` or `'0b'`. This was done as it was noticed that the first thing a lot of user code does after getting these representations was to cut off the first two characters before further processing. ```pycon >>> a = BitArray('0x123') >>> a.hex, a.oct, a.bin ('123', '0443', '000100100011') ``` Previously this would have returned `('0x123', '0o0443', '0b000100100011')` This change might require some recoding, but it should all be simplifications. #### ConstBitArray renamed to Bits Previously `Bits` was an alias for `ConstBitStream` (for backward compatibility). This has now changed so that `Bits` and `BitArray` loosely correspond to the built-in types `bytes` and `bytearray`. If you were using streaming/reading methods on a `Bits` object then you will have to change it to a `ConstBitStream`. The `ConstBitArray` name is kept as an alias for `Bits`. #### Stepping in slices has conventional meaning The step parameter in `__getitem__`, `__setitem__` and `__delitem__` used to act as a multiplier for the start and stop parameters. No one seemed to use it though and so it has now reverted to the conventional meaning for containers. If you are using step then recoding is simple: `s[a:b:c]` becomes `s[a*c:b*c]`. Some examples of the new usage: ```pycon >>> s = BitArray('0x0000') s[::4] = [1, 1, 1, 1] >>> s.hex '8888' >>> del s[8::2] >>> s.hex '880' ``` ### New features #### New readto method This method is a mix between a find and a read - it searches for a bitstring and then reads up to and including it. For example: ```pycon >>> s = ConstBitStream('0x47000102034704050647') >>> s.readto('0x47', bytealigned=True) BitStream('0x47') >>> s.readto('0x47', bytealigned=True) BitStream('0x0001020347') >>> s.readto('0x47', bytealigned=True) BitStream('0x04050647') ``` #### pack function accepts an iterable as its format Previously only a string was accepted as the format in the `pack` function. This was an oversight as it broke the symmetry between `pack` and `unpack`. Now you can use formats like this: ```python fmt = ['hex:8', 'bin:3'] a = pack(fmt, '47', '001') a.unpack(fmt) ``` ## June 18th 2011: version 2.2.0 released This is a minor upgrade with a couple of new features. ### New interleaved exponential-Golomb interpretations New bit interpretations for interleaved exponential-Golomb (as used in the Dirac video codec) are supplied via `uie` and `sie`: ```pycon >>> s = BitArray(uie=41) >>> s.uie 41 >>> s.bin '0b00010001001' ``` These are pretty similar to the non-interleaved versions - see the manual for more details. Credit goes to Paul Sargent for the patch. ### New package-level bytealigned variable A number of methods take a `bytealigned` parameter to indicate that they should only work on byte boundaries (e.g. `find`, `replace`, `split`). Previously this parameter defaulted to `False`. Instead, it now defaults to `bitstring.bytealigned`, which itself defaults to `False`, but can be changed to modify the default behaviour of the methods. For example: ```pycon >>> a = BitArray('0x00 ff 0f ff') >>> a.find('0x0f') (4,) # found first not on a byte boundary >>> a.find('0x0f', bytealigned=True) (16,) # forced looking only on byte boundaries >>> bitstring.bytealigned = True # Change default behaviour >>> a.find('0x0f') (16,) >>> a.find('0x0f', bytealigned=False) (4,) ``` If you're only working with bytes then this can help avoid some errors and save some typing! ### Other changes * Fix for Python 3.2, correcting for a change to the binascii module. * Fix for bool initialisation from 0 or 1. * Efficiency improvements, including interning strategy. ### February 23rd 2011: version 2.1.1 released This is a release to fix a couple of bugs that were introduced in 2.1.0. * Bug fix: Reading using the `'bytes'` token had been broken (Issue 102). * Fixed problem using some methods on `ConstBitArrays`. * Better exception handling for tokens missing values. * Some performance improvements. ## January 23rd 2011: version 2.1.0 released ### New class hierarchy introduced with simpler classes Previously there were just two classes, the immutable `Bits` which was the base class for the mutable `BitString` class. Both of these classes have the concept of a bit position, from which reads etc. take place so that the bitstring could be treated as if it were a file or stream. Two simpler classes have now been added which are purely bit containers and don't have a bit position. These are called `ConstBitArray` and `BitArray`. As you can guess the former is an immutable version of the latter. The other classes have also been renamed to better reflect their capabilities. Instead of `BitString` you can use `BitStream`, and instead of `Bits` you can use `ConstBitStream`. The old names are kept as aliases for backward compatibility. The classes hierarchy is: ``` ConstBitArray / \ / \ BitArray ConstBitStream (formerly Bits) \ / \ / BitStream (formerly BitString) ``` ### Other changes A lot of internal reorganisation has taken place since the previous version, most of which won't be noticed by the end user. Some things you might see are: * New package structure. Previous versions have been a single file for the module and another for the unit tests. The module is now split into many more files so it can't be used just by copying `bitstring.py` any more. * To run the unit tests there is now a script called `runtests.py` in the test directory. * File based bitstring are now implemented in terms of an mmap. This should be just an implementation detail, but unfortunately for 32-bit versions of Python this creates a limit of 4GB on the files that can be used. The work around is either to get a 64-bit Python, or just stick with version 2.0. * The `ConstBitArray` and `ConstBitStream` classes no longer copy byte data when a slice or a read takes place, they just take a reference. This is mostly a very nice optimisation, but there are occasions where it could have an adverse effect. For example if a very large bitstring is created, a small slice taken and the original deleted. The byte data from the large bitstring would still be retained in memory. * Optimisations. Once again this version should be faster than the last. The module is still pure Python but some of the reorganisation was to make it more feasible to put some of the code into Cython or similar, so hopefully more speed will be on the way. ### July 26th 2010: version 2.0.3 released * Bug fix: Using peek and read for a single bit now returns a new bitstring as was intended, rather than the old behaviour of returning a bool. * Removed HTML docs from source archive - better to use the online version. ## July 2010: version 2.0 released This is a major release, with a number of backwardly incompatible changes. The main change is the removal of many methods, all of which have simple alternatives. Other changes are quite minor but may need some recoding. There are a few new features, most of which have been made to help the stream-lining of the API. As always there are performance improvements and some API changes were made purely with future performance in mind. ### Backwardly incompatible changes #### Methods removed. About half of the class methods have been removed from the API. They all have simple alternatives, so what remains is more powerful and easier to remember. The removed methods are listed here on the left, with their equivalent replacements on the right: ``` s.advancebit() -> s.pos += 1 s.advancebits(bits) -> s.pos += bits s.advancebyte() -> s.pos += 8 s.advancebytes(bytes) -> s.pos += 8*bytes s.allunset([a, b]) -> s.all(False, [a, b]) s.anyunset([a, b]) -> s.any(False, [a, b]) s.delete(bits, pos) -> del s[pos:pos+bits] s.peekbit() -> s.peek(1) s.peekbitlist(a, b) -> s.peeklist([a, b]) s.peekbits(bits) -> s.peek(bits) s.peekbyte() -> s.peek(8) s.peekbytelist(a, b) -> s.peeklist([8*a, 8*b]) s.peekbytes(bytes) -> s.peek(8*bytes) s.readbit() -> s.read(1) s.readbitlist(a, b) -> s.readlist([a, b]) s.readbits(bits) -> s.read(bits) s.readbyte() -> s.read(8) s.readbytelist(a, b) -> s.readlist([8*a, 8*b]) s.readbytes(bytes) -> s.read(8*bytes) s.retreatbit() -> s.pos -= 1 s.retreatbits(bits) -> s.pos -= bits s.retreatbyte() -> s.pos -= 8 s.retreatbytes(bytes) -> s.pos -= 8*bytes s.reversebytes(start, end) -> s.byteswap(0, start, end) s.seek(pos) -> s.pos = pos s.seekbyte(bytepos) -> s.bytepos = bytepos s.slice(start, end, step) -> s[start:end:step] s.tell() -> s.pos s.tellbyte() -> s.bytepos s.truncateend(bits) -> del s[-bits:] s.truncatestart(bits) -> del s[:bits] s.unset([a, b]) -> s.set(False, [a, b]) ``` Many of these methods have been deprecated for the last few releases, but there are some new removals too. Any recoding needed should be quite straightforward, so while I apologise for the hassle, I had to take the opportunity to streamline and rationalise what was becoming a bit of an overblown API. #### set / unset methods combined. The set/unset methods have been combined in a single method, which now takes a boolean as its first argument: ``` s.set([a, b]) -> s.set(1, [a, b]) s.unset([a, b]) -> s.set(0, [a, b]) s.allset([a, b]) -> s.all(1, [a, b]) s.allunset([a, b]) -> s.all(0, [a, b]) s.anyset([a, b]) -> s.any(1, [a, b]) s.anyunset([a, b]) -> s.any(0, [a, b]) ``` #### all / any only accept iterables. The `all` and `any` methods (previously called `allset`, `allunset`, `anyset` and `anyunset`) no longer accept a single bit position. The recommended way of testing a single bit is just to index it, for example instead of: ```pycon >>> if s.all(True, i): ``` just use ```pycon >>> if s[i]: ``` If you really want to you can of course use an iterable with a single element, such as `s.any(False, [i])`, but it's clearer just to write `not s[i]`. #### Exception raised on reading off end of bitstring. If a read or peek goes beyond the end of the bitstring then a `ReadError` will be raised. The previous behaviour was that the rest of the bitstring would be returned and no exception raised. #### BitStringError renamed to Error. The base class for errors in the bitstring module is now just `Error`, so it will likely appear in your code as `bitstring.Error` instead of the rather repetitive `bitstring.BitStringError`. #### Single bit slices and reads return a bool. A single index slice (such as `s[5]`) will now return a bool (i.e. `True` or `False`) rather than a single bit bitstring. This is partly to reflect the style of the bytearray type, which returns an integer for single items, but mostly to avoid common errors like: ```pycon >>> if s[0]: ... do_something() ``` While the intent of this code snippet is quite clear (i.e. do_something if the first bit of s is set) under the old rules `s[0]` would be true as long as `s` wasn't empty. That's because any one-bit bitstring was true as it was a non-empty container. Under the new rule `s[0]` is `True` if `s` starts with a `1` bit and `False` if `s` starts with a `0` bit. The change does not affect reads and peeks, so `s.peek(1)` will still return a single bit bitstring, which leads on to the next item... #### Empty bitstrings or bitstrings with only zero bits are considered False. Previously a bitstring was `False` if it had no elements, otherwise it was `True`. This is standard behaviour for containers, but wasn't very useful for a container of just 0s and 1s. The new behaviour means that the bitstring is False if it has no `1` bits. This means that code like this: ```pycon >>> if s.peek(1): ... do_something() ``` should work as you'd expect. It also means that `Bits(1000)`, `Bits(0x00)` and `Bits('uint:12=0')` are all also `False`. If you need to check for the emptiness of a bitstring then instead check the len property: ``` if s -> if s.len if not s -> if not s.len ``` #### Length and offset disallowed for some initialisers. Previously you could create bitstring using expressions like: ```pycon >>> s = Bits(hex='0xabcde', offset=4, length=13) ``` This has now been disallowed, and the offset and length parameters may only be used when initialising with bytes or a file. To replace the old behaviour you could instead use ```pycon >>> s = Bits(hex='0xabcde')[4:17] ``` #### Renamed 'format' parameter 'fmt'. Methods with a `format` parameter have had it renamed to `fmt`, to prevent hiding the built-in `format`. Affects methods `unpack`, `read`, `peek`, `readlist`, `peeklist` and `byteswap` and the `pack` function. #### Iterables instead of *format accepted for some methods. This means that for the affected methods (`unpack`, `readlist` and `peeklist`) you will need to use an iterable to specify multiple items. This is easier to show than to describe, so instead of ```pycon >>> a, b, c, d = s.readlist('uint:12', 'hex:4', 'bin:7') ``` you would instead write ```pycon >>> a, b, c, d = s.readlist(['uint:12', 'hex:4', 'bin:7']) ``` Note that you could still use the single string `'uint:12, hex:4, bin:7'` if you preferred. #### Bool auto-initialisation removed. You can no longer use `True` and `False` to initialise single bit bitstrings. The reasoning behind this is that as `bool` is a subclass of `int`, it really is bad practice to have `Bits(False)` be different to `Bits(0)` and to have `Bits(True)` different to `Bits(1)`. If you have used bool auto-initialisation then you will have to be careful to replace it as the bools will now be interpreted as ints, so `Bits(False)` will be empty (a bitstring of length 0), and `Bits(True)` will be a single zero bit (a bitstring of length 1). Sorry for the confusion, but I think this will prevent bigger problems in the future. There are a few alternatives for creating a single bit bitstring. My favourite it to use a list with a single item: ``` Bits(False) -> Bits([0]) Bits(True) -> Bits([1]) ``` #### New creation from file strategy Previously if you created a bitstring from a file, either by auto-initialising with a file object or using the filename parameter, the file would not be read into memory unless you tried to modify it, at which point the whole file would be read. The new behaviour depends on whether you create a `Bits` or a `BitString` from the file. If you create a `Bits` (which is immutable) then the file will never be read into memory. This allows very large files to be opened for examination even if they could never fit in memory. If however you create a `BitString`, the whole of the referenced file will be read to store in memory. If the file is very big this could take a long time, or fail, but the idea is that in saying you want the mutable `BitString` you are implicitly saying that you want to make changes and so (for now) we need to load it into memory. The new strategy is a bit more predictable in terms of performance than the old. The main point to remember is that if you want to open a file and don't plan to alter the bitstring then use the `Bits` class rather than `BitString`. Just to be clear, in neither case will the contents of the file ever be changed - if you want to output the modified `BitString` then use the `tofile` method, for example. #### find and rfind return a tuple instead of a bool. If a find is unsuccessful then an empty tuple is returned (which is `False` in a boolean sense) otherwise a single item tuple with the bit position is returned (which is `True` in a boolean sense). You shouldn't need to recode unless you explicitly compared the result of a `find` to `True` or `False`, for example this snippet doesn't need to be altered: ```pycon >>> if s.find('0x23'): ... print(s.bitpos) ``` but you could now instead use ```pycon >>> found = s.find('0x23') >>> if found: ... print(found[0]) ``` The reason for returning the bit position in a tuple is so that finding at position zero can still be `True` - it's the tuple `(0,)` - whereas not found can be False - the empty tuple `()`. ### New features #### New count method. This method just counts the number of 1 or 0 bits in the bitstring. ```pycon >>> s = Bits('0x31fff4') >>> s.count(1) 16 ``` #### read and peek methods accept integers. The `read`, `readlist`, `peek` and `peeklist` methods now accept integers as parameters to mean "read this many bits and return a bitstring". This has allowed a number of methods to be removed from this release, so for example instead of: ```pycon >>> a, b, c = s.readbits(5, 6, 7) >>> if s.peekbit(): ... do_something() ``` you should write: ```pycon >>> a, b, c = s.readlist([5, 6, 7]) >>> if s.peek(1): ... do_something() ``` #### byteswap used to reverse all bytes. The `byteswap` method now allows a format specifier of 0 (the default) to signify that all of the whole bytes should be reversed. This means that calling just `byteswap()` is almost equivalent to the now removed `bytereverse()` method (a small difference is that `byteswap` won't raise an exception if the bitstring isn't a whole number of bytes long). #### Auto initialise with bytearray or (for Python 3 only) bytes. So rather than writing: ```pycon >>> a = Bits(bytes=some_bytearray) ``` you can just write ```pycon >>> a = Bits(some_bytearray) ``` This also works for the `bytes` type, but only if you're using Python 3. For Python 2 it's not possible to distinguish between a `bytes` object and a `str`. For this reason this method should be used with some caution as it will make you code behave differently with the different major Python versions. ```pycon >>> b = Bits(b'abcd\x23\x00') # Only Python 3! ``` #### set, invert, all and any default to whole bitstring. This means that you can for example write: ```pycon >>> a = BitString(100) # 100 zero bits >>> a.set(1) # set all bits to 1 >>> a.all(1) # are all bits set to 1? True >>> a.any(0) # are any set to 0? False >>> a.invert() # invert every bit ``` #### New exception types. As well as renaming `BitStringError` to just `Error` there are also new exceptions which use Error as a base class. These can be caught in preference to `Error` if you need finer control. The new exceptions sometimes also derive from built-in exceptions: ``` ByteAlignError(Error) # whole byte position or length needed. ReadError(Error, IndexError) # reading or peeking off the end of the bitstring. CreationError(Error, ValueError) # inappropriate argument during bitstring creation. InterpretError(Error, ValueError) # inappropriate interpretation of binary data. ``` ## March 18th 2010: version 1.3.0 for Python 2.6 and 3.x released ### New features #### byteswap method for changing endianness. Changes the endianness in-place according to a format string or integer(s) giving the byte pattern. See the manual for details. ```pycon >>> s = BitString('0x00112233445566') >>> s.byteswap(2) 3 >>> s BitString('0x11003322554466') >>> s.byteswap('h') 3 >>> s BitString('0x00112233445566') >>> s.byteswap([2, 5]) 1 >>> s BitString('0x11006655443322') ``` #### Multiplicative factors in bitstring creation and reading. For example: ```pycon >>> s = Bits('100*0x123') ``` #### Token grouping using parenthesis. For example: ```pycon >>> s = Bits('3*(uint:6=3, 0b1)') ``` #### Negative slice indices allowed. The `start` and `end` parameters of many methods may now be negative, with the same meaning as for negative slice indices. Affects all methods with these parameters. #### Sequence ABCs used. The `Bits` class now derives from `collections.Sequence`, while the `BitString` class derives from `collections.MutableSequence`. #### Keywords allowed in readlist, peeklist and unpack. Keywords for token lengths are now permitted when reading. So for example, you can write ```pycon >>> s = bitstring.pack('4*(uint:n)', 2, 3, 4, 5, n=7) >>> s.unpack('4*(uint:n)', n=7) [2, 3, 4, 5] ``` #### start and end parameters added to rol and ror. #### join function accepts other iterables. Also its parameter has changed from `bitstringlist` to `sequence`. This is technically a backward incompatibility in the unlikely event that you are referring to the parameter by name. #### `__init__` method accepts keywords. Rather than a long list of initialisers the `__init__` methods now use a `**kwargs` dictionary for all initialisers except `auto`. This should have no effect, except that this is a small backward incompatibility if you use positional arguments when initialising with anything other than auto (which would be rather unusual). #### More optimisations. #### Bug fixed in replace method (it could fail if start != 0). ### January 19th 2010: version 1.2.0 for Python 2.6 and 3.x released #### New `Bits` class. Introducing a brand new class, `Bits`, representing an immutable sequence of bits. The `Bits` class is the base class for the mutable `BitString`. The differences between `Bits` and `BitStrings` are: 1) `Bits` are immutable, so once they have been created their value cannot change. This of course means that mutating methods (`append`, `replace`, `del` etc.) are not available for `Bits`. 2) `Bits` are hashable, so they can be used in sets and as keys in dictionaries. 3) `Bits` are potentially more efficient than `BitStrings`, both in terms of computation and memory. The current implementation is only marginally more efficient though - this should improve in future versions. You can switch from `Bits` to a `BitString` or vice versa by constructing a new object from the old. ```pycon >>> s = Bits('0xabcd') >>> t = BitString(s) >>> t.append('0xe') >>> u = Bits(t) ``` The relationship between `Bits` and `BitString` is supposed to loosely mirror that between `bytes` and `bytearray` in Python 3. #### Deprecation messages turned on. A number of methods have been flagged for removal in version 2. Deprecation warnings will now be given, which include an alternative way to do the same thing. All of the deprecated methods have simpler equivalent alternatives. ```pycon >>> t = s.slice(0, 2) __main__:1: DeprecationWarning: Call to deprecated function slice. Instead of 's.slice(a, b, c)' use 's[a:b:c]'. ``` The deprecated methods are: `advancebit`, `advancebits`, `advancebyte`, `advancebytes`, `retreatbit`, `retreatbits`, `retreatbyte`, `retreatbytes`, `tell`, `seek`, `slice`, `delete`, `tellbyte`, `seekbyte`, `truncatestart` and `truncateend`. #### Initialise from bool. Booleans have been added to the list of types that can 'auto' initialise a bitstring. ```pycon >>> zerobit = BitString(False) >>> onebit = BitString(True) ``` #### Improved efficiency. More methods have been sped up, in particular some deletions and insertions. #### Bug fixes. A rare problem with truncating the start of bitstrings was fixed. A possible problem outputting the final byte in `tofile()` was fixed. ### December 22nd 2009: version 1.1.3 for Python 2.6 and 3.x released This version hopefully fixes an installation problem for platforms with case-sensitive file systems. There are no new features or other bug fixes. ### December 18th 2009: version 1.1.2 for Python 2.6 and 3.x released This is a minor update with (almost) no new features. * Improved efficiency. The speed of many typical operations has been increased, some substantially. * Initialise from integer. A BitString of '0' bits can be created using just an integer to give the length in bits. So instead of ```pycon >>> s = BitString(length=100) ``` you can write just ```pycon >>> s = BitString(100) ``` This matches the behaviour of bytearrays and (in Python 3) bytes. * A defect related to using the `set` / `unset` functions on BitStrings initialised from a file has been fixed. ### November 24th 2009: version 1.1.0 for Python 2.6 and 3.x released Note that this version will not work for Python 2.4 or 2.5. There may be an update for these Python versions some time next year, but it's not a priority quite yet. Also note that only one version is now provided, which works for Python 2.6 and 3.x (done with the minimum of hackery!) #### Improved efficiency. A fair number of functions have improved efficiency, some quite dramatically. #### New bit setting and checking functions. Although these functions don't do anything that couldn't be done before, they do make some common use cases much more efficient. If you need to set or check single bits then these are the functions you need. ``` set / unset : Set bit(s) to 1 or 0 respectively. allset / allunset : Check if all bits are 1 or all 0. anyset / anyunset : Check if any bits are 1 or any 0. ``` For example ```pycon >>> s = BitString(length=1000) >>> s.set((10, 100, 44, 12, 1)) >>> s.allunset((2, 22, 222)) True >>> s.anyset(range(7, 77)) True ``` #### New rotate functions. `ror` / `rol` : Rotate bits to the right or left respectively. ```pycon >>> s = BitString('0b100000000') >>> s.ror(2) >>> s.bin '0b001000000' >>> s.rol(5) >>> s.bin '0b000000100' ``` #### Floating point interpretations. New float initialisations and interpretations are available. These only work for BitStrings of length 32 or 64 bits. ```pycon >>> s = BitString(float=0.2, length=64) >>> s.float 0.200000000000000001 >>> t = bitstring.pack('<3f', -0.4, 1e34, 17.0) >>> t.hex '0xcdccccbedf84f67700008841' ``` #### 'bytes' token reintroduced. This token returns a bytes object (equivalent to a str in Python 2.6). ```pycon >>> s = BitString('0x010203') >>> s.unpack('bytes:2, bytes:1') ['\x01\x02', '\x03'] ``` #### 'uint' is now the default token type. So for example these are equivalent: ```pycon a, b = s.readlist('uint:12, uint:12') a, b = s.readlist('12, 12') ``` ### October 10th 2009: version 1.0.1 for Python 3.x released This is a straight port of version 1.0.0 to Python 3. For changes since the last Python 3 release read all the way down in this document to version 0.4.3. This version will also work for Python 2.6, but there's no advantage to using it over the 1.0.0 release. It won't work for anything before 2.6. ## October 9th 2009: version 1.0.0 for Python 2.x released Version 1 is here! This is the first release not to carry the 'beta' tag. It contains a couple of minor new features but is principally a release to fix the API. If you've been using an older version then you almost certainly will have to recode a bit. If you're not ready to do that then you may wish to delay updating. So the bad news is that there are lots of small changes to the API. The good news is that all the changes are pretty trivial, the new API is cleaner and more 'Pythonic', and that by making it version 1.0 I'm promising not to tweak it again for some time. ### API Changes #### New `read` / `peek` functions for returning multiple items. The functions `read`, `readbits`, `readbytes`, `peek`, `peekbits` and `peekbytes` now only ever return a single item, never a list. The new functions `readlist`, `readbitlist`, `readbytelist`, `peeklist`, `peekbitlist` and `peekbytelist` can be used to read multiple items and will always return a list. So a line like: ```pycon >>> a, b = s.read('uint:12, hex:32') ``` becomes ```pycon >>> a, b = s.readlist('uint:12, hex:32') ``` #### Renaming / removing functions. Functions have been renamed as follows: ``` seekbit -> seek tellbit -> tell reversebits -> reverse deletebits -> delete tostring -> tobytes ``` and a couple have been removed altogether: ``` deletebytes - use delete instead. empty - use 'not s' rather than 's.empty()'. ``` #### Renaming parameters. The parameters `startbit` and `endbit` have been renamed `start` and `end`. This affects the functions `slice`, `find`, `findall`, `rfind`, `reverse`, `cut` and `split`. The parameter `bitpos` has been renamed to `pos`. The affects the functions `seek`, `tell`, `insert`, `overwrite` and `delete`. #### Mutating methods return `None` rather than `self`. This means that you can't chain functions together so ```pycon >>> s.append('0x00').prepend('0xff') >>> t = s.reverse() ``` Needs to be rewritten ```pycon >>> s.append('0x00') >>> s.prepend('0xff') >>> s.reverse() >>> t = s ``` Affects `truncatestart`, `truncateend`, `insert`, `overwrite`, `delete`, `append`, `prepend`, `reverse` and `reversebytes`. #### Properties renamed. The `data` property has been renamed to `bytes`. Also, if the `BitString` is not a whole number of bytes then a `ValueError` exception will be raised when using `bytes` as a 'getter'. Properties `len` and `pos` have been added to replace `length` and `bitpos`, although the longer names have not been removed, so you can continue to use them if you prefer. #### Other changes. The `unpack` function now always returns a list, never a single item. BitStrings are now 'unhashable', so calling hash on one or making a set will fail. The colon separating the token name from its length is now mandatory. So for example `BitString('uint12=100')` becomes `BitString('uint:12=100')`. Removed support for the `'bytes'` token in format strings. Instead of `s.read('bytes:4')` use `s.read('bits:32')`. ### New features #### Added endswith and startswith functions These do much as you'd expect; they return `True` or `False` depending on whether the BitString starts or ends with the parameter. ```pycon >>> BitString('0xef342').startswith('0b11101') True ``` ### September 11th 2009: version 0.5.2 for Python 2.x released Finally some tools for dealing with endianness! * New interpretations are now available for whole-byte BitStrings that treat them as big, little, or native-endian. ```pycon >>> big = BitString(intbe=1, length=16) # or BitString('intbe:16=1') if you prefer. >>> little = BitString(intle=1, length=16) >>> print big.hex, little.hex 0x0001 0x0100 >>> print big.intbe, little.intle 1 1 ``` * 'Struct'-like compact format codes To save some typing when using pack, unpack, read and peek, compact format codes based on those used in the struct and array modules have been added. These must start with a character indicating the endianness (>, < or @ for big, little and native-endian), followed by characters giving the format: ``` b 1-byte signed int B 1-byte unsigned int h 2-byte signed int H 2-byte unsigned int l 4-byte signed int L 4-byte unsigned int q 8-byte signed int Q 8-byte unsigned int ``` For example: ```pycon >>> s = bitstring.pack('<4h', 0, 1, 2, 3) ``` creates a BitString with four little-endian 2-byte integers. While ```pycon >>> x, y, z = s.read('>hhl') ``` reads them back as two big-endian two-byte integers and one four-byte big endian integer. Of course you can combine this new format with the old ones however you like: ```pycon >>> s.unpack('>> from bitstring import BitString, pack >>> a = pack('0b11, 0xff, 0o77, int:5=-1, se=33') ``` You can also leave placeholders in the format, which will be filled in by the values provided. ```pycon >>> b = pack('uint:10, hex:4', 33, 'f') ``` Finally, you can use a dictionary or keywords. ```pycon >>> c = pack('bin=a, hex=b, bin=a', a='010', b='ef') ``` The unpack function is similar to the read function except that it always unpacks from the start of the BitString. ```pycon >>> x, y = b.unpack('uint:10, hex') ``` If a token is given without a length (as above) then it will expand to fill the remaining bits in the BitString. This also now works with read() and peek(). * New `tostring()` and `tofile()` functions. The `tostring()` function just returns the data as a string, with up to seven zero bits appended to byte align. The `tofile()` function does the same except writes to a file object. ```pycon >>> f = open('myfile', 'wb') >>> BitString('0x1234ff').tofile(f) ``` * Other changes. The use of `'='` is now mandatory in 'auto' initialisers. Tokens like `'uint12 100'` will no longer work. Also, the use of a `':'` before the length is encouraged, but not yet mandated. So the previous example should be written as `'uint:12=100'`. The 'auto' initialiser will now take a file object. ```pycon >>> f = open('myfile', 'rb') >>> s = BitString(f) ``` ### July 19th 2009: version 0.5.0 for Python 2.x released This update breaks backward compatibility in a couple of areas. The only one you probably need to be concerned about is the change to the default for `bytealigned` in `find`, `replace`, `split`, etc. See the user manual for more details on each of these items. * Expanded abilities of 'auto' initialiser. More types can be initialised through the 'auto' initialiser. For example instead of ```pycon >>> a = BitString(uint=44, length=16) ``` you can write ```pycon >>> a = BitString('uint16=44') ``` Also, different comma-separated tokens will be joined together, e.g. ```pycon >>> b = BitString('0xff') + 'int8=-5' ``` can be written ```pycon >>> b = BitString('0xff, int8=-5') ``` * New formatted `read()` and `peek()` functions. These takes a format string similar to that used in the auto initialiser. If only one token is provided then a single value is returned, otherwise a list of values is returned. ```pycon >>> start_code, width, height = s.read('hex32, uint12, uint12') ``` is equivalent to ```pycon >>> start_code = s.readbits(32).hex >>> width = s.readbits(12).uint >>> height = s.readbits(12).uint ``` The tokens are: ``` int n : n bits as an unsigned integer. uint n : n bits as a signed integer. hex n : n bits as a hexadecimal string. oct n : n bits as an octal string. bin n : n bits as a binary string. ue : next bits as an unsigned exp-Golomb. se : next bits as a signed exp-Golomb. bits n : n bits as a new BitString. bytes n : n bytes as a new BitString. ``` See the user manual for more details. * `hex()` and `oct()` functions removed. The special functions for `hex()` and `oct()` have been removed. Please use the hex and oct properties instead. ```pycon >>> hex(s) ``` becomes ```pycon >>> s.hex ``` * join made a member function. The join function must now be called on a `BitString` object, which will be used to join the list together. You may need to recode slightly: ```pycon >>> s = bitstring.join('0x34', '0b1001', '0b1') ``` becomes ```pycon >>> s = BitString().join('0x34', '0b1001', '0b1') ``` * More than one value allowed in `readbits`, `readbytes`, `peekbits` and `peekbytes` If you specify more than one bit or byte length then a list of BitStrings will be returned. ```pycon >>> a, b, c = s.readbits(10, 5, 5) ``` is equivalent to ```pycon >>> a = readbits(10) >>> b = readbits(5) >>> c = readbits(5) ``` * `bytealigned` defaults to False, and is at the end of the parameter list Functions that have a `bytealigned` parameter have changed so that it now defaults to `False` rather than `True`. Also, its position in the parameter list has changed to be at the end. You may need to recode slightly (sorry!) * `readue` and `readse` functions have been removed Instead you should use the new read function with a `'ue'` or `'se'` token: ```pycon >>> i = s.readue() ``` becomes ```pycon >>> i = s.read('ue') ``` This is more flexible as you can read multiple items in one go, plus you can now also use the `peek` function with ue and se. * Minor bugs fixed. See the issue tracker for more details. ### June 15th 2009: version 0.4.3 for Python 2.x released This is a minor update. This release is the first to bundle the bitstring manual. This is a PDF and you can find it in the docs directory. Changes in version 0.4.3 * New 'cut' function This function returns a generator for constant sized chunks of a BitString. ```pycon >>> for byte in s.cut(8): ... do_something_with(byte) ``` You can also specify a `startbit` and `endbit`, as well as a `count`, which limits the number of items generated: ```pycon >>> first100TSPackets = list(s.cut(188*8, count=100)) ``` * `slice` function now equivalent to `__getitem__`. This means that a step can also be given to the `slice` function so that the following are now the same thing, and it's just a personal preference which to use: ```pycon >>> s1 = s[a:b:c] >>> s2 = s.slice(a, b, c) ``` * findall gets a 'count' parameter. So now ```pycon >>> list(a.findall(s, count=n)) ``` is equivalent to ```pycon >>> list(a.findall(s))[:n] ``` except that it won't need to generate the whole list and so is much more efficient. * Changes to 'split'. The `split` function now has a 'count' parameter rather than 'maxsplit'. This makes the interface closer to that for `cut`, `replace` and `findall`. The final item generated is now no longer the whole of the rest of the `BitString`. * A couple of minor bugs were fixed. See the issue tracker for details. ### May 25th 2009: version 0.4.2 for Python 2.x released This is a minor update, and almost doesn't break compatibility with version 0.4.0, but with the slight exception of `findall()` returning a generator, detailed below. Changes in version 0.4.2 * Stepping in slices The use of the step parameter (also known as the stride) in slices has been added. Its use is a little non-standard as it effectively gives a multiplicative factor to apply to the start and stop parameters, rather than skipping over bits. For example this makes it much more convenient if you want to give slices in terms of bytes instead of bits. Instead of writing `s[a*8:b*8]` you can use `s[a:b:8]`. When using a step the BitString is effectively truncated to a multiple of the step, so `s[::8]` is equal to `s` if `s` is an integer number of bytes, otherwise it is truncated by up to 7 bits. So the final seven complete 16-bit words could be written as `s[-7::16]` Negative slices are also allowed, and should do what you'd expect. So for example `s[::-1]` returns a bit-reversed copy of `s` (which is similar to `s.reversebits()`, which does the same operation on `s` in-place). As another example, to get the first 10 bytes in reverse byte order you could use `s_bytereversed = s[0:10:-8]`. * Removed restrictions on offset You can now specify an offset of greater than 7 bits when creating a `BitString`, and the use of offset is also now permitted when using the `filename` initialiser. This is useful when you want to create a `BitString` from the middle of a file without having to read the file into memory. ```pycon >>> f = BitString(filename='reallybigfile', offset=8000000, length=32) ``` * Integers can be assigned to slices You can now assign an integer to a slice of a `BitString`. If the integer doesn't fit in the size of slice given then a `ValueError` exception is raised. So this is now allowed and works as expected: ```pycon >>> s[8:16] = 106 ``` and is equivalent to ```pycon >>> s[8:16] = BitString(uint=106, length=8) ``` * Fewer exceptions raised Some changes have been made to slicing so that fewer exceptions are raised, bringing the interface closer to that for lists. So for example trying to delete past the end of the `BitString` will now just delete to the end, rather than raising a `ValueError`. * Initialisation from lists and tuples A new option for the auto initialiser is to pass it a list or tuple. The items in the list or tuple are evaluated as booleans and the bits in the `BitString` are set to `1` for `True` items and `0` for `False` items. This can be used anywhere the auto initialiser can currently be used. For example: ```pycon >>> a = BitString([True, 7, False, 0, ()]) # 0b11000 >>> b = a + ['Yes', ''] # Adds '0b10' >>> (True, True, False) in a True ``` #### Miscellany * `reversebits()` now has optional `startbit` and `endbit` parameters. * As an optimisation `findall()` will return a generator, rather than a list. If you still want the whole list then of course you can just call `list()` on the generator. * Improved efficiency of rfind(). * A couple of minor bugs were fixed. See the issue tracker for details. ### April 23rd 2009: Python 3 only version 0.4.1 released This version is just a port of version 0.4.0 to Python 3. All the unit tests pass, but beyond that only limited ad hoc testing has been done and so it should be considered an experimental release. That said, the unit test coverage is very good - I'm just not sure if anyone even wants a Python 3 version! ### April 11th 2009: version 0.4.0 released Changes in version 0.4.0 * New functions Added `rfind()`, `findall()`, `replace()`. These do pretty much what you'd expect - see the docstrings or the wiki for more information. * More special functions Some missing functions were added: `__repr__`, `__contains__`, `__rand__`, `__ror__`, `__rxor__` and `__delitem__`. * Miscellany A couple of small bugs were fixed (see the issue tracker). There are some small backward incompatibilities relative to version 0.3.2: * Combined `find` and `findbytealigned` `findbytealigned` has been removed, and becomes part of `find`. The default start position has changed on both `find` and `split` to be the start of the `BitString`. You may need to recode: ```pycon >>> s1.find(bs) >>> s2.findbytealigned(bs) >>> s2.split(bs) ``` becomes ```pycon >>> s1.find(bs, bytealigned=False, startbit=s1.bitpos) >>> s2.find(bs, startbit=s1.bitpos) # bytealigned defaults to True >>> s2.split(bs, startbit=s2.bitpos) ``` * Reading off end of BitString no longer raises exception. Previously a `read` or `peek` function that encountered the end of the `BitString` would raise a `ValueError`. It will now instead return the remainder of the `BitString`, which could be an empty `BitString`. This is closer to the file object interface. * Removed visibility of offset. The `offset` property was previously read-only, and has now been removed from public view altogether. As it is used internally for efficiency reasons you shouldn't really have needed to use it. If you do then use the `_offset` parameter instead (with caution). ### March 11th 2009: version 0.3.2 released Changes in version 0.3.2 * Better performance A number of functions (especially `find` and `findbytealigned`) have been sped up considerably. * Bit-wise operations Added support for bit-wise AND (`&`), OR (`|`) and XOR (`^`). For example: ```pycon >>> a = BitString('0b00111') >>> print a & '0b10101' 0b00101 ``` * Miscellany Added `seekbit` and `seekbyte` functions. These complement the `advance` and `retreat` functions, although you can still just use `bitpos` and `bytepos` properties directly. ```pycon >>> a.seekbit(100) # Equivalent to a.bitpos = 100 ``` Allowed comparisons between `BitString` objects and strings. For example this will now work: ```pycon >>> a = BitString('0b00001111') >>> a == '0x0f' True ``` ### February 26th 2009: version 0.3.1 released Changes in version 0.3.1 This version only adds features and fixes bugs relative to 0.3.0, and doesn't break backwards compatibility. * Octal interpretation and initialisation The oct property now joins bin and hex. Just prefix octal numbers with '0o'. ```pycon >>> a = BitString('0o755') >>> print a.bin 0b111101101 ``` * Simpler copying Rather than using `b = copy.copy(a)` to create a copy of a `BitString`, now you can just use `b = BitString(a)`. * More special methods Lots of new special methods added, for example bit-shifting via `<<` and `>>`, equality testing via `==` and `!=`, bit inversion (`~`) and concatenation using `*`. Also `__setitem__` is now supported so `BitString` objects can be modified using standard index notation. * Proper installer Finally got round to writing the distutils script. To install just `python setup.py install`. ### February 15th 2009: version 0.3.0 released Changes in version 0.3.0 * Simpler initialisation from binary and hexadecimal The first argument in the BitString constructor is now called auto and will attempt to interpret the type of a string. Prefix binary numbers with `'0b'` and hexadecimals with `'0x'`. ```pycon >>> a = BitString('0b0') # single zero bit >>> b = BitString('0xffff') # two bytes ``` Previously the first argument was data, so if you relied on this then you will need to recode: ```pycon >>> a = BitString('\x00\x00\x01\xb3') # Don't do this any more! ``` becomes ```pycon >>> a = BitString(data='\x00\x00\x01\xb3') ``` or just ```pycon >>> a = BitString('0x000001b3') ``` This new notation can also be used in functions that take a `BitString` as an argument. For example: ```pycon >>> a = BitString('0x0011') + '0xff' >>> a.insert('0b001', 6) >>> a.find('0b1111') ``` * BitString made more mutable The functions `append`, `deletebits`, `insert`, `overwrite`, `truncatestart` and `truncateend` now modify the `BitString` that they act upon. This allows for cleaner and more efficient code, but you may need to rewrite slightly if you depended upon the old behaviour: ```pycon >>> a = BitString(hex='0xffff') >>> a = a.append(BitString(hex='0x00')) >>> b = a.deletebits(10, 10) ``` becomes: ```pycon >>> a = BitString('0xffff') >>> a.append('0x00') >>> b = copy.copy(a) >>> b.deletebits(10, 10) ``` Thanks to Frank Aune for suggestions in this and other areas. * Changes to printing The binary interpretation of a BitString is now prepended with `'0b'`. This is in keeping with the Python 2.6 (and 3.0) bin function. The prefix is optional when initialising using `bin=`. Also, if you just print a `BitString` with no interpretation it will pick something appropriate - hex if it is an integer number of bytes, otherwise binary. If the BitString representation is very long it will be truncated by '...' so it is only an approximate interpretation. ```pycon >>> a = BitString('0b0011111') >>> print a 0b0011111 >>> a += '0b0' >>> print a 0x3e ``` * More convenience functions Some missing functions such as `advancebit` and `deletebytes` have been added. Also a number of peek functions make an appearance as have `prepend` and `reversebits`. See the Tutorial for more details. ### January 13th 2009: version 0.2.0 released Some fairly minor updates, not really deserving of a whole version point update. ## December 29th 2008: version 0.1.0 released First release! :tada: bitstring-bitstring-4.2.3/tests/000077500000000000000000000000001462444752600167105ustar00rootroot00000000000000bitstring-bitstring-4.2.3/tests/__init__.py000066400000000000000000000000001462444752600210070ustar00rootroot00000000000000bitstring-bitstring-4.2.3/tests/requirements.txt000066400000000000000000000001471462444752600221760ustar00rootroot00000000000000pytest >= 7.4.2, < 8 hypothesis >= 6.98.13, < 7 gfloat >= 0.2.1, < 0.3 pytest-benchmark >= 4.0.0, < 4.1bitstring-bitstring-4.2.3/tests/smalltestfile000066400000000000000000000000101462444752600214720ustar00rootroot00000000000000#Egbitstring-bitstring-4.2.3/tests/test.m1v000066400000000000000000003645641462444752600203360ustar00rootroot00000000000000  MPEG-1 Test Sequence, 25 frames/sec @ R".RJR")H".RJR")H".RH"&J֓B'bER)JD\)rER)JD\)rD+})H".RJR")H"*9JR")H".RJR")H".R#P?")HZ"4JR")H".RJR"})H".R)H".RJR")H".RJR")HퟝPt!,`r@ ,v!,7!,m ZH~x[<poF延= VI珼 RVJv6@B!JŠ%" 1wQ(w)J)H%  U K3 1L ,\m)z H] A khBcPBprxؓs)[JǾ (i4 { Gr !H``o ,q480P i`0,f7'QsHx 8 `}3@3I`T5`Mbssn%|$V{\co, ,40(J6.@#w-8@/BƣiD G#R_m@"C6mbn! <5$vOR.RORt".RJR")H })H".RJR")H".RJV5bPGK>~B9!XhiDt`vA)@0g-߾GL: P(b2#_K @2XeywT1vT%t:z @70) 9?s% @ 1_H@aܚ`T ߧ4 0)()@b [bM`*YO23 eGaaۮbhb{0 v@V^l7-` Y=|C @>XK))+"h0!ВGh_z3"hHCwI홗HBfC,5oaRo 4<5+ZRQט1+d;_1 p@+JQ~q hP@3&ǥ/ (`IOH/%c50 'L2&`BlK}}?O?6uYqpSorER)J%I bFsフ)H })H".RJR")H".R=T~h LԠj߶VSp!c`Yhd%J|`t(' &b;gٛ܉}D2 ?nI5?`gc߫>a3!n_?H`'&!j7`CJ1)=@0xL͐6^4&7}r-* =J9@ 4hc ?-WH$`CԘj;+c.1 8)8rLJLnFၜ8@^ e7ePfQ0(`9=N곆<К+ ^ -@11oo\}\. /P<v`~K)(<`@paPo)(PaG[V@idfm " `0ҀtPo37a^v5D2](3w *vn? 0F[P1;{`"dA0!$ + 71 @dᇣTJ_:>s@ @@,{41! t%﹟ZuYBsw6m.R>rE$S];+@) "%nb:A]gb })H".RJR")H".P BXh c1;$wfP t ;OX1??g1v`k`la 0@/(nrD A2v;Ct 1 ?@B\8![!OX@ !;ck$!??@@yp'd?T', %(Os: tB& ۥ[}P g :rcV`|  &$4b Go6+d#dl/l '!M/"W)BJRV\] @@ ,d2VF7 @0Jqx% cD 0,*K}}-< !@A(,j5 G>;9g[,%tUHI>P*P*vѿ״ 8/$]@&:S 7N^`«@0πhCC>oP INd*0 P GܵB@; @i]Н0@i bO} G$8⠀aDԠ,u􀁎] T1 Y JRjdoz#3EJR")HH5))OӴtRNNo })H".RJR")H"/x y[$1_\a1 2ͻlEO7)JZE˨;pс 䏷ۓ簀'b`C9 #R@0C%  @jR2? BwO3va]>0 I` @bzBS=Tw{ j% ~o?&o,0B6hFF !?x@R ?qL. !@\ 8J?⠀0(XK )%tax=WIeghhh @F (&BI0bQ^<{ :&&Kߑx b!'1# 8bO# `8BLHu010' @,` a93(Kppqh P3NSy3   RYAn"0q!.=Ѽ/R)OER)JDYO7s'>rIݓlI10O3|ȅ)c })H".RJR")H"/@ ;J NlssG}#  vPJߟ @@xjJ @51a1aq~/>sy< %x`^]0 HIfNm5@p w)h`@LP8M)KOvnBCC% ҷ+e% ;X Y(8Ha3:e@R ` X 7}( e@*W}dΎƧjoPH*hMn+/bHe?mշ( @?;107%+;Bﲂ f75#8 [ftGIy\ހ) R_ `Qe ''ٓ rA`!!#ID He `wA 78A`L_fGh-9 E` vC!' B;6?sE aÂ`PEbghp!,_?Zz{߶P @Y $1x ab3!yohh 0 Av@ThIG' %Z.+v8% Q^E(|%Bx߇5pX QGn @NlR0ж=x( f` \ pyE\|4 ,V͉Uր&G(nU}XnزWӟr:qqҘF:1)JD\)sԤ66ڔD })H".RJR"(x".]@(i [ypC!C'};4@*Q 3wQ Q0)GJJxdM!| " tp3$kl/{@v;lC |N8u@^FB~ֳٜ @pi_׌p@/rёЬ_@{ @00!HY4"w @:nBJ B7s7=d@  ^m F< !Fnd>@AD2ٔA) p @@ez>2poR{)nOu @p]k =ahuLBu, /5 !h]_p`i  ^sc_t@ Z e,@#>hY 0 /1Ʊڠ_k42LjR20wP2A0` 1(471*WnƏf I ;-uF_H CS8o PXBHe(%S:Z PM) P^ONQ8{`ғߑj!!bY| h<x (B25P2Ӟ0 Mqe@fc+EA\@ӌ, pC1, nK@NQ0J] C/lLB:!ebrҗ>ޔJR")H".RJR" })H".RFzR"j6".6)rE 4@Tyr@B @tS7sx)b(bCbJNo04!bRϿC_G z ThoUפn\@2-)'0 \(M,:ܴη3 ( 140BSB,׀ L{&'2?RMUB~̒HX4Z?ydhB>I`1= ^$=a t :s^`d2o%{ M(1l*,Q.iN<*CHp` @;v 6pᡥI}y؍0oe`;_PbM^|8Hd$cx} vhB@tev^A0 VBl˸1L[,2ɻk 6LCfC'%t6#x 2&\(LFB0(QEdd#!?b U/fCfs֔-rR>ͩrER)JD\) })H".RJR")HjD\)r&0rF@J`T['8@$ `!>7K/  MA4; ^% R%˼P@B,Csc$0hX#!};LD0Vop$͊wJ7pzvǽ?k : :b҇=@@xp,Ηf9Mf@M!,@'&#PNjm58j v7M~ CU%ttLc yJ]i+n`e## Hإ 0(@iPwCL-)l)CZT72@v姛z@pH@& OBِR ̄da5)hXiaJS)IR)JD\)rER)JD\) })H".RJ5".RN):v/jrp %`!v Q,BQNHqpҁ$3 R_ g=@P[ qA4vuqbO@A @NZRp : d'` _A@v 41+8 0Y C!3m`k@:P(L!bU۔~LJ+ @ 0JQKb! fBy ydBtb`L?V7NɦBu'_o iS{?HER)jt,7i9i/#X"C!J~ڛ\)̐ݼJg })H".RJR")H".RFԤEa  G?| 2if k=$$%(k 2` HC9:f&O닃56ڔJR")H".Rԥ".td mHxsmH a?DxmEȖoJ+.GE͛R })H}D;By::vOy1`'K%>i B5JfrpĠ@jkJgPnNJo".RJR")H".RJR"6s”JR")Hh__mQN\}, ^FadER)JD@ })Hbi]:(Q,3}"yB&vBuːPo] U>uy|b:B+*!4|V\W"ɩ!%V$ 1 Jr):^:H--nP,3b KrA:W xۀ?F=:@#-H O^TxLKB$ Pi>C= xLG;U fL4$pjR)Έ7 chFk ;R:v(bh+/"r=6i@!I}Zs,2¬RmF}.w>_d@B+{c ~XԽR3j! I!.RJR" })HJsSJ?|!h\)roJD\3Rj ԤE5)pe6jR")HԤE-6l?BH,H9tR~(#.jR'JR"~ DYhm))B^ER })H".RJRCk#F}FztoqDm>Am RrC"ܥ))H)JD?JR!RRrC%ʮuGi0:e)̄h K9:o F tb0t)`&Q$)JSRx҇QRt ~JVM(0GU.2Q4ӼDZI1 !gJuv011~RR&: (}-@PeQRMa3/oΖ=zֱC֜?;4bd*v?Ҹ4Q3:S@#(+DPc kdް~+8~߽";M&b_L95~"@5]!ۨC Q~"z<}FDAĈ@T]!~Co0MVpAMcWv_dCIPMoBoG˷}ϤP-0 a@:cI/2`Rx(wbYVP} {QO뾂C KwTM k&y~ }Pt^xr{CDQ܂ w!"iԖ-C1(;)E>Į:@~s%0H5[ܨK^z G@OKC_A!Gx%'A/H4ɯhJV׆ͨHHtFFJ%z$ @2j6H8 pBx/ʾ(pMGx20d+DPOEqNT">BC  !F^ ?^ C ʻ(-6EmTPw :"5p PË/|7%>")>L뷦Lˈ_M& ̢(KE`tk?= ܤ"`U_qHg֡@2{X_x!|bA?Z:e9=o1 Q8@`, 1=qI*8rxKwF >(}xkC{#̷"!3rYE+ @;!Ҁ S>"^@)\cOPOgPhe@2OIiPQ1g/}k`8(2&hB^A<@\~E<0b5ڰb!=78R< +>GDj fMޑ>)3_`KcBAN\hJHV>7\q-N"v P_@cjC_}qwtSR. rШ *L@BXf_@  @Q|uܤ]FS]U;x6if (.2~"L)ka x|0z ietCČ1Rh) LgfM3v8uࠛڌ6@ Y1^L(0Py7]ޢ %#^,ȱQ"@cv^ؑ- ',}gR@Ok Ϲ" >)ӑtѯ@=h G,mH珹b(ϾM 4P 7:q4OTEL&\` Ո?DBA `L Ÿ" 5tdB-kG0-.)-$~?ƥ)9RIMN" 8!wɻ A` A?.rw: DPIHo@4-{W!'A;#&ǔm3Qo\K.ɼ12z03E!oڏd뷀vDQ\f Hh,v[6jͩ8>E)A)G)JDW4?X9:@5'I ?%9}l L%bEX5u8 Bߣ#^Z ס $DĞRE)G)JDQRrE"g qV?Ҕ"()H9JR"R"()H@Zzy"ԒJ@p qp,"()H9JR"R"()HA`, q:B 4aJ2>E)H wEH]p rmU%A`]!nޏ",X`7[`ޛSJ-롵l\-5@;APn7 K 0 N p03jq4Ir롹c=ڼ5-*uXSκp z& @*\AJA CZ-d7nKǛ3 y [jC^kjCz`1 YM @M Hh f@lX`o fh4Ŏ@EAOWʮ#HUpl@ l ri Dg {n`l/LoKO 6 k'n-Piil6Ki ruui. o!5VZr\ @*PgyX:W'܂5 IH 4 ))I`:W[MRpM LFfT")7# "c})H9JR"M0ie 8d 22WZ = p p(')G)JDQRrEAArmp)2WUWW\0 vRI ,! o@&uҩ\Bc})H9JR"R"()H9JR" P Wm @H0b#:6Z+3Kp WMԬ'Tz"p\(&@Wd(! "*Z"", Pm @R C $&A!4Z=IzJȎF@ W&hBii˖  \d(Sa!# `}'@TpuSbH&`,K>oS;};a`?>Š&k:P@)0ஆ`M161 W'o%K(А)p[\ .NCJ00b[(#͂? @ 9``PgR>_FYyo㎀vCA 8iy(iMc6N,Piddc#la1AAB942cPi@7H & P \00Zeq! i4JS…%@!ǖ@ & ٛ4xB @H҃ _ 4`1ID!󳩜}MB!`l kg@7!hgqMӈiK".5A('[&#%ɻd ]d$Ͼt I-3;: H0qN%Ѡf(4j%mȐ lC , MHae)S@ wBv@iΎzA @tHɘlڐtX WI NAH2HC ,^B; I\Ҝ^P,I@jJ3$J@]1mEXihAE'I/ (a`XyEYE ,p];0T3ei%$@`aE>:;=D{KgYh;?>A4 tpчlƘ&(q\ bd0I&,ܴ8w dcd33.Ai-t$|pjUvPha4Z{ԓ ,`ƥn&$‹: ::P4`Qg0WC qu&$ Bi7: dvWhb &C rڀlQ7T@4C h&?Pd?<èL76ہ _Y5(bp00OC3 m6Ih!PO)(=X5 c5w|3o@|>&,`ܶ0O`@dL?XjP?@'0 + @0:IL&$aNGjcըJR"$ ϤI 2 H$  W; (jḆn1940ۍ!bpщ%8@`K  VLi:d*ZL/uɀ1 !'cݙMAԝfc$ tpa t?H2ί~sRzJ@bNŧ`ʯ@H80e8{&%pX %>=k(Y5 !X҆) !7 ⒐ەS={y?|7lJq/*,1}?}o@F.؄J, 86Fva@`a5  Xj ǜ8$hI d$gV_o) d0 ( Wl](LA0 d $:P2 'X41*ku% l"h䏐4;8 PyҧG5 P :4ur * ugu⟹ٱN0PyjWJR"R(.M@pH` Wuhb /uty|ɟ$ 4 h0o+8iX3s|a:@0IJ#@-JzCC_9EwY(8jNӎ_< ԓJOA4?4оk| L 0ZsՃi4!k)`w̾8`€b0l%*@&n9dt,c#3x[J.,tܑ|ʹ+[ZKO(ahBw1y_|p2\bp,MN!C> q?HoFJM_rq8!)g&+NBI_ܘlyܰӊB:.S \CJJ a&|iJ @hkk.`hhYn#95N;- @4 b\CRh)A w_$C L 1m 0Ӷ~#g.z@LH{:<#9"()H9JR"R(,kI e;R Ap^@t @ W';a#ӄ=+UpD~<6~P5@9FPwQdd DѩJ9)zR&O)N)Ok~ZF!=Dxg%b5k>lJ:$[ bWsL~efJ?zAØ3&4j9e|p !4W |YFlv02ၥb7I%~WHv ~Jtrվ)n\vX|Dґ9Q>E)G)JDQRS@~HLA!ÙN}H  >:|:rOoc<ߚep8!}ֽs١J\4Ї}(HT(rWۖO҂^$sGOc3rR[|زⶤ8AP#T$?$:_8蜂@l 8EiR >9X|Ԗ!qW !;,DՃs(U jl?3x!$R8r94ƀH.~V HsJd#Q'VOBIK-qQ[c FN=;^ԁbRNdQ#0͊r7Ư7$21HFc2  7f5U1 xӜ hdq53޾ZvvOC!9id10oasB OUAZ֨tA'6iYl3sHc  J@p W'Dg"*nnD50zFcjG #, W'dE B H@6" j@l ,QR1e8љ(ӷ/ +Ps vd;còe87%99? H @5 ^WcBBtR)8=|!C€bY0 ؔXs- Oob >ridҀnL8)<a\a`0$y>C@i5%tODž ^!Tyh$ހa`;BJ~ݙLw*  (0@vL@mo;:'& !j6OT53s i4 ` 3 &i4xs|J nԘ&|&퓜'uhGw\7>:& IdR$`nN (%(| iC,wƑ0Pi04 솀Ғ€}#;2)J)#R;eC ߐ요ǔX,aӝ`ۧu%b0MAI/pk42a2_yCPZy 3>gB3#5$`a[ @Xʙ> %6ZBs jIEf8`1 @]8q SC2xwBpN6Y(qe zp0G{`2Ғ7BL000 ``:@喂^Gcg0&@*7`T$dv*X`a1|‰d!{ Ú(1PIab$5Ia\m~j?j0@Y|:? B 4* dra6|ψZWtGޘX dRL I#򤂿%T/' fL3W@XɨBCto$ C" D3 Hܞ%"8 { RCCSR3̀R7P&Wwz~(W%t-ה(jP/lw8yF 'b!bЂO_,Q 7t\\ww.^ܟ{٫䔒+liɁ M, ae``)ZCF`%@`[4K8TlQ ri1;`g၌{fe-%$u/M_UvPha4Z{ԓ ,`ƥn&$‹: ::P4`Qg0WC qu1 UIK%3»CY0XgGGb402KA01!D̒` Oe0XMn(E +p@hc2|aB-2%F`Q!) dⳳ#8[@7堆Yh!:y}ŏL (h ħAo9č% )"0*1[q0tπ9`a0 +m0r`0Db1Xp.H@; !alcY`1`iHBY ?00 1c Z{pJJrya)H>%tul3 FuhY'8$4C8]a܍CX ڦLؔLou(Gp0'@)`Q/ƚ(9$I|;tJ@'d PhbPQI Rx&#? `xtB RR Ĵ#}W p@Ha|0,L-5;^$#G3 @Bo=/X|/ T7pPۯNd01OjzvQet9'^LbR_פ0@*!ĆPǓ`PP ~{3( 4 1 ' BPRՔeEGX0oۭѿC~Dt^AcsnzpV`0`b[6=x ^1;~c^W1kR`EXP2LZ>vF o# {4 ,r@`` @NM &v{i,oXh4bIN,+b4(@HվSN,0T$_YbB!:Nǻ30i1 &q;Ͳ7 :H/!#~d 7_'71/-7$Zv$ k$ nZ\f=LP Aw8f, 絔MF,,hiCl@WBXIHcW)$w%tݰk'-)ľXO0% iE#L( <йSr@AK Ai00$ 8Jo) d0 ( Wl](LA0 d $:P2 'X41*ku% l"hb 94(cPG@-A`U<A2V:Al^3Oާ{t>R:W@9EI(j0.v+x ]$F-$)'3LI5^ptRC?r!C e87w1@pXƀ+$!I7b{&pĔvOU1 (¨0B{-Q4|fOOMHWt@)ߒ~IAI1cn67@eCJ+X@ZY?| &A0KF,1[2  Ϝ>vpymp?HxI]bXO'nX ɥi )ZJe'҄5 vk RM8&o 4u% 4X` ;+CPgT|@?ҀM (gJˀ ZkLlWQcY4 ܤ! U`tP1;~Cbh`ae'?rt@v MrFPZM 7 @$0 z]TJ6ڏ̂@ZBۛ/F{@SRS`LBFb 󀀒E.Ͷ | {lCwWϯ rg !#!)|BAV s@,&$+t9 =RR Rw~G_V!<,oe(8e~Y|wA.+[ 4k00INcz{{&3e#2 P FP A3dP`Ҕ`2Z,_t,c#3x[J.,tܑ|ʹ+tز^|V%C B8*À9#Gۀ^K%rzSHbPφjOtEѣRWܜN'HJyhɥ Ӑji@  Ӊf >p*L,fώr`* 1Z4)ڙd!9-Y dega0a3Ӳg ybfHk'c4^+$6LHA3 pBK:AφS 4``{{hL@ %ar !dZ2 ƅ4``aNh4 S`0S e6 @`Q`T3J7?aB)I(iY %0ɡ83lc @(fI[5(R> Jq-r@`lɎ`J C},5-aܒx}(bPZP`bga f7 s@P.B(E)(()ltxi1$ҋ1h @MGd ,1 90QmA-W 0 5!PRRR{$aMPC w fMOeb &} bjPQ>YL ;!dl_R[4w|= &R:P#: 0О R;~CzC5ؖZ]$0pĆ14@/quo%* k  0oGC@` (1)e6;k@;,_|5v]0@ 򸩤@x7#ĜP(` P419:P`@ %159xU@!,p ]T‮۫>l|W~:0! Nt6=ך3$j< v&$ee4>SbV_Q(`IZKܚj,d:JݖjZX>uKŚE1[^cq'8 ;,>YeC1ƾ @Q,Q/p(L̀0[ˠi&!bq:( ZYnœ>K(0|~*CɅ҄Gp#_HK牤-B`vPԘe|ZM^HM} %{wae2~O=g)!CBM?J~PXq B @C!%hfl@:U)&pT %ZPXaD<_lMp8*d%% rϾ$Ė5 `5k,_dd[$ |-!<8j3(-$I) (?Bz%rO8OM6|K(K IRHHi`Xi$6GHbVMH !1S2Z>$&%$+`bKl܁iIE$nODBFp P Ĥ7 c0OnV""ɮ°`‰P#%@U) )(& 蛃R wf1?|~؈h@TAT  F|c5bt_~d; \$g9w%/xp@AJ @ d5ӊP]XaA:z & Q'?~ Zv'FIJp"o e  Sn))p;}\m@ ZC>aWDPr~c}@*|)0!Ͳ{0 Tg A-G)0 p-w4 `JB w]\C7R:E`WvH ,,?>b $.` o&s~Y4` B!<}H Aa dU 1)$`B\n>_ - &| 0{5@hd. 2x ]E#wQl I @boДEVp X_C0f/RDiEr0Ќ5 Gq$B-80ƒGOA=8nA-lA -4uDTPVdޡ쳺0@P.RI'a#DѠU ;oe'~j@,4 Ke T K/iOBI4@%zI` LdQ"PPbCLN'zHb.RxoOa @@ KņHE FBQ|MQ7- e `NAQd1RIih&Xo8ApKe'K; A΂I 8{WMHwe 1&MN7`1 I!eE &y0_u^*(hi 45 R1NFdi0BF&'9NI9{sU:wBYԩ+W$gFl04C>JB||5s;n qʿI-* &X B2(B,`:,!E _H)̀4a-ޜVFJw &R qIܔ{rXΣ( (l|LLP-d@Ӝܗ2mK1-;@+씱LmUML h8yiS?#J}j$# F؍7& Ab{pp 5󯜀 IlKҀddKG)lk 0 XnC1NpD   ?`~דBL ̧|A({5u1(͕v@@ Hdه Y &v^ˆaEp‚#u`@<B9ͻ`p7'fy!(^=i ` 1` S~vZa )gdӀX ^Q7GtP` %`+ue bN^z>` H>2ƏL¯J@ I{o [ =cP` A0 bV,@ aǧz@(C@+վNG}׾d|l sJH|$>ބ:|KI!4P__-s J V T5\9#_IO|>=) v̲GCOgJs_ _w6t )B`I";_̔`"~w|nI/rb_J` 8\aa6Ad/ k $ܲG{@'2ƒ Ԝ1_Zv :t2%#N})VNbZ2G(gWI)}:Zb +laHǺ|jԁbRNdQ#0͊r7Ư7$21HFc2& Jz7Fp VΏ0p!B? :&qE%!YD> i@CR8 h5CPw\ K %3'}|fh`a7Nm:ɠ Os?( pPpjq#ؚcS+BKK9>N7c]Ap`Ap` I EF4h"xP  C''ѱ43 ɥCSZbPW\@0ܮ>p @d>G a$qi.3Up- &bkP)\hŹ F??'>#FapݓNZC }%$7XksW#v:~s$N1ooJ9}I (BH~$>b K158܎<40В|HŔ1( &?GsKF q (&_ [ǨR!dtfxC}1cP WbPOkp!6]ƺ@| B^M5G wHem)K'-eOyvɠ!/S I? JM/$\XeC @M&B:PR2 )=}!&ʼ@-1`:I`ƶm-w@@XhKmGx D;@_d _ Un{ Nw bD%<1t}2` %% $0T+iD4 + |B \@E,nU`d+6:( Cf R2C ՈZrH@So(f1Zml ptxdm٨vA+ƺ@:!!@(Fov@@ba Q . ae}hH@' 'R @В/F &ev ɻנEwlw0 Ĭ!P1\`G@ɥ%|.4Hd VY ـh/7sN - ?p g$5(Ae09@'+%ïx8i5=_y@2'>QEmx@h<R=򠀒x@N7wzp pa>3lWICC)%'7 P^^=)u  "% $ , Hq] yn~{ԓrPWIGIxCup@;Nd1r$V)bd@Hx (` b7#^0 2F|u܄x@0OAH _Sn{F! ( ~s8 @2 7;όH |^Y awİܦw}t4`s@8 HDG^@`5&(kx=@@`B/Uy © q:|:r2k@B@o ByHYh+cS d #M (1GڴP\Yhs31 $$У۴b'`v) @hk)jސ( Z8\L @-2k2.8 e? FOgUvp KOw`;J]?}~rjo/JCbT%.@tLxMp 0?] !eAA@04n !a|;ղ@@N"!?zL PG 0BSjd &@`b G#lWBfƒ1# * rl,Z 'ɐ|1>$ I I@0H ؘY`Io $~ctH iF 63@NMBwBB`';I1nOr ,nO4dM)%$}|@/ v@Bd%s^00 BA_ B:^p@:dmolP|4nTOi 9玹p*P# ?+vY,u @@ 8<ـ .`BH8#¨. 0N, IȔ}0@BL&'q^@NI׀ eq`}l! !zߑn Q(`: @`Ea:}`; 0 2 ! ʸd8iHphnQRC2 A4 @(418paMB2;}@.bb Ftm A45PhkփEaA#h z>:t '#r>&9RO_H @;B8'@ !Br頁POO6$JՔ$}$$c;԰0 \5~bbCCVob|аȽ`‹!=%p5@|(4EtLtZ6OW7s>QE4RKEd_[@&dC8 xhPicYp}7ew'zJ޸ vTٳRuIฝ 'B"C/bi1j1-`1]!jƁ`k}HbP GQpP4~ 3 B @ԞBN|0 x Pm?u41~|v@@"hl8}@5j_3{0 e<Vq]8jJ)56\4i}Π tC^-j<h^6 BC!$z|y`@X&M(`(d ,h 7wV:OpB~]|aIx pM*iC L 8 h+}@0&!(| 0@:Hbsz@|Xj]{[4\"ac=CHA"7s]_`vCjF2逜 [m{ T *bqo, 4p @Cɿ 9>P. <0F@hQ@P@9 Ha(wx@P\P ,3> `Umi{j?>@ HB? 1meRF)F;m rt(jb ;/<'7g}p@WE1OA 7KBtǁ<] Ŕ`%d~;i`?P@c!B8P곀*ieBR @dAN`&&}!9@dE|1nT}@0 Pi B6יp0|B3l:M͕shp2@aYӀp \@a@TB__08@5 P2?txa>./Nr ^$GC#aPu  +ǀdv?'` W'dq sW8;<*4 Wh c5m $c@`a^aǛ51:(8Ѕo웪#~ ""G,$ޫ"bb}_"X|N W6r84iDb51G1 -`qcD'. 9P |Cgȕ8Pß:y@?oab8ACd `r*G\NUy:O4tOjjN3%9$ s@2fOXQ6Ϭp]' W'? <(' FP tNY<; ,OD9 P((.4PN"pajGna$ ]yDF"7`7x2h0YXCQZ>P9p> jiŇ=x,>`#P p kUNic`D̥0@} > y:C J@B! ט@adM [ ŵ7* Q>xb| `#.ň5oApq qd D,_yw ; Ô@n!,Ś ,ip 9q8xg#@>.=q^a<9Gui„C|A%CEpx|:X,lلO"r=uB @Oجl`P B` @'?bP\~J $gb5`#1'& I-4vp]ANq[ub^ I`6{uQ`'! 061  I~ "PIMC@BA0aAؑl: j@K>ͩ IE'37FH  d7;0`H)% $p̀ p4\ah8lIA 8 2ݚV>LP Dt%1 dy )-{9*:ГX@G W'"NO%5`t "Ҕ` W'Ȳ:, W' )H<" W'3Mhu##tʐ5H W'Tdie)AUN)GL>P W74 EfZҔX W'TH"u92"d{V}GFT3""98 W'E)#Ȳ=%)YJl$ry` W'3JO&ʤNG[ p \@a@TB__08@5 P2?txa>./Nr ^$GC#aPu  +ǀdv?'` W'dq sW8;<*4 Wh c5m $c@`a^aǛ51:(8Ѕo웪#~ ""G,$ޫ"bb}_"X|N W6r84iDb51G1 -`qcD'. 9P |Cgȕ8Pß:y@?oab8ACd `r*G\NUy:O4tOjjN3%9$ s@2fOXQ6Ϭp]' W'? <(' FP tNY<; ,OD9 P((.4PN"pajGna$ ]yDF"7`7x2h0YXCQZ>P9p> jiŇ=x,>`#P p kUNic`D̥0@} > y:C J@B! ט@adM [ ŵ7* Q>xb| `#.ň5oApq qd D,_yw ; Ô@n!,Ś ,ip 9q8xg#@>.=q^a<9Gui„C|A%CEpx|:X,lلO"r=uc-_0`!^ajN):12)jXSPWxH0*Å49v;N#s+ߟl{TO<9 l${LI;pڶ8LeY][n8؜x0˜p`D;6#@BOXu2֜@_!`/'Pp}x!@@X8KU!~r(@e$ XYrGqtŁ5U.$w^Ǭ2IJp5 U^`cnS۹[0IKS V'CGS텻`WȄ\jiop;џ`ٲhH}fdPTL#t8Zhp:tx9ڼx;AG8^%p:Ju)uѧ0n,@ W'"NO%5i!l' ty 9 fͅVe,p[hc `?01Ϡ <9{,yZxj` W'it +lQpdx_҉'רDX/&=yd:쓨p6.tY^Ѓ1՝$ÂPq` W' )H<'(|8N,<фWu 5u$>^`p,vX>q {z5XB &Ξ?Y;!jw?`d R \k/F(tK W'3Mhu#72Y(^bEBa,\^cu?m,bIf͆HD *a0`204Bz=c@; `1&LBKJ~;@PPB?9|? '~k> H){m@@R2_@ Fap A (#roFԮ;RnX W'Tdie)`:kxW )PQXv+F ?B!( aH ͜@( (FŒl(ih /^crO?n54BV@(b``&# L(e` bt 3찺mcS- W74 EfZҔX W'TH"u92"d{V}GFT3""98 W'E)#Ȳ=%)YJl$ry` W'3JO&ʤNGS;9 t8]g@Sgx#U X }fPLjp;ugAl"tTHF а92Xt;@p v̜N"Dj8;͢z܎, 8QO^8Yp08Ȱ<%.!G0w[8<8X8аdbqp:J D^O G،؏db(q[p* #T+# 0X@ntDj(Did|`x Wa" ])`4|jT}LX2ǯ!f`ߙG.Fp]ٝK/ &8z^}6(@ dPWj Bun B {|9q @?#"@4'؃ `` ^$ndq(,(K @.R@f8yx Xf"EKB|uLut>" .`2=|㎀Jl@g p8a"0l<<+w!gL"TnOb faD8ar$l OBÀ7„l)x7c-oKl%o_]S1ǽÔ@-G {dc@]^ _^ t^'K'/Wx>@0t v d^FPP9YPX VN,9 #x$<\ZG( Ũ 4m.;Z. OQlAZp`mq j@8q u ppL;V |s!nys hڇe\Qt}spuJceLB5|P8a€rI'o < g^a#`Iۅ.ձǚc,Jp4W' 0'(\ ?:.wY(?j| !`GT3:bk!8B^O`d5P?`S>iyO 5ag>B.Ł\­r.$wP6ArIJ0~n Ǔ,,H{j(E VDxiL upNa,NU?PX>u$%‡u)ݰC"d$D|cT|`0u3':`9<ӡ0vv> {qX'ìAԧRSNaRYʀ pdD gQ=Ox]@-ByYgaA3|ໂ@c `?ٌn}QwA`}8 {/ a V ŕESGP [p:0bR @nHJG `@Pa %7Ğ(r2z !/w[cT'eS0@RLŀm<}0h C@`'`` \CLsKG)l"# Et'ﻹw@h R˼0! NJ}'O"rÈDO@k:F@Oc[ITcoF[eQ Z ;chTG'e0pU8 ?q00G 4 ܳM̼1` ? _'k :C !@9,y˸A@=G/w&uN8~ @a'm(ԛՀ1+#t|  Œ!)0Bl#i p0R JA|0Y c!kǐIe\ IxF>B)4D4hdja7  &ϒlL!bnOW +Q :@}f3(J]{ A<P_b_J h!ݱ;dv?TT `M&B o՗W?F] ;aJv_ v@ ( @@ܟҵ~M8ɨ@_@W[0,>h'8v&UK[o4J&gfﳀ 1!8)O @ R_ 8?~.4"f)OPq8m R@bFJ;@ @3)e'%[q8 OfL ŀ &`; &W*-hēHIHF܁ z?@ LR:F!=C`* jC ̔vRpT %.ݼmEpԡlR1z`' :?U `ic] K- _> h @6@ᘰG  rOC!@#vćVvq4,`l1} fmxR5`j7>8ɠL 5!ݽXhj rv| x PRIns&>䭖l0(?`E(ԲW3bEBa,\S|ǝ qhdO#f)by:Bq@rB8CP(sNuQ vI 6QEpY ?PK -׶ j PIZt PCK0|t4 +gJ^LGgy@@0q o`р?0-q.b/9-coZ`İ,Q%&D ɠT)w\M|C+uLBJ=tn}@b gFI4'iҀɡC:!ڼNG31F|`PI#  `4 = @1d5t0b>~0 o@ CSd(>%A1U7dXN:y?#}8  Hx ! C{7S,ۼ  HC~x TA !A5ߤ8Ä^h8 @5&$22@BY 2hH Rkm3~k,@>0a()«Oт1ۿ (, 0RiA~PB97XT q CL# 5^$ @tRQķ``XNBW01!#s{@|`Maf,G @r~: ;|"bJQB2nn%ï @)rjFq(Ϩeą 'C!- n!K~K8'ġ9g-$$fvr5x818iy&   /a "5Єp> ghM- lo!Q5?h_J,"݀  t>Wl21Q(O3}ɉG9;ـĥV١#u 2ql\8o:Q4$ҙAi0 @bbRM0 MyW@ @ 6L A0Aw} @@bC&!O3 U,En>½L#QX €n#8f)^=ӛhaM(Ph<(mY$d{"pU3H8/| ܔd|Q5AqerqG*  @,F:jBȘjPg` W d"`h`m} @D  I[ݶv @x0 $m#Q@@0( d7VJvk)4 *[̀0&$r eNӈX!$l}3!@ ɥac @X a%3ր`@ `Zyy#fL!G@93(y>P0fFc 01A-?}P0e1Բo' &`Lda@3N@j@0!'n˟}8 kT. :Ae G1@;H̄u}7T)*9}t`@NB&J:.G@@~ Ѐ*L IxqB}<5A@ !k@1,. ;/H]`n@\1fKת/;/XHU"iX2AdvP%=>2,1G!$BB`^CfVnQD+e A^|@@IA/8BJ4 7_0@\@~ G-Ì|¯) @`K )8j4|  @L+썶u(Y{HfޤL@0Q(#6~`:!fu&Hwn0PL JRva4u`!X_?(h@2bh@4ħ d5 ONҀ- ,`YyxZxXwNVG5;" ԳTd#˸@b9iQz1_r@1&'7T+)x7G^4@04^_2M*!+@ K-[Wːh )Ҕ|u4 K_0MH ",XG߱Tn(ύG@!400)n `2{2 A09@.hGfbrW bpxp"@*< 8m[<* ?ր+BTa|0'iX4N@@n=@B T.I.xP;![5xy PپרJ~Ic}~ HM!dG^$ @B)o:GhT[|'㔫5^1[Cz o.`@+0d*c} Z I@|YG@m(&Y4&(I Cu0@дfKҦaJ1D޳o0 $0ev* @B I' P2Q60p'wpx |X59քh_XIJQ@N#0]XXyIw b!zC H‰@` =%ad7` H`1JC uFJ20 ܏{@~`(M&+LJ2 p (d FV;vւR`XLH݋(Vm1>ZzM۱j3d$*@:N(RRvNX/uid>} $ĥ,˨M>Y5 .@T!wL:Bz @KC#@2ԓr:p04tf@ >+l5ڠ" 0@%@Pw; PP9?i0Ww+gdf҂n{N@ߏՁpQ1?↎Ṳb{lJ ) ;)FF+jRiOO]4)NfD9N8,zتP/ Q}.'=@BA6w`9f*s(Fn'a sHd"vL,yQ.`0Ha7Lݾ;3$<44`bn+_|v &Xmq(  6B9-}0@0􀘴 o'Ak@!L_@@ & BVbInaq (57 Kz*`&pP!{kȂ Jp0 aF@t` E-ԅ (31HB0f /a:26$ tL)8 4 O-)H8 A#jFJvW0 TZvn` @&rReƧ$(@UYg&Dz/=kQ(JP!GYTqT`8 \8]>t;? @@DD*~ 5`2osȀ{` 8`u̟::< Sǀdv?'` W'S'Ю 7`OR Xΰ ,`0{@B2j `W2x#Yl4> [YޞaǛ5`nm$֘H $pրg`+ WYrDX2o0 S>i sNjX&X0 D >l` F:} :|6)O G} X o`vE z p8a"fK8 ``3טD7`>8X׈а0CF_€ A' yQdKX\DX; ˧A*بs8 5"<}0  gD@ͅP`_>͈, !lp\ uqk\O'ݮ\}ۚu@0X8U4تM W'nێ=>jx.{`lKraTQ8B'P Fv@f:~ŋO8_:eH uGmb} 6mw7{`t(Y}F$AׁrZ z4$pshz 5`D"c@?<3Yy|E0 'D[{Q'TM %=?w|` "((*Έ<>3X(:`v DxdB pQ`74^Fツ' ܀)8ǁ`?ȄvͶe,b `90"WfwY`?\{ `k9ŏFö W'JRK'KwG C^^o /יɘh`2?X 6|b48 W'2!ßeyuL `6>#"vxmDX/&=yy;B ?G+/cpXus`qaRl W' )HoI"dpb,,1hqnϱ5as  ,E8O W'3u;:Li LQWD[W{B4q _x9֦J3]DxXW  Exu< ǀo{dx368va8w '; 'W(b4M8};6d W'T&ߎoXF%Fؘ;1 =@/x8y:`Ekg¤μ j7 k+lV/r#<b.€@o|kH`6;Y @@2 É恨@ W75REY?O({I0 ILICo3^b }A #䃡mz$ow> 1`qOA^yQ f?W"8?!hx,9r(t*w r}' lQObb0yb?H gpxzٵ^xʀh W'F`y/JS:N!PzF},}$AVO8>,@58p@P{fhP !1l> Y8|D#l W'E)#xRטzF[eyP5cApz-@=L*@qJ4pxk>`RMd-t;? @@DD*~ 5`2osȀ{` 8`u̟::< Sǀdv?'` W'S'Ю 7`OR Xΰ ,`0{@B2j `W2x#Yl4> [YޞaǛ5`nm$֘H $pրg`+ WYrDX2o0 S>i sNjX&X0 D >l` F:} :|6)O G} X o`vE z p8a"fK8 ``3טD7`>8X׈а0CF_€ A' yQdKX\DX; ˧A*بs8 5"<}0  gD@ͅP`_>͈, !lp\ uqk\O'ݮ\}ۚu@0X8U4تM W'nێ=>jx.{`lKraTQ8B'P Fv@f:~ŋO8_:eH uGmb} 6mw7{`t(Y}F$AׁrZ z4$pshz 5`D"c@?<3Yy|E0 'D[{Q'TM %=?w|` "((*Έ<>3X(:`v DxdB pQ`74^Fツ' ܀)8ǁ`?ȄvͶe,b `90"WfwY`?\{ `k9ŏFö W'JRK'KwG C^^o /יɘh`2?X 6|b48 W'2!ßeyuL `6>#"vxmDX/&=yy;B ?G+/cpXus`qaRl W' )HoI"dpb,,1hqnϱ5as  ,E8O W'6|㣂>%?<\zaZ dyf%Bd:AAW|<,\{(y`2*MZePj<:<'>#VbSϞ2Tq#Ņ*=X>,}îȡn<g x C c'NA8}Q:D ǀiؠ`  W'7s͏"$}z3GX\n0J)RV{ N܉u$>0RdClW5`-cjr)>A0*6a}yQAxpuF+-X < N@8udw8MT(SW0U\H;X^`N>gBpA{`uDv>0 @tH |ϔ;^`"@`y`^ 0py]vO4 @>: W75REȞc6, QOEטkCQ-o-1u slëz!4x^20E!gf'A<|As3#꼁sv>   GcיY#s(3 @׉_caڼvt quoĞ>Ajd(H¨{=#09Xq<p8`>jp`1A*@\ 0 ǁ)dA"" W'Oz@Q _0MLDX/xWuCp L>p nIB,,נ$ $9Xf"-6ԡ5 ^=fQ^ea$Xifhy9޼KPS -A#:4^'ϧXԾFas<`4l16*: >Àr US5>P  W'M!+H Py:L<Zǀ,RG3B>'1Eg԰h(̼ws9ͅT(9R:*8:`vz]= #>`I@-xe 0|t1S })H".RJR"(u".RJS.Rڍ.RrD\>6x nӡ0|76njo'xoF;")H".RJR")H".RJR" })H".RJR"(7ccFH5`]4|?K` @ {C+9y.MNMq%z!,R;Dأj j }Gj }HJD\)sU#}x)JD\)rER)JD@ })HԈg^``!`  KG i4i`'&([% d HYsb}0@@`3  %938e ,3@1n^qx @0X1!$c;G7s@)@g[Vo!3dZs|P( p_J~ٹ;9 0 A1B IaZ@20(e7<  QL?l. V`А)Ҏ5K" A K(4U.Z_6Ә:@h rlQЀM  @( .-րAh"t01Hs-؋` J,t<Ό)PPh rso+|,A4 0`NC0 1Y OeD_@ @@_mҀ?p< kfB! 731; E 0@4Dr[ j@B`:PZNE8 etwv*RPp c0o~qi`h B^"0#1 '؀<a' 4K^@ Fwh Z`tPV֐@*&4 O)97͖p{@ ` RDԡ+oP eM()KP]bd L/poP@F,@NVX3[H >N!Y ^0ϱ@T3񫱀d@ - @p BU8 0z@0 >iFd0 )$l] $ tl_ p02E;sꏽēМv)GjjD_*`VBGO *􀁚 $B ܯѫF  h }j ~0 S)>p:(z@.qx! -ca!T 1 % >.` ( @C?  f3!\߯7!' 1 (G(u@Y p#ZaW -UPs_@ 9 rPu҈?EP@gİѮ  pQh3pAx.B SregP c`rI4RR h\ղJGX`RINw -jwӹOR?X=(rER)JD@ })H".%qA`4ЎP&(V}¯jM ? 5 Po/ߩ@ ;o@@-@vv~y ɥbZx`p -af_00'Hg!! Nc\ @ \;+֍(~;|taxA( ~ΒWg{@nBCRKJ."'@A7{h x-/oIP rO$E b/ʀ 9LKG`@`P,Lñ}a$ ; @NBX_"x ^@@ @$ $WC)ֆw:H `q` bC/4jS_~uRZ3Ԑ`! C~rW @4!=I%l.  C2 KոyʁT`1,O`&8pRwBi[6^@XjvS p (baXloe ]`0`#$m@ aNd` @b; ^@ `I%fJ텂@ı``^1zZ1x ' nMp d$5x] 'E@ȭ&lV: )(;V8@|3Oɩa5T5bH@bw&"xcA YpFk?&7 ɠW~g>x B3kTEQE!ZAx P'4A`H"]@5@M8 ɀ[H `dX[d:@x` c{@.IIBv} C w fMOeb &~@$dd d h d oHҹ``P0EtB(haCJp_0 " j7 #^,8\7]$@ O:ozێs*,0$''uU iAGa<& HNNT\(:&1#K2rN"  })H"/@ khRjR3 ߘ~Ḵ ko" @1nX5|xΠ P7!nI  `;_}?>{΀5l@ d'Z2EX2JNG[ } %|tɠ6=0@NMIeGqH't@@BІ @LLAdҐ1ڀ )@ {ai0B^#{@b_BǠAP" ]2&o+)BYLT+ѱI8vi[aĀ"*Q,N$+ 0 2`!$27ufVU0LL)( ,o0g](0 }]т -` I$n?^0d! %|5P@4& 0 U)RC^[J _HB!+,5)~xN =BIAqWh8 ,q@S K (72Q'^@)t~Y1'u~ (R7mNe|4D?ˬ!8!@S0S]'-v`h@09i+8@$0'+rVgqgE G6] @2( Rި8B OB/ )d~L 䤐@c0k}7b/ulD@3vY;8 @̆RKR HBJ~x2h ~efE2 &@`RI?o"b:QvϏy38CMtER h!ˡ0kJD~   c_g_+k _j* KJ}@ th `ݘ1-* `_|ED$bVH{@bRSv8UN'{J  О_?:"HB 3ځ, LFe ,*!AH ٓ@ P51TCCg?D_(P0O|Se򎾊 xّˠ \ ( @e~;ި LrSn=/`@WěB,np jWH` S>خˆ; F-Jfe@piA;n1foD_P K J;5P  8H S`ԀRq9$ G I5Jâ/T :#R0@_D49UiHH@*GJz>ư P !Yցb(XN$3``Ah7SzRJSt;k} A^` {pՀ3i ,1ȱwӀbMiPKFJM4]/ :Pܤg@Y})p'zB#[ub!0gbRT*"@(^m" .%҆u=¼8;@Bp\E@* @`VH R`=@l RB) W|[8%Pş{I@p @N&_@:+.GOtތI` 7wp%yȋ",HATPjZ9@I0B @js 2>@ pԖ[/a[ So! ' c@@)|nJּhm} ,L@ƳI ~ bCtP f#:_P @@!bqD4l;w@ZbQESvŸR]@xah8[V@@) 9 slN J` tGIj9KUT 99 (?waj4hAh %%Qe'%P@@tނX O` 1݇8@n*?x6`@nRJhodhx-+膂>p ,@L ?Р35@@\JB5/-=(q Ɍp 3$@j BvgRJR"I’+@ճdEµ@!04QIHVQ>Gtrsס^RX KזM Cp*_ /4@ vpRx̞qۂ gj!3%9hra6T\Hv&ět^ WI}֧ /1Hy@ץ !9i~WFmbaa-Ƨ_U @*7&gJR&!49ӻz& 'baLf |R @LL йilQ &,rbP{~$ *@e VCz- pjK@ AL!؃|(g!.|Hh <8 @~0 ~$ŜZ1 0 :K?܎+ߜLА uu'(,;fFل'_P He#SWR^ 4@1IB d Y j#Y@X9e's.@:*.FNd1͈m".RJR"P:&NB x}P@] 7;(07'd'8;| @!e@ux bf\<ɩl--~3@@7$8]h  m̴ 5(Y'1ǎ| 0d I78:  ɠTO؀=Ha3|xSb_ObzSRbK);0NH)˕13f0  AFZ &@&&V@VJR.RJR" })H".Rn +GGJR")H".Ruxh AJta"h @rS|JJ>9οA * KJWҗP<{@) ~o` $ T RSOl`$[VZ;ԝ`LX@1thďׂ @BI J, !l,=P( Y0ct0 o "@X(d%(&}ӷrFJ"WH$< 5;;` P4s{@ 0^LS;^`  Gߙ44/@b!4}@nH@a ' c|7zïa+ ܒO|~߯N1k$ԥ tw@P kd@.@j9 M* @.c|H&4;!#!+RՏz@s"8a_G=\s @` EvL}I0ĄdmͩJS+@0@K`LI(9d"v'7/ : wyɅP$8`vἷB?=z ALRjI(+xVZB:>  10Q$>]bI222)L@ TC2w>@F|P3FQR7*)H".R })OrEN^u@Vh Pkk g#_R $0#/#_yx )I40Wz &(LO@N]*4CIM'?܀ HAKlV( tY@3 T1`;gwOf^& <9 ` []k@d"ҍ܂;P4 GBweHv'% ϾJRtܥ)rp @2&^ !/;y)~0wbXie{1c@1) ~C1Βo|:!n77 (443Ž>$ LQL0g9Ԗhm~)P(t,ٸ},,ADၡ~raҌs 12{[wJɟF@8 7bd I?h 3?& u_/p@ KJ@8޼a95`9] _ L[ FY_~UT"IĘY4'B'f &}+~btB 3o F+k `H Nl:Ғ٤uR)JD\:r2k@B@o ByHZ CHCJ F?;j o|| Vw@%Lh0?-8\ `{CͺJY\)rER y~~"ӝz @aNҶwG@bpNNQ{@; @ng{BD p zw1 @/-, PZwxQ@MI[; @RQ5糀$JM ?7D0'(YD΄o:  ,e` @'A7Gc= $݅D4e<̠ @`CG(Gg}l5@ Ɉ Ae|>. !c5x5)yT6ow~*A$hhQ P7͹ғdp#l*p`n`$ P +Q~d욄Q>?Z+I)!u/h'8v&UK[o4J&gfﳀ 1!8)O @ R_ 8?~.4"f)OPq8m J(% 4 $o$c۔2X vRrUwCdXO ha1 ;J_I®@P&: I4gc B^Se'1Hr ,A /2Qw)JN.R?Q)vo+=vA]*4L_űᥖ/fA4  Xn (P `Òzi  @ g$<%p+{>G1`G  @ >P3H@W:Xij !8Ԕ:\ KU`r0(}8)-vPbCC+s0 0 .,A$r #imjd& & #?7^2 d #d+ ?"y?)LR" })HORvxBAx%-#}k o^ 8(Rv<*Qra Oր1p"z7FP 724L&dwWgy I!wY:7H0`^ (@@E%dܺҀ0A'!ܨB(00&4*翯 "p*KVu[n؀%@!.-?;"h`I0,@ 6$IA@8 @\o>76J0зfQ =G̐ 1e%b~mH (/YH !&N0 s #@c!H0 efК8I󠀺`2&^w(eH$!p ` !*8 g j~ !`N^x{!0$,BԖQ5 rHE!6j @b3$leą $2vh  <]h+|} UAh/}V-)%#3@FTbo (@Ύ>|LMAy(/#B҂pЖ eS?"$ex7GY@ J+7@&FBK&%dy A\g>sk@Ip@@dM@M63`  xy 01Nt%$s,aZYY J26` brx@QHCdk`\'Wx O<./Ѫ`D,QZhL H#8 5#qԦE%2V<6"KKzt7v%#lH٢ͩJD@ })Hj$V>όӷΘ47ZC!Q3QdUы I@tC,+u҈vTܥ:v%/GCxH@X@n7xo>g hR72~6 KSR`^Z4BF>FI2ћ'p  u) -)JR'rEPD/ōGm.v>/#z2rc")H"  })Hbi]:(Q,3}"yB&vBuˊ(B@ϼy^y(s1< ) $v\MC#=^:r2Џ 1͉`g Y13nH'SB$X('3Į.$@2@F ላ : ܚC) I@HTL+P`ndEfԥ+)Έ7;!q,7\E 2w?ܥjt.QŐV_4!Z6E ,{m7zҀBY3Y3eX) ڍD\,7H|ȁ5 /ЄWyEzfBB24\)rD })HJsSJ?|!h\)roJD\3Rj ԤE5)pe6jR")HԤE-6l?BH,H9tR~(#.jR'JR"~ DYhm))B^ER })H".RJRCk#F}FztoqDm>Am RrC"ܥ))H)JD?JR!RRrC%ʮuGi0:e)̄h K9:o F tb0t)`&Q$)JSRx \Gb,2󘀱@Oe8pv>`a] D`<aD2?gv 3i'a /qr"@/"πX;rtX W: ,aM? gY:X{,@,(4'X;,( OrAg6t>rOl(?#pD. 'l~`Ag@{\x:" )ԍ `O0nO1͞0:R}< B:8OOA>""@`S#DUG̺y[ y<} \x9K" Ni`m\jM W:5X Nd/>?l 03ƀ@F ̰{~9qlÏP4  -& DaGBc #k hFotO` @|:-`J:<vp `B+=*',8u? @=AaE72}<@Bа0;`V$@6 |7`rB<z<['Ƀspp@jp@ = :HwM W'PP #}DW ,. nH1@t:3O4 @>/h l7m , 6tK"Y?W0`7w7=qO`5 X[Q`cȶj,>kъ0OÃh~؎ 41: @ |"|'p}=Pa@?8Pb@<>'XUHN.@nMJ;-@@F$G,^yHEys@D \]͓^fNc恻^w6B4 R|:a M@, >[ n$N".Grtt W'"nҎGQ<^<(*<x"P^fDmc'8\Kx78P|k^0c2B(0[г9ĪL^eܜ$0-tZ,DzÄB`"5O;!/P ȯt(-ya揈( ؀A=b.m\GG|:0nqX=OQ> @ c8Sȡi6*9X<8Hy, 48NXu2)gHóp#dL##Mݺ|ZHǎiF0p-_!J{+i]Ǎ%c͂Hq8q:f+a֥RxxP4su_Z5-dh7ԡ@hJ(jb4`v}4:"@na%udqkR yIZǀ}c^=<ڨ9cǝO0r"׃,lx#fy L*:% W>%?<\zaZ dAdbcsK>b{^>pu]<`Ekg¤ΞG{B58 UDzy?tN=> y-j@[6|y p !(|}<@`4 ! N>T uaϜqq/:OA;w'ǀs+@ 2dlkXRB@*pICRl `q?VliD9d_~r-bWF3_[*[&c Sz!4xOƙC~"搳BUc Py$ncǝi*3=mb=➃<>yQ f?Sz4xKeA܊ ,q3<{ K^d" b"@4=\H'L/^# x 0t(gyZA. 0W[X3z']ؖH< ="@fXxF#WV#1$*[" Su̥QG(*yaԶY/Q?|f4Ga'<sv> PZ1Vh6#.kP488+l9S̗kL>AW:n$ ]  v} C ta@BO0: pq?_rO@8s: dd&< K'O'p4,D xvcQb<}٤xArsiHO`l<4G0Ԛb oH 6^W`i]O$HXE<[\an#mt5&;OA@H1cHrJB" mJO1-k28yP5cApzx [8O#:cX/gӬjGH'@t*8i]=?OFΞ` 8ãS08P8> W&l!)(<PW`bԦJ-OAZI'` ',Bd2oa^ b`*C>4$)E^C!3GD` bWBD& B]ߤQ W'$ oL z3VO0A1 |a/ @M JI@e p` /h2B3@@bY5VZav`ĆX&HIap`/ jq3~of+`4 L yE1H, AT]qsq < x ,g t@P!+W;sρDxYDP Qs]}d /+n /b݀pYffߛL P:&ɨ%!HNC2a@ / D.M`1Ay|7&'dSlOx  ~X[iA3@2ZrܗX[`tع`/,S,0p#!3cو3s,@6A ,;pҢh!n?&1? fG1݃ԘR ,FJ)pp 2xɁh0a`EeG@/Đԥ A#CS#D d2:|0gθ4D! ^32 @pFrJ Q C3$@P9` x>P'&VLH( ѓ8+ZC9~ފ2@`{p|lHHkbP3d%֜AA+q@BWH(h P6e n-GP L01X04"Ĥ Fu ez9æ W0B OȐ\lYK!ӱ ~FL#P*PfF@@@4 :  a04: ;Ol](9)0NCOO|V(, 8R4{b3w+p%@/@%i)żLQI,C/ez!@ߓ@@,W`槼,I>ādu(=4?v@bx ?? $}=#aAyO!pY<s`3 :/X8FH$XhKZyr|yl9"p%n:@2zz38PAiAܟbP ɄwJKA!=kߐcZ`#r~4 Wy<}@NKmL 2N,o9DPp 1(+t ra5(flIAd1'L %R]cS, W0F` }@`,K)$$e"bP%656N!½0D #^ Nx R@#Y>p,@?'Á͂o{Hڈ@?Y:|b8à""8 {0qFqA$ဴuȗ0B(L|Xtm`tsmvmShC@Gl ]<gD8 NAR`,89fX 'A@>~B_ N O(5HN#PD'%% W@am&`#ġp н ǣHBP y` W: u=p8@Aă  6D@;#X_Bŀ'IdQo/3AHx0h:@6/l`0,d;rdP;5v!v >%bHbv.`- \N-`>` b|> /h l7m , 6tK"Y?W0`7w7=qO`5 z?\cQ0ިv 00V@  C  &(w$!X# 80 @4,&!' %̢PCJooqP9Da'|) @ 4 2@`n'xx|"|AY`[ -$?d ?'4fˁ#wZpKHX Sp n#urHúeb1r/mq0a44" @O8v=СaL@hp @h y7g)/p5pY@`!!` 8Xw(D  7e eXD"ߊt 殜#v@{} ." m *˰anYǜ"8F< v`"a>sܣp!d,-Yduר0 ȱπ[5nxy @5hxaD xPo_Mh ip1rH㣠SP:z?2 CGCy7#-}@bo*`ߣ4p r pSun֣;΄(w,,p&Dp&Pv`7EN#@l?"ty\.< x5Jq 0 <`  9  (YDIJJ^= @;&Q 43\w  , 4JSZnز^ Q W'"nҎGQ<^<(*<x"P^fDmc'8\Kx78P|k^0c2B(0[г9ĪL^eܜ$0-tZ,DzÄB`"5O;!/P ȯt(-ya揈( 4OHCz'D9{eAJG/#/XuM W5"hDIx|G Tе`!ay? ^fpkќ-KUy݈z4G= 0~vƀ $T[uzpquz%PQ>؀A=b.m\GG|:0nqX=OQ> @ c8Sȡi6*9X< lSQt$Vܯ; ;tD` (anW fr݅3@!O0 8RDO"eImv}i ;:JAYd|+`u20xhҙ6 8` Bہ& ;TnP f%-@+ L4Xcq1 @'$&r9hGp$94dXÛXi `1Onq,`0`LF|`9EP CXn:')$`FI ':@BU<uG9K@xjk.B=0c^d"9YcаEPxO勏s SS6+ΡOxu<<'>#SuX1)MLgi&=aJABĹPXu @~Q43_ D^43- @@%p߾|\"ܠ-WѽA ?xNNN[f `P HNO0?=aI``!KS!(:NYG?6<^P f_'q=wO0J)RV{ N܉t )`2RB @6OumX EX<;ZO1Ei%Fؘ; tgBpA{`I!D2? ޞahܘ5X `(Hg/gl5m4 K,dq_>`0 R ~w/ g 1 ?n $4 fJ@` 9~&e):Pp B/x Į<^wIb2` I!F| CX@X8c! *CJU#_pk BV3mpp5=e 5ʬ :a5Γ O@2 @|X Rq& ҁ]̀b ^?P @AoJ@YW͹ N N 1 8X_@vxzS`W98(nz S e>%ejY:@4 OZ $ ?`0(H_0-B7 j\C N7b70 % TdBA ]F@dXjS`0 &by4pҌN<>63dlvFhpP(XA|||B8 F47폝RwJ8J~0DF 0 4 ~f  S3-ND !B*_4p J@v{P@4!f*@T00U J@|sjCw J=dYEc \L@ꌝ<$0⑎ E#X r&HuJ~ꈣQ9f"(ϢR"}G"lڔ9h(L}"xGxݤj3ioژd'6T'acLG:}<:57GgFˋB{IDhCu a]G!jp vA})H9JR"RTG)JDQȓI3кn6!)lcX-U a1 @ h/x`Y@KnbOl@hӒ3d> @  P*hƒ;%9G ϰF @N4*M,z@p: J7ŚE`& %! s:z~2&h '+& 0@tY  0뤕#?3!E47ؽP `1/ nH{.@'' NK - >n Fy]S;[; yb`΍KlRäQA dlv٬.FU6SX. *>[PxVN|mDP[}QSwԈ md  F:6@@X`Jm"()H2Z[PJ2tP",& `!C@-H&4i/ CxP@0` 0;BI$Rh&A @ @ 32ZId!o;PRZh @5)Jx ItY1|NDh͉DԤ0뵾y`h3A[Q(#D4 #M8` MAUO䔍#?_ }B:9{z==>> _<82?C]R"eҘDQ|rY`ąRQ#(4(4p S!L!⳰  t R[l_({dj0,2h` Y4:@@+˜3?\@X 8`Ae$,}-%rI],̔@- 0XI-;pN"h(RYAWL5@ B%Ҕ   ! wODŽ:212bC3 2@`7ߨsh @q0wR2X f&J?u#6R6\d7KzF 4&6^Op]  &É"S& =AvDu: Zw@'@̯o> lETQK  L_~Р9J7¾;<:wϢ(&gh;JpƒmVBAY2<6mG6ڟO?"H!r gJ Gk(C)hMcLua'+$Bhb;9\oIdaj qϰR%5JN qF>G͂"(!JDQԥ"()H3mEd}Ԉ FX/QSqx_><(:f~^!_L&mT58Eeč? ޾!G 7}d vi($^hHBi7;CN^ކn{+U4>%`R;0b"QvE}G奔x鷈MDRi CxpmpN|$uH@ZC{[5hSip w@d }@ <BHJLF~2Ӑj~O;xq{k ~P_GRk0<ݬ#A!J^&GR«l`PcBg \A[C(R{G"g "Fٴ3X q&( 1 1S0FL Qӵ6)@GrɅs%0' 2ꔠ*^Rbͣ4)"$n4\B AI+% {ou[i@CIњbɈN^+pn%H` ?8 PH_)`F!)v dϤmK$&n#6Sjp z=vZg>@ #ޒj.8]rg<`cac5fXi*hhph #Jz4 ,Yov0Ct5MQ4fFrK.꺶!8*p z4kB4 #oo6jP}k޻=B#TڨK. rmzr| 6"YUL-.HQm`-pڿ \?v`-[UH?AKsJ'[}yX W'dg3{WM1,W#ԭ:'RY:, W$%3`4>@!xbLQ=Phxi@,D\$>`T @1$"g: A2n"A,Cp @tB&fߊv (G=@%#k;a<0ɠl A7b%dĔcbR!L5(LC/tBX@K$!+&(`!a MYIG'T_ - II^p!4$͖ WWr?l/x` RtoSQ( x 0’FPTT1db Ad~[͌l0N 1@h` bK&W .C +CxP4ׁ@4&P(CFIe y`@,d0c~փsZm<P( :0R{e +ӘO KV7,!?M ! JSz`tXtk,s''ԥii,՚i[RTp\i:y/Ŏk6,0@cHI% h0 `ܐ(i+7q4QP`v!@nd1Ѽ`*vB &aedaIP& .(dߒ`@K @=;FHiFIHܡy`~ބa9|41:[, `L Py8L,@-v`PHTio\#΀  P I'$5+Gv?@X:ŔJ @,y\g 6xP#^ЖBO[38|e0t` c+)&Ed!l A09IB KCd7堛Е@ f#|p|B! N uU0`*-&;8d߲:Uc`& 4 +@Oh 0RR3>`|m\ĘPi0qjs<^(ţBr4]=)P3He W'HH&x @L Ҕ;S @8f`CCx̒l d` W' pۍ C܀Dc{pr̐(iC; Y28O>XX ~E'[$؉O pp@h1g5LDDb}<4}9OJx;Yi`?qJ:-Al _` W'=pCΎ} aPWPXig/6$,V/ }A @cq9KQDn}?0P5kv@ǀl?<&] =ӏR wqt$:GY".J_B8lx.ir\qaʠF#h?C@S|# q,iK$2s TQcQǁ*§4zz4tq@R @J@hoPxrwow6+ yImϣ@` q5kxy@0` 3/,RRzKn44҆߹>|4υHJw;?\k1L <*[xCR@U ܚC0E$(jR3~8O W&!jlXj8^W:x 8`'H_%o~uSݐ@ Ђ5iK1sO7L!C]?hXAaBE C֐ǜxW6";sYYЪJ䓌ȱ ,wOݎF M?<:8P @5'SQ! i qZă !~ΧU60vrWI0Xb{~@e\a0ZPŲ0 C ,1l5@~@a73"@ BKh8a{}ׇ5 0!}A k ;IfBW["`nNKZ>v$P Iܞ*X& Y@Z贈ŏȴ#@LtQ '/r28 @ @B0iaj[?Z^ SJpI)8آ-%dO/Zx[!J?K wl<| d-D~.B/8Dpy@=`"a>sܣp!2w,|:82,s[0} < <8?a "-Ns 4>iyO$agG@@\!)^.*az@^(Bq8Σ6p Zޘ +J ˓\o)vp iА! }kj@3`trC 6!% @,8 @1P`&,0cxhOp`@ @@8 PƺR~s+1΃I ,."z  @@. /||L`5x 1G>)ԛfP Q pyO1.c|pkYid:hsl}*r P}< pSunkQB;8M,Fvt83?N@ (0!9bzT4О3{ Rl|wԀ2*!'vCQ3b#r @bFi_LpHh@q@2!7j#c DB``& aAB6d0&hB@(mi8M܍ W'"iG#N ;}=Ȕ荬q'7 !z6fTr )@tAޅ%Rejzx$XymF%U3Хq@!^M <4H`XDB=V<~i̟۶s=*byh11N @qC'0```  8nc+H@3TTB'}rH!Vd)PͰ (3 m @X 蘂~1.D  W'e! BfI/ ܭ|` CQ1,u` K(j=\ISӈx< Xj`BO HJj;;@F44$c i$(g 0 %8,x)k0 @, +p+0(1 @C049 $j}L h@Z %Ĭ4(( J5ㆊ'z-I у5hώL29G0@5`$ r`10pѨAm " h 2p*Lm۱m. @-pQH,ac:V+8` {f2@ ` xrW Ի)Fp `А vLRɉ)o C, @!@`a @!bJ0!{!c|U< AT@3&#bJ I4aa=n̈́@ .@A GdtX+RxiEB"IK;Tr)GYMjF e W!!Q.$D$5NDX 4V)KG|͚ͣ =)  &!wq` R3U@m lE-W&"D[P}[`)v֫UiM|ZαUO."bxTp}$)3 ]5Dw%.)t<Ȉ&'U:=J!70=}"u\ Wm l)@?V!ý/YBxT20Ntnf:MN"pyg[)Bn\x, W6uǀb`qcjkj/|xȕekh."V DDDHsez/׃ e&wt>E85AwT;X Wm""b)\"S5sXU ; #Ttj\ W&i7eíTHYF,*D i7,Ɉ \?v`-[UH?AKsJ'[}yX W'dg Br zm!R"r\RId W$@7Ru蕛E0䚐BhJL` WWrުu2a`;&INx ?\ #K ,7 @4!(j@j`.&{;;R *@P򉁠 ܴ?uGY ׷zf6c NMA0@Tkj;0s", Z".#vB -~Wf|` vP@RM,f BRq< @  @LMH4Ѳ^=ׇzJ2wH/d}8zfafp[3p `+;.!ðj0ba/bolCzS 4mJϭSڦvZX@C, W'Q}`'@ Eoªmv5I N Oq)@4@:H)(B qX\L@ `XhHU gdd1+|`)  @t`*M0bP% ZQ``@) `)x`l\ HIQI8vyd`GNb5O!/, +#&i4$ŖZq3A3Y<=(QHfaƵ(Zk14%C^Bꌎ?@/2),H[@xVwQmpqTeT PPnts:. 4!fBP rSـ唀Ww@~PLP(3 \,jޣ` d3Ќk&dÀj Q4OG[.ZL(x rR+ra7K<]y]` gl83+G-~"A6!IytQ煨w\ :!C+%g/!^$ aKzB@o2gc6 W'i" @$` H,sl `h``B`lK W)(tvG !!0j  I$ 3@(7 䔛l.|IC5=!0ɥ+Ho%tOv䮅zrǁc;FP#yŏ%gRtDjXHt6_ŅǸF Q4pxRzb9*uq((p< ;2¢Ғ%}kz? :(g0Og0G|" @ 2%Q!4BB% .BƊ-I&b#YJ` <*Z:06AP@+ (1 TB@ _I3g4/xtL-y{0z!d/ZSA]pb͈uvF4*ҹ$8,F@G 7c'Qk||HC8$ԝMDT%,ADžjg '4G:TڕW@W:i4]$>U4L Y@ZL}gBA+02Gǚ$ъ8 }O+v><O3'| xan'î3">lոX}1ъ0OÃA059Eq8=X|t,XqmH$=w0EH8\*c8"B@ }kj@3~mRwnGڦ25o?c YKJNfHG`p@ BV~? =1&HiH, WIDys4U1@>P1 "0^bd^&@<&rhHJBU-سDQp A4! 8o~Wx  ;05]@* P\Ph @}9A=<_o:6}ɠ +QGedh@t H4Q> ai4 c5dP@@ ŝ4ñ S(Qe`OGp `+vo ` ` @`͊HGP@711T O)ͧڎ@,@@LB Ao,uG!6FC2*=t=Ua:-a5L5m$)7r4 W'"4GNHz?b rn.) !!;Ǫ&G0A@TЅo:2@ @`vl ˗hj=@P ( ͪI `mX0TX1Մ2Um˱u'fCkڴɎy'?VFR@& W'e$" r}«@NRR'66ifO]] lWX! %3: @b|I!h~@S2mX0K YFX܊oVSE8ڐ 2 6X W!!Q.$.o@$̖ٝ}H巏%), u}wh DиY1̀պEVRVMq<?(!U9>-W&"J@GZUI7k k:W!<|8шqQT ]5Dw%.)t<Ȉ&'U:=J!70=}"u\ Wm l)@?V!ý/YBxT20Ntnf:zA Sz@3뭔7Zh<TN W6uǀb`qcjkj/|xȕekh."V DDDHsez/׃ e&wt>E85AwT;X Wm""b)\"S5sXU ; #Ttj\ W&i7eíTHYF,*D i7, sp.n/[LϰP5Vbt8 sfdpd?ҟgZ 0T _߬=[տGU.0@@%e~ϘL @<47JEdM ɤą5H HU`$nj>;\@@ @,4qA4 HYT@왝%W@:` I&5 &_6!«D਎ {]v}(om   h;uXNAHEӐR?u, Ha8Z7  I6\CE'Fs;* zvNa̽xh UfZ{$܀ &I㎹@0(,C\CQ55W(yz @x~K-Nocv4A׸r@1i5+ ;fo$@W 腀p?2&1iNkC^Ra|v ߸J4?E`P ("f$bu@@ 0!o  v@3,RHi3\`VHU BB!19SV H@0Q$ nB!$ %+Hb[|f* aZ)5Cc{>؆M5SFڣ8 v%Q1Ӑ#lA+``L, CwV4@@P+?@(Xр@ P @-5~b}H,z[dx @*w!. @3,y K@Oo /+% exPh1Noy@044GX %e0 ņ-Qt ,e B&9# A rL^Vowgc. 6E$ kfa^h^q^:^Qd d(_̀6*P NN:Btdn :\ .%1=؝t5[G@) qeL(` À 43facԻʁ@&!|J'hB ̄dam!Nﺷnp`; :A3eliCKJ$>) [ ~~J+^=qTF>ΥGTTʤ>j@Y6p t?m0 bi 7 GO~*6> ]i~Œ `+v`UK7Q ΂ ZHi! ZU@BxJK6 d=Xb^_H %0 ;!o0 zX1K@;!`f sjp2$h@ b6)9;;uA @br fH@c5-JXH`b~@), %'v>Ppbbt +#6퟉m.Yl5@#jTBjU҃4ǎURARIj ppi t?`@%(Ѹ,0ImܛP iraD`ai2Q۸k0@:=E%-x4$5!np` I>s @Bԧ@:;l" 3BS6x Itq`@bpNCn8;.`ŀ;w- c*2@P !ѿϲv9` Ja`8 JҡL!tL(LF(d!6UOhSr,V)gɞ҆;:#vSKT< 7%ϒZ0sTE'GV@?1 ǀX'QH*|bh''Z B=}nFTBj1! =k"_ci4 1%&rd律(  @ Y":@À A[} &4@h)=?M|:  +%)1|Bhmd^ףP@WJam͂ )Ġ*gG^cx2߬`p iI )lv<@]2B7DsB1p@@AD# 1 n:, )Bhii/a_F n})H@B-%%bwBq &$H֨.X$޸$vп\F[ m "='Eȡվ1 p5!4U>d!5%Zg4= s&$#U'4vY> q1sUHdbFӀZy1À9$SQ vr{IƳQ՞ kq3*O*8D8ڴ(:G vGltX}lӉ#u>$|- \Qª9SU"L\Mq<? Fh>+IY&8u@GZU Y>K"B&.DD ⣃N vR➤DL.MYQx;35ᕺӀ z*p 8cmkօVˆl}N<Ut rl@`.%lDPsŊg@ \]' iT\ 5\@/NyX W'I%&T0;*9\ W$n)"y]3ԱԮD򩙦TSe VWgjwpE[ӛVޑ͉tp8^κgV5@SfZXGe W'fZy)JuM]U͒?=K[aq[bPuWZT$QgHR W'dyJSmg*|me)AUefi )2*dg)M=tgzjL wGF]Τ%Id}e[O#P.`7Pt&rRk {=>(Mf}eG U%&}+笋[TR}ZaD.l9 @F GjG̥&r \侑Z@gM鑤yH}h(>˔CJtg2UL W6B[Pp7r?P1Pp: MKE&S")3O)Bn7p9=R8 WnWGi>pX}ҰaAwp35.>ZkQ@՞@]YN L DDDV͢WU;.Ec,s}d*V Sg , W7iZT"RNGdM+=3U'XԖFJFGy8 W&i'2֪U#af`p:"w}퀾yh \]' iT\ 5\@/NyX W'I%&!8_ )kk^'|.@ kh<Ƭ ڙYե"ҩJi8*` W$n+lN8F0b-âsUY~Q:`lUxxU$Qbp טx"\@Δ)By)\8ppy$6ex[w r(Mf}eG U%&}+笋[TR}ZaD.l9 @F GjG̥&r \侑Z@gM鑤yH}h(>˔CJtg2UL W6B[Pp7r?P1Pp: MKE&S")3O)Bn7p9=R8 WnWGi>pX}ҰaAwp35.>ZkQ@՞@]YN L DDDV͢WU;.Ec,s}d*V Sg , W7iZT"RNGdM+=3U'XԖFJFGy8 W&i'2֪U#af`p:"w}퀾y sq`7eV`-[&g}P *4uY>JG)-D\ ^Q:5!dp d`H0< ]uؠ1p` /_BPVqk406ˀBXR V ǧQWEz24 [cy Qz1 r۟@6Hhnat`&HJBI|Ac[x K`Ҁ4%tŜ[sao`:0 0pj)U;1}2b`a8Ēw5=GT>| 0 &04[u` XqiCBQͳ d^Y)p|-`zT|߲>lxJ:Úx K2H 3$A7nۈ vҺC /^P!7 0 n (^xKC=z9`Q8~"\ BHa @NZK#pؼ燑 @T0zLL&`b[6fÏ'@T$B͆l_P{T% JfbL@ p< @1‰ӀZ>*M4u@ a.ڢl>@[4v\ p t?!gv܃}  &fr0jH~7;{@k^M&JIcS1sbP r?l?3|̞G's2$' BQԟ&8U oy;N"^[{? e4 ko|w: 7h !.  @Bh`j8k-wZ@t @0H7 !'͈UY\sV&*F *42MǵcNx A!PRo֡U)pO4[0X@0!^K)y@!%~%ΰ!&!K#ΰ!,!5G(M Z @Tozp@F  4jƞOh0Z7;`0,bm@vtX֌dby=p 3 v?`@$((Ӱ(0ܙNHAZ:8~\A"o&߁x %ѲR`z^ @2`$aČ_q;P 8bZ@:GwU*GP0Y4 6 C&@`bhoғ;f<(EﹷT .̄ZR;<(RQ۴>(Rwa` '%oxbF()~;mx +p 0&$۫k"h BP]WOsru@;@ GHh& _j?;m{@@| P-\@  4&@Tt'%/g  pCVC)w(@T1 I4|vG1 fсcML0x'@5 97vP@-x  My_6: !P0Q I` Ok蠀  a,(\  -fYbY\l"W=I*OgQ,,Teh  vl17RN%Y]!n1ﴂ !%Rv_1Ș ${@+(8#~ǚr1 @3'_dz?uxdtc{ 3fWC%,р-\ZQjB0ģ(`љ; \3CJ:>fyp@/ 2,0h |7׿I0 p*::!qGw2`;xBG  ed@>Z~K9(`@m`߻w(~o}@@9 Nn=: ɝD4;`T@)@?/CH` @@r$ Jdh P X )R{@-+e~ZA4.` ՒƏf k*1 @3!!`p(sl1 0(Y0'7r5a7 PnHB[>倁 M>& ߤ0=b@bQ5ݍ@ A0 Ixu1' I`p!N &~c@'fYdRנj̰ Kt֤`T P Q˘` mUL0M>&߿y@\btZA`II1=X ` Нcuy@: JX2t>H +@9oLjaH&l;eBdԐIi{!RC-%䚧8:# \D@HYEPFJ:R0haH@o_ {@RD0a]yU` 2P`nd`|@2 `;̞ ."hP`|xr0@7:P@`P``a}t:􀁤afKi{ @ N7@ /1!f%'77V$ *Vad^䧆-'W5OA0`_[\(908 v/\`: 9@]:&dPo ;^Q1?dt @oRPxCz?@LdHd!߹ zaBH^8NRc?J(ITIj N^;m ؑ@8pg,sZ;;@G,gf  @C 2x|pD 8H Ht-k΀0  >f)$L =w`A0%ja 2S~7m6v2S.!B!b+6T3l&!9z' cC@,'HMtz>GB qU>G@ v/ 0 04N"h@ $!o#/qܟ䜔`fp( @;^@P@Y$w&@Ac!q>&M qe*PajKp  !#U(FT%HA xYp !KYˀ3?@_6XV,@ d`(LFmSJ@vM!.ZR'M\OϬ$huh:mn z*O]@ !Р kx}pqLPt6ϒʮrt$S8 z4>jT N~:"v}C@=i@7$pYn6w#X| h(TQZ,? ?H z>K}nb`Zuhy[cp eWO qU+uȉPq g[ؐ \?KNW2\ 4 ).eb, W'2JM=ùkqD,JO0@2aWRRy]rnK'E W$mMnOF(  7K ~vH6<q Ad0`,"hhrBMH3M2U%6X Vw˦biEDp.$ b,W. `? =D/Ȅ@,{vXJ/ qCκnMP3ɶ[, W'zYc ql@<`,:$x =d"p4GJ',|.8ebjjMN*#sM~ltڇ,P(Nd q:> W'zXpu^a`Dȁ,n/`{$ T?=fN[ZtD #RndOciS fi ҕX8LfxqrjiHP24Ұ wg^a? B60?85ࢍx b -S:OO$h #oHpx5/L\k|^h ::zvـ <  @1x(LJWp <z;Ki),qt`H<#",Fr,4aӜG*b  }i%]i[VلBqPb<@n{W%g4 4`UxE`P!?|Zel`ҍ@Rs1@(Hv r$7 ?|Q31)HoG@4 `)À[-,M( Ng @8DR@b)2DQ`'A3 5` p9Р+8 0@j1Gy+^1_jiDw6@2u&YE7gZFѠ3l W'd'haDbC>n?n;ie$0ZIg?2;lP - |D#]xN6@rRNf:@E!@[sH \I Cnm!prmSyH3E u}w['=+킀pGRJn L 2 #j OEp$y"Sb]ʃ5(}, $֩p 8ueC@_ .9JL@ ]5Dw%jST2XGH>P});Hy̱`r0 Wn*9@?`ƒi+\24x}k j[Dr)E&ry&ndaT8 W6LvgB,`gx>x d @.tAp(gPB0ϰylAdzFM xYPRGmiq``^(<<G W7iLRnIU+ )ΙLX W&hiH[i 8@;`8|[J @ \?KNW2\ 4 ).eb, W'2JM=ùkqD,JO0@2aWRRy]rnK'E W$mMnOF(  7K ~vH6<q Ad0`,"hhrBMH3M2U%6X Vw˦biEDp.$ b,W. `? =D/Ȅ@,{vXJ/ qCκnMP3ɶ[, W'zYc ql@<`,:$x =d"p4GJ',|.8ebjjMN*#sM~ltڇ,P(Nd q:> W'zXpu^a`Dȁ,n/`{$ T?=fN[ZtD #RndOciS fi ҕX8LfxqrjiHP24Ұ wg^a? B60?85ࢍx b -S:OO$h #oHpx5/L\k|^h ::zvـ <  @1x(LJWp <z;Ki),qt`H<#",Fr,4aӜG*b  }i%]i[VلBqPb<@n{W%g4 4`UxE`lJX를D6@2u&YE7gZFѠ3l W'd'S,s6Z@iXwB]Yl<呄}g ^f,!d'y\nqPp4vox1A@5cPig5~( 02AH /N<=@iY3Еy>@E!2}`@s&P`hjZ0Fv hxx L   •H9"Sb]ʃ5(}, $֩p 8ueC@_HvtRg* ]5Dw%f@wP F ]X<Pbyhz ɠBk݇1Ap_dz`d8 8|g^ݘr0{nH>P});H9̱`r0 Wn*9@?`ƒi+\24x}k j[Dr)E&ry&ndaT8 W6LvgB,`gx>x d @.tAp(gPB0ϰylAdzFM xYPRGmiq``^(<<G W7iLRnIU+ )ΙLX W&hiH[i 8@;`8|[J d })H".RJR"(u".RJS.Rڍ.RrD\>6x nӡ0|76njo'xoF;")H".RJR")H".RJR" })H".RJR")H".RJh@<'P1~tdtoFGOVEK+@8bD߳/bs=f M҂a,}B&`i 1!M@`@;&~f @:NCj`r  5ҋǺ1!gIPM|ft|j6@; 8bC5 -}qjR"AڔER?mNR=pl7=z!bER)JD\)rD })HԈJR")HEQ)/vA,@J@Ѻ2w"*&! =^@ 1gF_ݠ@  abHge^/A wl48xy- b+Owğ!dI4q(vHkk kG Hj+7$%JP>@ [?7Php4bjV/@vdIOs_܀0. `-@  @$4渶A K/bi4k7'bLL)VD A#_ @@Rx-J$8 V@1& S?稜%o} ue4Ą^O': $썻6 0&HIvR @AJSͮU0@ *Z_(fZeyiDdQ׾ ;-zbӵu\苔".RJR"R)ޔI7ޑݼ".RJR")H })H?=".ROݢYe-9Jr7ބ*>G~5cD@t xHQY@ P @-5~b}H,z[dx @*w!. @3,y K@Oo /P_7Z @3NtuR@;rYHA1?SK  }Xh`5t ,e B&9# A rL^Vowgc. 6E$ kfa^h^q^:^`@NIJa-"8s A  rayy:@0!bJA!dIz@LI Q)= Bs4] XM&~hi4g|;@T}JD\eҔJR")H"  })H".Rm'`A0)<m`@B$tZy{@ bbooyHd`o#Õ}D4@^ҒͼYbih_p4ؤ C@ƿ8@0_`솃_ (` BB( ~7/qP 9kd2LZ ( 0I,o{50@Z@jGNNee@d@@Bvٿfg*PA0+#!z` @} KIA/@ (۶~'նz{z{]Ćѓ}G7u_ @s&5&`0B IRH [ @MJH_V^N׮ Z Sb;埽)4Šsp C@LZ /0dV` hB? (A\$4 'l+;^X^`!(!n}@R h`PR>8 2 \L*Wau ɠ&ѷ` .bCJPs;t%\' @ et'ۣ.@pEĤ[2~߶8(zOF|^k IQho/YbLSi͕4"./u6y>vuYqpSorER)J%I bFsフ)H })H".Ru@ F\%Wrox @ɅI Gn%cxP0@ *x;/ H[/Xo9@f5)x};Ȅ(BFٔehLPͯ  H1&#Ɂ,ɉ:9 b^?!ق |Bܴ-^  @(l_F c>'=B畀,)+㹆݀0)+JkE0d 6T+-*`~aCtIEdqh 2!j+ 0D0 Rgh},p @7BIp.-f8AL(0d b JRp12kS#'oӣ#?TϹ4@ %e$5|=x*`Ab7#nf1Q]ڀ\ݯLB*JHjٌT $JLc}`@)rT҈awGWgk @94KBSЍϝl`} 1tMA0 d$0`5`@: (G @p#֌Ԃ @qH-s/T$b3= "߾ @8@/HNc= @b@b;%{upD"F#^H "[#| NLAd2%V@%H|Q Smt4HN@&`@Md:l :&  /m/lZ^g^  ŀdC "e%|P \FhNLBb[;[0@\!,,_$w?up^7[0SXL&9 @, 5(d[hFIK - e A X!P` 9=N;| J2N'ߠA:F&]``iFAW@  2ޘhQ~Ҕ"-L?@ݾH۝?ԥ%)JD\JD\OJ~mbBru#}o A^` {pՀ3i ,1ȱwZ4OR o( 1FDŽgf5 O! c&P LPqhC퐔7|¯(U(p@/ |A !hܻ` Hn6Mawh=0BOx A"rv! Bg^R` @fmяO7bM,z{^$C4`,xHhJ:qh1 /frI"҂_^G"5lBnL F^W%Ҕ| xg@1awJ@Ep *DRgBp#܀@;p f;x (!!m^`)g)/HPig)ʼ°`aE`< +mw RC (%,~ `*B \05((CH/D4 Q x@iDvGި@3 @ď~z @bx &_ӺH!@@1rt|yL,;N`@KzRBؖ?#o 5(F S}>p@00,P R2Y[/k@D0@b6 @ )s1H9H;wEAT ` NC];ecI(P@C 9$|{@tRj G^ P5} AX(g I|  ``'@KOe3ę@U;:)#6BEk@ B q( { `I}< /`IۍB Q; ~^FP5I( r`~ޕRzSm.2ܥ:[77)H",A9M9^ɶ俤S'`BB @LL йilQ  3 D\)H,҇g R~0 Zd|u5@dfI)phn8 5ZWBa+zr>&P- 1~(`G@/G, 'KrI`C Ǟsn <-@!-2BW j8@T5 }RUrE'N$,nމ &%?r>NPϡ BmԎ"s@b^ Ԝ00eP@^B OY%9WO0C},Xh> `Z@#k N` {@> ~!}l {D&(_8É`'N:H~ޥRNFDnրLr`ӫ 9Y$@/+77$20RS8ܥ))\)sԤ66ڔD })H".RC3nA39y 5$^p?oϽ @5N&HE%$ NP ~np19M 0v[d= ;. 8j610o{BqC_Ł\2A_NHe{7>;}4CJs|hP 405 l5V_- : Iz $ ^ )KJSE·͂",2Eԥ".Wj >꨺4bL0"!K,qh@cN iE`>M(@}[@N4Z~2`ɤ+y S3ӹinYzM冸l8[kt9 FH 3!p. J4 RW0}@@TJ09Ĵ; AtTBae1M}Dނ`' Ro@`JRoBH{Bo9М7|>hXi}g0P4?چ> &zfQY#RJR")H"  })H".Ru N\Srf8 BB !Ahr!j ǣAZ @*p;Ietl/ް(0&WF ~XIi01#N}}TP8y Cbwu]@dD2҆怀ؘM ;gJL/p@@ P*2h J'sf HIGnn HI-:7(jBI}j D0QJ F߸~~[^,@%btno\0 & +5ȚjP%We^y1!PF|x|K+ g,H!h}>aG`@#omIh))mv @'1 bJB?m @pBh0GOBrR6p@B I\ C@vQ |u:P GGSy08 S{aOӄ^*{@6,$2񗖁ט p :Br$\Id!sꠀ D0  ,k `tma[%ak @M&1۹AX&1۽JR.R/tRm͠ )JDRJR")H })OrE}o>@|щ@bKʚCq_9M"M },-x qe?}(  1-*D HI&p 8 YDNơn<лȼ_=8#@Nph NJ:2`:N13prnuH @.-(}c^`5!bQ^p 0h̝^@) @2ᜢQ7k˂x Afa@NXvIT(PmƧ0;B;tn=h  U-?%Ŝ @60Roݻ?7˾ i7`d΢U@*ot gT !_C7;9'(` D$&W~K's@ YbVp'`N*i[+elw0pemG5az 0|z^9A`r,X S|pAR[A7$!-, @dТh5^'{@p*&hrbjKƸ8NjO D-p?؀`PL!7N| @ 3,Y )X5fXakR*CWިT eB0m*& n4$<`@U,hRSZ??4E` "Y i1)&'@qw@tB!7Zv V0 NBBNU(br]2PxaF}渑 )R3!ml^!CRY}C!KIerrERS})JD\)rER y~~";"s1e[FA|^]3fSzl6Fk (Z2Q"/0haH@o_ {@RD0a]yU` 2P`nd`|@2 `;̞@2! lLJ.3p NX~|+ JO &ApH@,QL&dƜw2 px.1;b.1!f%'77V$ *Vad^䧆-'W5OA0`_[\(908 v/\`: 9@]+rfHa56 |fGL +^ ^H ~P %07(@LdHd!߹ zaBH^8NRc?J/IĖ! IH fPy@8@r`^8(E @@#QX @@(}@G,gf  @C 2x|pD 8H Ht-k΀0  >f)$L =w`A0%j ,&!!%7vۨsgc/890D"f(mC6ʘb*p47)˨t챤H$0tzz`(V>I `Pc 2>3)H1e@5?jޒ.aTC eef! fNːc}SrD\:2CvG")D@ })HORs5)rER)JD\)nC;X&Po~1߶giAGť'@8@Ȯױ0 y!PBg߇\:H0$0č@jwg9`47 B0(?p*B WߌR=  ?f/}GGmǚ``#Bb7mpVirґ>4do"}RNzR"FGj ԤEͶ)q(Ɉ GFђ~ԤR^h:~::,h--,W\5#f6) })Hj$V>όӷΘ47ZC!Q3QdUы I@tC,+u҈vTܥ:v")JR")H".RJR";ґ?xu#h9JD\)rEPD/ōGm.v>/#z2rc")H"  })Hbi]:(Q,3}"yB&vBuˊ(B@ϼy^y(s1< ) $v\MJ '&rC7ۇRB<]^C\bG6u9+A?ŷ(DIʙ  u(DX _mP!2XxLG;U fA7 n6mJROҜ#~6 df(mᓿ(Smr, ѷ(Yg#Ci֔ժɟ?ɜ3,*)HVh"aXCD I~ b?:,K#6Ԓ)H"  })HJsSJ?|!h\)roJD\3Rj ԤE5)pe6jR")HԤE-6l?BH,H9tR~(#.jR'JR"~ DYhm))B^ER })H".RJRCk#F}FztoqDm>Am RrC"ܥ))H)JD?JR!RRrC%ʮuGi0:e)̄h K9:o F tb0t)`&Q$)JSRx \<]O `ҪKs`X .hН.Xr W'I_I!=ùkqD,@>xq' dr%#9NW'E W$nm! >`"rq`xDdgO0q$P: 'xp,pu) @ ,`m^' ? <%PĴv4}p@-~X!Kao q>Tؠ~Ղt@ B 05%$ K- - $'9\ J#F`| K K3t,RPi1`e|Y 1@cՂpw<,QBͮ ]5DyI)57) OS wtMt`$2ݙBaCE<|@'BPS䔛:}70\`4` d18W4 R=l߱n+ t SZ$…0n`>0a[0@V i/܎&/uv ?20_n~HPcXlBlw '_U&rr0 O`P<֮ip >z_wuvllgR}^#$CHb\8Y3kpdhN<$PMFf`lJXLe='SKs.9qy^bbT8[s=@^P0י.k#݃77yŅYdRͅ`'Ҫ{)Un8}kSɶX W'f(MI=69LN \Pjca v g)or y#4 6f,!dpy8G:'DzÈP3ѡ1aUadF9l$8-̏u,.<Wf)lĎ,}wG"q "gsmE u}9ETT$pu68817saaDhyg00 oO2cKH[0]O2$s~0UZrW8 W"D9b#T \UPT'@,`=)Ypkd!jOp\djY'FꬫQub*T}&` WLTrwzK(T@da8UUX"9<#]# zp Wn4k<Y &&Q@" 4/hMp}#+eu;.E "@jW6Ze24 {>P\(P*R< W7ir&RnI9FtJ\עtQa|IdndV>I W&h>x TFF,*d;m+\9 \<]O `ҪKs`X .hН.Xr W'I_I!=ùkqD,@>xq' d]$n)Wr:, W$nm! >`"rq`xDdg^fi,x 1x<@zw<,QBͮ ]5DxRJMdJB wtMt`$2ݙBaCE<|@'BPS䔛@@@LCFNJ>߯1'!=|~^3Z`Y8FDž ='VYel(Y`d"#jPי/p$ppZ1OTf I<ҩ3L.  O`P<֮ip >z_wuvllgR}^#$CHb\8Y3kpdhN<$PMFf`lJXLe='SKs.9qy^bbT8[s=@^P0י.k#݃77yŅYdRͅ`'Ҫ{)Un8}kSɶX W'f(MI=69LN \Pjca v g)or y#4 6f,!dpy8G:'DzÈP3ѡ1aUadF9l$8-̏u,.<Wf)lĎ,}wG"q "gsmE u}9ETT$pu68817saaDhyg00 oO2cKH[0]O2$s~0UZrW8 W"D9b#T \UPT'@,`=)Ypkd!jOp\djY'FꬫQub*T}&` WLTrwzK(T@da8UUX"9<#]# zp Wn4k<Y &&Q@" 4/hMp}#+eu;.E "@jW6Ze24 {>P\(P*R< W7ir&RnI9FtJ\עtQa|IdndV>I W&h>x TFF,*d;m+w) sr9tzB}@Ha\ q"p]S::z; G.5 8ՁSX8ZMMIau:# {' G#CÖ}P$Wqg9DUU 4~$ LN,sK?l-Bb「`+yЀ4x8v\xE7'o:Ur٤=I~S tmxT*d?%cX'"Jͅjg<feb:'< l@S!d`ݶ[TcȠ?Ə[T *aJ N݇v*JS3j{г/Dlަ e Q:j~^9KP@fe2w<,QBͮ ڋ[髓jtڜ F:nǵL k'$37̰aU1.gra| ďub >f }pDfJ'pptZ-v%j2Y@y KWC橞` (b[}Ba u3Z`Y8FDž ='VYel(Yn `2U#jP}+#}Bb}u LL==>> _<<@z ai48X ddL]g#mN(C'P2+D?j}q(@/c1\XX8x g[ZZΠR-^ǀEkªbMP0Et( Lpd` @ fV#<;`H"9D4)À>9 824'([D3<>bYKjgGl>} ȍ$&6^Op]  & YseBňVFŹS3dѦdB Z _ N(B:BExxy"@W,^+e `, s#;î Y@01bTp }1dx 3HVg{X\ s(RL olvSmT`@0:qH:2~v}[50*`{j@p$-yYc}`X^ϒ$Jv|$ @`0g{rѸSММ?}:B2zxy( @01倝)NF-L~!3/Z@ NWXwnYD.Z jQ_E!(0l5K7Z @c;>XJ;" C k Zk/ ~P"?fm!$ + A{U i >x*5`5;P(#*`T`T C|KC͐-v;u}}۫Ꙩ,5Jais`>~ݐf==R/X9VQ eMRS t0q aX~P$UØЦ'?: y03>r'c,QP)%s"gg1NRC KV`2r]}@@3 $CWN/%lWЁwbMJ{qS^p ™,a0u@ǀ O E E@! H[{D :(2q%>`P*B%!rĘqi8j@?rbU}^Au'y`x`xQt 'b,;8:P ¼|BQ:>!a`REZL6V& @5,&'sI&%[,5l Q(vjCI0` T 91ap @BIa,:yj SSZ?$ 9<` QU\$dUmj4(`gb|A;r%эP!cs@Ij#8 tmv4DdHz4?h8?b qSl,t'Vs6#k0fPP!`rp  HYĤvvX'wPXha!)@(8HMBn͸*b $(QD;oyϗIJyz3]D@;+ ,.^0?JC -;v ;]@|› }|wFa5 @5 +0XC0\Mԉ4Ԩ65 Dx2ih) &V %)W?C!FHx~g'r}& RM +!17 %\01 {l`j@j0{ b_f!^a0‰b6(+l ?jۘ=oZKuH7``S% 30)B)M 3e'p%{H5@[܃r >橘,lo?j" 6"~u^ΥŨ"OªdN88sGn!!AhxuL8Ň0ZB Uug 尐XS#d :rYl@ݸ\%n=}D"q "gs \Dvr'\DNsO>j[8֣NvQ>ptp ~G`#2v{гΩ.X( eKkjT8MU-4 {wbt=EcaNQ'g~-SQQ,852AN &cH#CJUٰBk? z=vZg>@ #ޒj.8]rg=<;<\um@BqEvUL z4k]suTuh QmmTQ::iP {B}DydHML:'[<mlR oojY@> rmzr| 6"YUL-.HQm`-pbitstring-bitstring-4.2.3/tests/test_array.py000066400000000000000000000670121462444752600214450ustar00rootroot00000000000000#!/usr/bin/env python import pytest import sys import bitstring import array import os from bitstring import Array, Bits, BitArray import copy import itertools import io from bitstring.dtypes import Dtype import re 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) class TestCreation: def test_creation_from_int(self): a = Array('u12', 20) assert len(a) == 20 assert a[19] == 0 with pytest.raises(IndexError): _ = a[20] def test_creation_from_int_list(self): a = Array('i4', [-3, -2, -1, 0, 7]) assert len(a) == 5 assert a[2] == -1 assert a[-1] == 7 def test_creation_from_bytes_explicit(self): a = Array('hex:8') a.data.bytes = b'ABCD' assert a[0] == '41' assert a[1] == '42' assert a[2] == '43' assert a[3] == '44' def test_creation_from_bits_format(self): a = Bits('0x000102030405') b = Array('bits:8', a) c = Array('bits:8', [Bits('0x00'), Bits('0x01'), Bits('0x02'), Bits('0x03'), Bits('0x04'), Bits('0x05')]) assert b.equals(c) def test_creation_from_float8(self): a = Array('p4binary') a.data.bytes = b'\x7f\x00' assert a[0] == float('inf') assert a[1] == 0.0 b = Array('p4binary', [100000, -0.0]) assert a.equals(b) def test_creation_from_multiple(self): with pytest.raises(ValueError): _ = Array('2*float16') def test_changing_fmt(self): d = Dtype('u', 8) a = Array(d, [255]*100) assert len(a) == 100 a.dtype = Dtype('int', 4) assert len(a) == 200 assert a.count(-1) == 200 a.append(5) assert len(a) == 201 assert a.count(-1) == 200 a = Array('>d', [0, 0, 1]) with pytest.raises(ValueError): a.dtype = 'se' assert a[-1] == 1.0 assert a.dtype == Dtype('float64') def test_changing_format_with_trailing_bits(self): a = Array('bool', 803) assert len(a) == 803 a.dtype = '>e' assert len(a) == 803 // 16 b = Array('>f', [0]) b.dtype = 'i3' assert b.tolist() == [0]*10 def test_creation_with_trailing_bits(self): a = Array('bool', trailing_bits='0xf') assert a.data == '0b1111' assert len(a) == 4 b = Array('bin:3', ['111', '000', '111']) assert len(b) == 3 assert b.data == '0b111000111' b.dtype = 'h4' assert len(b) == 2 with pytest.raises(ValueError): b.append('f') del b.data[0] b.append('f') assert len(b) == 3 c = Array('>e', trailing_bits='0x0000, 0b1') assert c[0] == 0.0 assert c.tolist() == [0.0] def test_creation_with_array_code(self): a = Array('H', [10, 20]) assert a.data.unpack('2*uint16') == a.tolist() a = Array(' [\n" \ " 0: c040 3f80 4000\n" \ "]\n" a.data += '0b110' a.dtype='hex16' s = io.StringIO() a.pp(stream=s) assert remove_unprintable(s.getvalue()) == """ [ 0: c040 3f80 4000 ] + trailing_bits = 0b110\n""" def test_pp_uint(self): a = Array('uint32', [12, 100, 99]) s = io.StringIO() a.pp(stream=s) assert remove_unprintable(s.getvalue()) == """ [ 0: 12 100 99 ]\n""" def test_pp_bits(self): a = Array('bits2', b'89') s = io.StringIO() a.pp(stream=s, width=0, show_offset=True) assert remove_unprintable(s.getvalue()) == """ [ 0: 0b00 1: 0b11 2: 0b10 3: 0b00 4: 0b00 5: 0b11 6: 0b10 7: 0b01 ]\n""" def test_pp_two_formats(self): a = Array('float16', bytearray(20)) s = io.StringIO() a.pp(stream=s, fmt='p3binary, bin', show_offset=False) assert remove_unprintable(s.getvalue()) == """ [ 0.0 0.0 0.0 0.0 : 00000000 00000000 00000000 00000000 0.0 0.0 0.0 0.0 : 00000000 00000000 00000000 00000000 0.0 0.0 0.0 0.0 : 00000000 00000000 00000000 00000000 0.0 0.0 0.0 0.0 : 00000000 00000000 00000000 00000000 0.0 0.0 0.0 0.0 : 00000000 00000000 00000000 00000000 ]\n""" def test_pp_two_formats_no_length(self): a = Array('float16', bytearray(range(50, 56))) s = io.StringIO() a.pp(stream=s, fmt='u, b') assert remove_unprintable(s.getvalue()) == """ [ 0: 12851 13365 13879 : 0011001000110011 0011010000110101 0011011000110111 ]\n""" class TestArrayOperations: def test_in_place_add(self): a = Array('i7', [-9, 4, 0]) a += 9 assert a.tolist() == [0, 13, 9] assert len(a.data) == 21 def test_add(self): a = Array('>d') a.extend([1.0, -2.0, 100.5]) b = a + 2 assert a.equals(Array('>d', [1.0, -2.0, 100.5])) assert b.equals(Array('>d', [3.0, 0.0, 102.5])) def test_sub(self): a = Array('uint44', [3, 7, 10]) b = a - 3 assert b.equals(Array('u44', [0, 4, 7])) with pytest.raises(ValueError): _ = a - 4 def test_in_place_sub(self): a = Array('float16', [-9, -10.5]) a -= -1.5 assert a.tolist() == [-7.5, -9.0] def test_mul(self): a = Array('i21', [-5, -4, 0, 2, 100]) b = a * 2 assert b.tolist() == [-10, -8, 0, 4, 200] a = Array('int9', [-1, 0, 3]) b = a * 2 assert a.tolist() == [-1, 0, 3] assert b.tolist() == [-2, 0, 6] c = a * 2.5 assert c.tolist() == [-2, 0, 7] def test_in_place_mul(self): a = Array('i21', [-5, -4, 0, 2, 100]) a *= 0.5 assert a.tolist() == [-2, -2, 0, 1, 50] def test_div(self): a = Array('i32', [-2, -1, 0, 1, 2]) b = a // 2 assert a.tolist() == [-2, -1, 0, 1, 2] assert b.tolist() == [-1, -1, 0, 0, 1] def test_in_place_div(self): a = Array('i10', [-4, -3, -2, -1, 0, 1, 2]) a //= 2 assert a.equals(Array('i10', [-2, -2, -1, -1, 0, 0, 1])) def test_true_div(self): a = Array('float16', [5, 10, -6]) b = a / 4 assert a.equals(Array('float16', [5.0, 10.0, -6.0])) assert b.equals(Array('float16', [1.25, 2.5, -1.5])) def test_in_place_true_div(self): a = Array('int71', [-4, -3, -2, -1, 0, 1, 2]) a /= 2 assert a.equals(Array('int71', [-2, -1, -1, 0, 0, 0, 1])) def test_and(self): a = Array('int16', [-1, 100, 9]) with pytest.raises(TypeError): _ = a & 0 b = a & '0x0001' assert b.tolist() == [1, 0, 1] b = a & '0xffff' assert b.dtype == Dtype('int16') assert b.tolist() == [-1, 100, 9] def test_in_place_and(self): a = Array('bool', [True, False, True]) with pytest.raises(TypeError): a &= 0b1 a = Array('uint10', a.tolist()) a <<= 3 assert a.tolist() == [8, 0, 8] a += 1 assert a.tolist() == [9, 1, 9] with pytest.raises(ValueError): a &= '0b111' a &= '0b0000000111' assert a.data == '0b 0000000001 0000000001 0000000001' def test_or(self): a = Array('p4binary', [-4, 2.5, -9, 0.25]) b = a | '0b10000000' assert a.tolist() == [-4, 2.5, -9, 0.25] assert b.tolist() == [-4, -2.5, -9, -0.25] def test_in_place_or(self): a = Array('hex:12') a.append('f0f') a.extend(['000', '111']) a |= '0x00f' assert a.tolist() == ['f0f', '00f', '11f'] with pytest.raises(TypeError): a |= 12 def test_xor(self): a = Array('hex8', ['00', 'ff', 'aa']) b = a ^ '0xff' assert a.tolist() == ['00', 'ff', 'aa'] assert b.tolist() == ['ff', '00', '55'] def test_in_place_xor(self): a = Array('u10', [0, 0xf, 0x1f]) a ^= '0b00, 0x0f' def test_rshift(self): a = Array(dtype='u8') a.data = Bits('0x00010206') b = a >> 1 assert a.tolist() == [0, 1, 2, 6] assert b.tolist() == [0, 0, 1, 3] a = Array('i10', [-1, 0, -20, 10]) b = a >> 1 assert b.tolist() == [-1, 0, -10, 5] c = a >> 0 assert c.tolist() == [-1, 0, -20, 10] with pytest.raises(ValueError): _ = a >> -1 def test_in_place_rshift(self): a = Array('i8', [-8, -1, 0, 1, 100]) a >>= 1 assert a.tolist() == [-4, -1, 0, 0, 50] a >>= 100000 assert a.tolist() == [-1, -1, 0, 0, 0] def test_lshift(self): a = Array('p3binary', [0.3, 1.2]) with pytest.raises(TypeError): _ = a << 3 a = Array('int16', [-2, -1, 0, 128]) b = a << 4 assert a.tolist() == [-2, -1, 0, 128] assert b.tolist() == [-32, -16, 0, 2048] with pytest.raises(ValueError): _ = a << 1000 def test_in_place_lshift(self): a = Array('u11', [0, 5, 10, 1, 2, 3]) a <<= 2 assert a.tolist() == [0, 20, 40, 4, 8, 12] a <<= 0 assert a.tolist() == [0, 20, 40, 4, 8, 12] with pytest.raises(ValueError): a <<= -1 def test_neg(self): a = Array('i92', [-1, 1, 0, 100, -100]) b = -a assert b.tolist() == [1, -1, 0, -100, 100] assert str(b.dtype) == 'int92' def test_abs(self): a = Array('float16', [-2.0, 0, -0, 100, -5.5]) b = abs(a) assert b.equals(Array('float16', [2.0, 0, 0, 100, 5.5])) class TestCreationFromBits: def test_appending_auto(self): a = Array('bits8') a.append('0xff') assert len(a) == 1 assert a[0] == Bits('0xff') with pytest.raises(TypeError): a += 8 a.append(Bits(8)) assert a[:].equals(Array('bits:8', ['0b1111 1111', Bits('0x00')])) a.extend(['0b10101011']) assert a[-1].hex == 'ab' class TestSameSizeArrayOperations: def test_adding_same_types(self): a = Array('u8', [1, 2, 3, 4]) b = Array('u8', [5, 5, 5, 4]) c = a + b assert c.tolist() == [6, 7, 8, 8] assert c.dtype == Dtype('uint8') def test_adding_different_types(self): a = Array('u8', [1, 2, 3, 4]) b = Array('i6', [5, 5, 5, 4]) c = a + b assert c.tolist() == [6, 7, 8, 8] assert c.dtype == Dtype('int6') d = Array('float16', [-10, 0, 5, 2]) e = d + a assert e.tolist() == [-9.0, 2.0, 8.0, 6.0] assert e.dtype == Dtype('float16') e = a + d assert e.tolist() == [-9.0, 2.0, 8.0, 6.0] assert e.dtype == Dtype('float16') x1 = a[:] x2 = a[:] x1.dtype = 'p3binary' x2.dtype = 'p4binary' y = x1 + x2 assert y.dtype == x1.dtype def test_adding_errors(self): a = Array('float16', [10, 100, 1000]) b = Array('i3', [-1, 2]) with pytest.raises(ValueError): _ = a + b b.append(0) c = a + b assert c.tolist() == [9, 102, 1000] a.dtype='hex16' with pytest.raises(ValueError): _ = a + b class TestComparisonOperators: def test_less_than_with_scalar(self): a = Array('u16', [14, 16, 100, 2, 100]) b = a < 80 assert b.tolist() == [True, True, False, True, False] assert b.dtype == Dtype('bool') def test_less_than_with_array(self): a = Array('u16', [14, 16, 100, 2, 100]) b = Array('bfloat', [1000, -54, 0.2, 55, 9]) c = a < b assert c.tolist() == [True, False, False, True, False] assert c.dtype == Dtype('bool') def test_array_equals(self): a = Array('i12', [1, 2, -3, 4, -5, 6]) b = Array('i12', [6, 5, 4, 3, 2, 1]) assert abs(a).equals(b[::-1]) assert (a == b) == [False, False, False, False, False, False] assert (a != b) == [True, True, True, True, True, True] with pytest.raises(ValueError): _ = a == b[:-1] with pytest.raises(ValueError): _ = a == [1, 2, 3] with pytest.raises(ValueError): _ = [1, 2, 3] == a with pytest.raises(ValueError): _ = a == [1, 2, 3, 4, 5, 6, 7] class TestAsType: def test_switching_int_types(self): a = Array('u8', [15, 42, 1]) b = a.astype('i8') assert a.tolist() == b.tolist() assert b.dtype == Dtype('i8') def test_switching_float_types(self): a = Array('float64', [-990, 34, 1, 0.25]) b = a.astype('float16') assert a.tolist() == b.tolist() assert b.dtype == Dtype('float16') class TestReverseMethods: def test_radd(self): a = Array('u6', [1,2,3]) b = 5 + a assert b.equals(Array('uint:6', [6, 7, 8])) def test_rmul(self): a = Array('bfloat', [4, 2, 8]) b = 0.5 * a assert b.equals(Array('bfloat16', [2.0, 1.0, 4.0])) def test_rsub(self): a = Array('i90', [-1, -10, -100]) b = 100 - a assert b.equals(Array('int90', [101, 110, 200])) def test_rmod(self): a = Array('i8', [1, 2, 4, 8, 10]) with pytest.raises(TypeError): _ = 15 % a def test_rfloordiv(self): a = Array('>H', [1, 2, 3, 4, 5]) with pytest.raises(TypeError): _ = 100 // a def test_rtruediv(self): a = Array('>H', [1, 2, 3, 4, 5]) with pytest.raises(TypeError): _ = 100 / a def test_rand(self): a = Array('u8', [255, 8, 4, 2, 1, 0]) b = '0x0f' & a assert b.tolist() == [15, 8, 4, 2, 1, 0] def test_ror(self): a = Array('u8', [255, 8, 4, 2, 1, 0]) b = '0x0f' | a assert b.tolist() == [255, 15, 15, 15, 15, 15] def test_rxor(self): a = Array('u8', [255, 8, 4, 2, 1, 0]) b = '0x01' ^ a assert b.tolist() == [254, 9, 5, 3, 0, 1] class TestMisc: def test_invalid_type_assignment(self): a = Array('u8', [1,2,3]) with pytest.raises(ValueError): a.dtype = 'penguin' def test_set_extended_slice(self): a = Array('bool', [0,1,1,1,0]) with pytest.raises(ValueError): a[0:5:2] = [1, 0] def test_set_out_of_range_element(self): a = Array(Dtype('float', 16), [1, 2, 3, 4.5]) a[3] = 100.0 a[-4] = 100.0 with pytest.raises(IndexError): a[4] = 100.0 with pytest.raises(IndexError): a[-5] = 100.0 def test_bytes(self): a = Array('bytes8', 5) assert a.data == b'\x00'*40 b = Array('bytes1', 5) assert b.data == b'\x00'*5 def test_bytes_trailing_bits(self): b = Bits('0x000000, 0b111') a = Array('bytes1', b) assert a.trailing_bits == '0b111' def test_operation_with_bool(self): x = Array('int4', [1, 2, 3, 4]) y = Array('float16', [100, 2.0, 0.0, 4]) x = x + (y == 0.0) assert x.tolist() == [1, 2, 4, 4]bitstring-bitstring-4.2.3/tests/test_benchmarks.py000066400000000000000000000042641462444752600224440ustar00rootroot00000000000000import sys sys.path.insert(0, '..') import bitstring import random import math def test_cutting(benchmark): def cut(): s = bitstring.Bits('0xef1356a6200b3, 0b0') s *= 6000 c = 0 for triplet in s.cut(3): if triplet == '0b001': c += 1 return c c = benchmark(cut) assert c == 12000, c def test_count(benchmark): def count(): s = bitstring.BitArray(100000000) s.set(1, [10, 100, 1000, 10000000]) return s.count(1) c = benchmark(count) assert c == 4 def test_token_parsing(benchmark): def token_parsing(): s = bitstring.BitArray() for i in range(10000): s += 'uint:12=244, float:32=0.4' s += '0x3e44f, 0b11011, 0o75523' s += [0, 1, 2, 0, 0, 1, 2, 0, -1, 0, 'hello'] s += bitstring.BitArray(104) benchmark(token_parsing) def test_findall(benchmark): def finding(): random.seed(999) i = random.randrange(0, 2 ** 20000000) s = bitstring.BitArray(uint=i, length=20000000) for ss in ['0b11010010101', '0xabcdef1234, 0b000101111010101010011010100100101010101', '0x4321']: x = len(list(s.findall(ss))) return x c = benchmark(finding) assert c == 289 def test_repeated_reading(benchmark): def repeating_reading(): random.seed(1414) i = random.randrange(0, 2 ** 800000) s = bitstring.ConstBitStream(uint=i, length=800000) for _ in range(800000 // 40): _ = s.readlist('uint:4, float:32, bool, bool, bool, bool') benchmark(repeating_reading) def test_primes(benchmark): def primes(): limit = 1000000 is_prime = bitstring.BitArray(limit) is_prime.set(True) # Manually set 0 and 1 to be not prime. is_prime.set(False, [0, 1]) # For every other integer, if it's set as prime then unset all of its multiples for i in range(2, math.ceil(math.sqrt(limit))): if is_prime[i]: is_prime.set(False, range(i * i, limit, i)) twin_primes = len(list(is_prime.findall('0b101'))) return twin_primes c = benchmark(primes) assert c == 8169 bitstring-bitstring-4.2.3/tests/test_bitarray.py000066400000000000000000000654651462444752600221560ustar00rootroot00000000000000#!/usr/bin/env python """ Unit tests for the bitarray module. """ import pytest import sys import os import bitarray import bitstring from bitstring import BitArray, Bits sys.path.insert(0, '..') class TestAll: def test_creation_from_uint(self): s = BitArray(uint=15, length=6) assert s.bin == '001111' s = BitArray(uint=0, length=1) assert s.bin == '0' s.u = 1 assert s.uint == 1 s = BitArray(length=8) s.uint = 0 assert s.uint == 0 s.u8 = 255 assert s.uint == 255 assert s.len == 8 with pytest.raises(bitstring.CreationError): s.uint = 256 with pytest.raises(bitstring.CreationError): s.uint = -1 def test_creation_from_oct(self): s = BitArray(oct='7') assert s.oct == '7' assert s.bin == '111' s.append('0o1') assert s.bin == '111001' s.oct = '12345670' assert s.length == 24 assert s.bin == '001010011100101110111000' s = BitArray('0o123') assert s.oct == '123' class TestNoPosAttribute: def test_replace(self): s = BitArray('0b01') s.replace('0b1', '0b11') assert s == '0b011' def test_delete(self): s = BitArray('0b000000001') del s[-1:] assert s == '0b00000000' def test_insert(self): s = BitArray('0b00') s.insert('0xf', 1) assert s == '0b011110' def test_insert_self(self): b = BitArray('0b10') b.insert(b, 0) assert b == '0b1010' c = BitArray('0x00ff') c.insert(c, 8) assert c == '0x0000ffff' a = BitArray('0b11100') a.insert(a, 3) assert a == '0b1111110000' def test_no_bit_pos_for_insert(self): s = BitArray(100) with pytest.raises(TypeError): s.insert('0xabc') def test_insert_parameters(self): s = BitArray('0b111') with pytest.raises(TypeError): s.insert('0x4') def test_overwrite(self): s = BitArray('0b01110') s.overwrite('0b000', 1) assert s == '0b00000' def test_overwrite_no_pos(self): s = BitArray('0x01234') with pytest.raises(TypeError): s.overwrite('0xf') def test_overwrite_parameters(self): s = BitArray('0b0000') with pytest.raises(TypeError): s.overwrite('0b111') def test_prepend(self): s = BitArray('0b0') s.prepend([1]) assert s == [1, 0] def test_rol(self): s = BitArray('0b0001') s.rol(1) assert s == '0b0010' def test_ror(self): s = BitArray('0b1000') s.ror(1) assert s == '0b0100' def test_set_item(self): s = BitArray('0b000100') s[4:5] = '0xf' assert s == '0b000111110' s[0:1] = [1] assert s == '0b100111110' s[5:5] = BitArray() assert s == '0b100111110' class TestBugs: def test_adding_nonsense(self): a = BitArray([0]) with pytest.raises(ValueError): a += '3' with pytest.raises(ValueError): a += 'se' with pytest.raises(ValueError): a += 'float:32' def test_prepend_after_creation_from_data_with_offset(self): s1 = BitArray(bytes=b'\x00\x00\x07\xff\xf0\x00', offset=21, length=15) assert not s1.any(0) b = s1.tobytes() assert b == b'\xff\xfe' s1.prepend('0b0') assert s1.bin == '0111111111111111' s1.prepend('0b0') assert s1.bin == '00111111111111111' class TestByteAligned: def test_changing_it(self): bitstring.bytealigned = True assert bitstring.bytealigned bitstring.bytealigned = False assert not bitstring.bytealigned def test_not_byte_aligned(self): a = BitArray('0x00 ff 0f f') li = list(a.findall('0xff')) assert li == [8, 20] p = a.find('0x0f')[0] assert p == 4 p = a.rfind('0xff')[0] assert p == 20 s = list(a.split('0xff')) assert s == ['0x00', '0xff0', '0xff'] a.replace('0xff', '') assert a == '0x000' def test_byte_aligned(self): bitstring.bytealigned = True a = BitArray('0x00 ff 0f f') li = list(a.findall('0xff')) assert li == [8] p = a.find('0x0f')[0] assert p == 16 p = a.rfind('0xff')[0] assert p == 8 s = list(a.split('0xff')) assert s == ['0x00', '0xff0ff'] a.replace('0xff', '') assert a == '0x000ff' bitstring.bytealigned = False class TestSliceAssignment: def test_slice_assignment_single_bit(self): a = BitArray('0b000') a[2] = '0b1' assert a.bin == '001' a[0] = BitArray(bin='1') assert a.bin == '101' a[-1] = '0b0' assert a.bin == '100' a[-3] = '0b0' assert a.bin == '000' def test_slice_assignment_single_bit_errors(self): a = BitArray('0b000') with pytest.raises(IndexError): a[-4] = '0b1' with pytest.raises(IndexError): a[3] = '0b1' with pytest.raises(TypeError): a[1] = 1.3 def test_slice_assignment_muliple_bits(self): a = BitArray('0b0') a[0] = '0b110' assert a.bin3 == '110' a[0] = '0b000' assert a.bin5 == '00010' a[0:3] = '0b111' assert a.b5 == '11110' a[-2:] = '0b011' assert a.bin == '111011' a[:] = '0x12345' assert a.hex == '12345' a[:] = '' assert not a def test_slice_assignment_multiple_bits_errors(self): a = BitArray() with pytest.raises(IndexError): a[0] = '0b00' a += '0b1' a[0:2] = '0b11' assert a == '0b11' def test_del_slice_step(self): a = BitArray(bin='100111101001001110110100101') del a[::2] assert a.bin == '0110010101100' del a[3:9:3] assert a.bin == '01101101100' del a[2:7:1] assert a.bin == '011100' del a[::99] assert a.bin == '11100' del a[::1] assert a.bin == '' def test_del_slice_negative_step(self): a = BitArray('0b0001011101101100100110000001') del a[5:23:-3] assert a.bin == '0001011101101100100110000001' del a[25:3:-3] assert a.bin == '00011101010000100001' del a[:6:-7] assert a.bin == '000111010100010000' del a[15::-2] assert a.bin == '0010000000' del a[::-1] assert a.bin == '' def test_del_slice_negative_end(self): a = BitArray('0b01001000100001') del a[:-5] assert a == '0b00001' a = BitArray('0b01001000100001') del a[-11:-5] assert a == '0b01000001' def test_del_slice_errors(self): a = BitArray(10) del a[5:3] assert a == Bits(10) del a[3:5:-1] assert a == Bits(10) def test_del_single_element(self): a = BitArray('0b0010011') del a[-1] assert a.bin == '001001' del a[2] assert a.bin == '00001' with pytest.raises(IndexError): del a[5] def test_set_slice_step(self): a = BitArray(bin='0000000000') a[::2] = '0b11111' assert a.bin == '1010101010' a[4:9:3] = [0, 0] assert a.bin == '1010001010' a[7:3:-1] = [1, 1, 1, 0] assert a.bin == '1010011110' a[7:1:-2] = [0, 0, 1] assert a.b == '1011001010' a[::-5] = [1, 1] assert a.bin == '1011101011' a[::-1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] assert a.bin == '1000000000' def test_set_slice_step_with_int(self): a = BitArray(9) a[5:8] = -1 assert a.bin == '000001110' a[:] = 10 assert a.bin == '000001010' a[::-1] = 10 assert a.bin == '010100000' a[::2] = True assert a.bin == '111110101' def test_set_slice_errors(self): a = BitArray(8) with pytest.raises(ValueError): a[::3] = [1] class A(object): pass with pytest.raises(TypeError): a[1:2] = A() with pytest.raises(ValueError): a[1:4:-1] = [1, 2] class TestSubclassing: def test_is_instance(self): class SubBits(BitArray): pass a = SubBits() assert isinstance(a, SubBits) def test_class_type(self): class SubBits(BitArray): pass assert SubBits().__class__ == SubBits class TestClear: def test_clear(self): s = BitArray('0xfff') s.clear() assert s.len == 0 class TestCopy: def test_copy_method(self): s = BitArray(9) t = s.copy() assert s == t t[0] = True assert t.bin == '100000000' assert s.bin == '000000000' class TestModifiedByAddingBug: def test_adding(self): a = BitArray.fromstring('0b0') b = BitArray('0b11') c = a + b assert c == '0b011' assert a == '0b0' assert b == '0b11' class TestLsb0Setting: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_set_single_bit(self): a = BitArray(10) a[0] = True assert a == '0b0000000001' a[1] = True assert a == '0b0000000011' a[0] = False assert a == '0b0000000010' a[9] = True assert a == '0b1000000010' with pytest.raises(IndexError): a[10] = True def test_set_single_negative_bit(self): a = BitArray('0o000') a[-1] = True assert a == '0b100000000' a[-2] = True assert a == '0o600' a[-9] = True assert a == '0o601' with pytest.raises(IndexError): a[-10] = True def test_invert_bit(self): a = BitArray('0b11110000') a.invert() assert a == '0x0f' a.invert(0) assert a == '0b00001110' a.invert(-1) assert a == '0b10001110' def test_deleting_bits(self): a = BitArray('0b11110') del a[0] assert a == '0xf' def test_deleting_range(self): a = BitArray('0b101111000') del a[0:1] assert a == '0b10111100' del a[2:6] assert a == '0b1000' a = BitArray('0xabcdef') del a[:8] assert a == '0xabcd' del a[-4:] assert a == '0xbcd' del a[:-4] assert a == '0xb' def test_appending_bits(self): a = BitArray('0b111') a.append('0b000') assert a.bin == '000111' a += '0xabc' assert a == '0xabc, 0b000111' def test_setting_slice(self): a = BitArray('0x012345678') a[4:12] = '0xfe' assert a == '0x012345fe8' a[0:4] = '0xbeef' assert a == '0x012345febeef' def test_truncating_start(self): a = BitArray('0b1110000') a = a[4:] assert a == '0b111' def test_truncating_end(self): a = BitArray('0x123456') a = a[:16] assert a == '0x3456' def test_all(self): a = BitArray('0b0000101') assert a.all(1, [0, 2]) assert a.all(False, [-1, -2, -3, -4]) b = Bits(bytes=b'\x00\xff\xff', offset=7) assert b.all(1, [1, 2, 3, 4, 5, 6, 7]) assert b.all(1, [-2, -3, -4, -5, -6, -7, -8]) def test_any(self): a = BitArray('0b0001') assert a.any(1, [0, 1, 2]) def test_endswith(self): a = BitArray('0xdeadbeef') assert a.endswith('0xdead') def test_startswith(self): a = BitArray('0xdeadbeef') assert a.startswith('0xbeef') def test_cut(self): a = BitArray('0xff00ff1111ff2222') li = list(a.cut(16)) assert li == ['0x2222', '0x11ff', '0xff11', '0xff00'] def test_find(self): t = BitArray('0b10') p, = t.find('0b1') assert p == 1 t = BitArray('0b1010') p, = t.find('0b1') assert p == 1 a = BitArray('0b10101010, 0xabcd, 0b10101010, 0x0') p, = a.find('0b10101010', bytealigned=False) assert p == 4 p, = a.find('0b10101010', start=4, bytealigned=False) assert p == 4 p, = a.find('0b10101010', start=5, bytealigned=False) assert p == 22 def test_find_failing(self): a = BitArray() p = a.find('0b1') assert p == () a = BitArray('0b11111111111011') p = a.find('0b100') assert not p def test_find_failing2(self): s = BitArray('0b101') p, = s.find('0b1', start=2) assert p == 2 def test_rfind(self): a = BitArray('0b1000000') p = a.rfind('0b1') assert p == (6,) p = a.rfind('0b000') assert p == (3,) def test_rfind_with_start_and_end(self): a = BitArray('0b11 0000 11 00') p = a.rfind('0b11', start=8) assert p[0] == 8 p = a.rfind('0b110', start=8) assert p == () p = a.rfind('0b11', end=-1) assert p[0] == 2 def test_findall(self): a = BitArray('0b001000100001') b = list(a.findall('0b1')) assert b == [0, 5, 9] c = list(a.findall('0b0001')) assert c == [0, 5] d = list(a.findall('0b10')) assert d == [4, 8] e = list(a.findall('0x198273641234')) assert e == [] def test_find_all_with_start_and_end(self): a = BitArray('0xaabbccaabbccccbb') b = list(a.findall('0xbb', start=0, end=8)) assert b == [0] b = list(a.findall('0xbb', start=1, end=8)) assert b == [] b = list(a.findall('0xbb', start=0, end=7)) assert b == [] b = list(a.findall('0xbb', start=48)) assert b == [48] b = list(a.findall('0xbb', start=47)) assert b == [48] b = list(a.findall('0xbb', start=49)) assert b == [] def test_find_all_byte_aligned(self): a = BitArray('0x0550550') b = list(a.findall('0x55', bytealigned=True)) assert b == [16] def test_find_all_with_count(self): a = BitArray('0b0001111101') b = list(a.findall([1], start=1, count=1)) assert b == [2] def test_split(self): a = BitArray('0x4700004711472222') li = list(a.split('0x47', bytealigned=True)) assert li == ['', '0x472222', '0x4711', '0x470000'] def test_byte_swap(self): a = BitArray('0xaa00ff00ff00') n = a.byteswap(2, end=32, repeat=True) assert n == 2 assert a == '0xaa0000ff00ff' def test_insert(self): a = BitArray('0x0123456') a.insert('0xf', 4) assert a == '0x012345f6' def test_overwrite(self): a = BitArray('0x00000000') a.overwrite('0xdead', 4) assert a == '0x000dead0' def test_replace(self): a = BitArray('0x5551100') n = a.replace('0x1', '0xabc') assert n == 2 assert a == '0x555abcabc00' n = a.replace([1], [0], end=12) assert n == 2 assert a == '0x555abcab000' def test_reverse(self): a = BitArray('0x0011223344') a.reverse() assert a == '0x22cc448800' a.reverse(0, 16) assert a == '0x22cc440011' def test_ror(self): a = BitArray('0b111000') a.ror(1) assert a == '0b011100' a = BitArray('0b111000') a.ror(1, start=2, end=6) assert a == '0b011100' def test_rol(self): a = BitArray('0b1') a.rol(12) assert a == '0b1' b = BitArray('0b000010') b.rol(3) assert b == '0b010000' def test_set(self): a = BitArray(100) a.set(1, [0, 2, 4]) assert a[0] assert a.startswith('0b000010101') a = BitArray('0b111') a.set(False, 0) assert a == '0b110' def test_failing_repr(self): a = BitArray('0b010') a.find('0b1') assert repr(a) == "BitArray('0b010')" def test_left_shift(self): a = BitArray('0b11001') assert (a << 1).b == '10010' assert (a << 5).b == '00000' assert (a << 0).b == '11001' def test_right_shift(self): a = BitArray('0b11001') assert (a >> 1).b == '01100' assert (a >> 5).b == '00000' assert (a >> 0).b == '11001' # def testConstFileBased(self): # filename = os.path.join(THIS_DIR, 'test.m1v') # a = Bits(filename=filename, offset=8) # self.assertTrue(a[-8]) # self.assertTrue(a.endswith('0x01b3')) class TestRepr: def test_standard_repr(self): a = BitArray('0o12345') assert repr(a) == "BitArray('0b001010011100101')" class TestNewProperties: def test_aliases(self): a = BitArray('0x1234567890ab') assert a.oct == a.o assert a.hex == a.h assert a.bin == a.b assert a[:32].float == a[:32].f assert a.int == a.i assert a.uint == a.u def test_aliases_with_lengths(self): a = BitArray('0x123') h = a.h12 assert h == '123' b = a.b12 assert b == '000100100011' o = a.o12 assert o == '0443' u = a.u12 assert u == a.u i = a.i12 assert i == a.i x = BitArray('0x12345678') f = x.f32 assert f == x.f def test_assignments(self): a = BitArray() a.f64 = 0.5 assert a.f64 == 0.5 a.u88 = 1244322 assert a.u88 == 1244322 a.i3 = -3 assert a.i3 == -3 a.h16 = '0x1234' assert a.h16 == '1234' a.o9 = '0o765' assert a.o9 == '765' a.b7 = '0b0001110' assert a.b7 == '0001110' def test_assignments_without_length(self): a = BitArray(64) a.f = 1234.5 assert a.float == 1234.5 assert a.len == 64 a.u = 99 assert a.uint == 99 assert a.len == 64 a.i = -999 assert a.int == -999 assert a.len == 64 a.h = 'feedbeef' assert a.hex == 'feedbeef' a.o = '1234567' assert a.oct == '1234567' a.b = '001' assert a.bin == '001' def test_getter_length_errors(self): a = BitArray('0x123') with pytest.raises(bitstring.InterpretError): _ = a.h16 with pytest.raises(bitstring.InterpretError): _ = a.b3317777766 with pytest.raises(AttributeError): _ = a.o2 with pytest.raises(bitstring.InterpretError): _ = a.f with pytest.raises(bitstring.InterpretError): _ = a.f32 with pytest.raises(bitstring.InterpretError): _ = a.u13 with pytest.raises(bitstring.InterpretError): _ = a.i1 b = BitArray() with pytest.raises(bitstring.InterpretError): _ = b.u0 def test_setter_length_errors(self): a = BitArray() a.u8 = 255 assert a.len == 8 with pytest.raises(ValueError): a.u8 = 256 a.f32 = 10 a.f64 = 10 with pytest.raises(ValueError): a.f256 = 10 with pytest.raises(bitstring.CreationError): a.u0 = 2 with pytest.raises(bitstring.CreationError): a.hex4 = '0xab' assert len(a) == 64 with pytest.raises(bitstring.CreationError): a.o3 = '0xab' with pytest.raises(bitstring.CreationError): a.b4 = '0xab' a.h0 = '' assert a.len == 0 a.i8 = 127 a.i8 = -128 with pytest.raises(ValueError): a.i8 = 128 with pytest.raises(ValueError): a.i8 = -129 with pytest.raises(bitstring.CreationError): a.froggy16 = '0xabc' def test_unpack(self): a = BitArray('0xff160120') b = a.unpack('h8,2*u12') assert b == ['ff', 352, 288] def test_reading(self): a = bitstring.BitStream.fromstring('0x01ff') b = a.read('u8') assert b == 1 assert a.pos == 8 assert a.read('i') == -1 def test_longer_more_general_names(self): a = BitArray() a.f64 = 0.0 assert a.float64 == 0.0 a.float32 = 10.5 assert a.f32 == 10.5 def test_bytes_properties(self): a = BitArray() a.bytes = b'hello' assert a.bytes5 == b'hello' a.bytes3 = b'123' assert a.bytes == b'123' with pytest.raises(bitstring.CreationError): a.bytes5 = b'123456789' with pytest.raises(bitstring.CreationError): a.bytes5 = b'123' def test_conversion_to_bytes(self): a = BitArray(bytes=b'1234') b = bytes(a) assert b == b'1234' a += [1] assert bytes(a) == b'1234\x80' a = BitArray() assert bytes(a) == b'' class TestBFloats: def test_creation(self): a = BitArray('bfloat=100.5') assert a.unpack('bfloat')[0] == 100.5 b = BitArray(bfloat=20.25) assert b.bfloat == 20.25 b.bfloat = -30.5 assert b.bfloat == -30.5 assert len(b) == 16 fs = [0.0, -6.1, 1.52e35, 0.000001] a = bitstring.pack('4*bfloat', *fs) fsp = a.unpack('4*bfloat') assert len(a) == len(fs)*16 for f, fp in zip(fs, fsp): assert f == pytest.approx(fp, abs=abs(f/100)) a = BitArray(bfloat=13) assert a.bfloat == 13 c = BitArray() with pytest.raises(ValueError): _ = c.bfloat def test_creation_errors(self): a = BitArray(bfloat=-0.25, length=16) assert len(a) == 16 with pytest.raises(bitstring.CreationError): _ = BitArray(bfloat=10, length=15) with pytest.raises(bitstring.CreationError): _ = BitArray('bfloat:1=0.5') def test_little_endian(self): a = BitArray.fromstring('f32=1000') b = BitArray(bfloat=a.f) assert a[0:16] == b[0:16] a = BitArray('floatle:32=1000') b = BitArray(bfloatle=1000) assert a[16:32] == b assert b.bfloatle == 1000.0 b.byteswap() assert b.bfloat == 1000.0 assert b.bfloatbe == 1000.0 with pytest.raises(bitstring.CreationError): _ = BitArray(bfloatle=-5, length=15) c = BitArray() with pytest.raises(bitstring.InterpretError): _ = c.bfloatle with pytest.raises(bitstring.InterpretError): _ = c.bfloatne def test_more_creation(self): a = BitArray('bfloat:16=1.0, bfloat16=2.0, bfloat=3.0') x, y, z = a.unpack('3*bfloat16') assert (x, y, z) == (1.0, 2.0, 3.0) def test_interpret_bug(self): a = BitArray(100) with pytest.raises(bitstring.InterpretError): v = a.bfloat def test_overflows(self): s = BitArray() inf16 = BitArray(float=float('inf'), length=16) inf32 = BitArray(float=float('inf'), length=32) inf64 = BitArray(float=float('inf'), length=64) infbfloat = BitArray(bfloat=float('inf')) s.f64 = 1e400 assert s == inf64 s.f32 = 1e60 assert s == inf32 s.f16 = 100000 assert s == inf16 s.bfloat = 1e60 assert s == infbfloat ninf16 = BitArray(float=float('-inf'), length=16) ninf32 = BitArray(float=float('-inf'), length=32) ninf64 = BitArray(float=float('-inf'), length=64) ninfbfloat = BitArray(bfloat=float('-inf')) s.f64 = -1e400 assert s == ninf64 s.f32 = -1e60 assert s == ninf32 s.f16 = -100000 assert s == ninf16 s.bfloat = -1e60 assert s == ninfbfloat def test_big_endian_string_initialisers(self): a = BitArray('bfloatbe=4.5') b = BitArray('bfloatbe:16=-2.25') assert a.bfloatbe == 4.5 assert b.bfloatbe == -2.25 def test_litte_endian_string_initialisers(self): a = BitArray('bfloatle=4.5') b = BitArray('bfloatle:16=-2.25') assert a.bfloatle == 4.5 assert b.bfloatle == -2.25 def test_native_endian_string_initialisers(self): a = BitArray('bfloatne=4.5') b = BitArray('bfloatne:16=-2.25') assert a.bfloatne == 4.5 assert b.bfloatne == -2.25 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') 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') 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')) 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 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 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 assert a[17:24].tobitarray() == b.tobitarray() try: import numpy as np numpy_installed = True except ImportError: numpy_installed = False class TestNumpy: @pytest.mark.skipif(not numpy_installed, reason="numpy not installed.") def test_getting(self): a = BitArray('0b110') p = np.int_(1) assert a[p] is True p = np.short(0) assert a[p] is True @pytest.mark.skipif(not numpy_installed, reason="numpy not installed.") def test_setting(self): a = BitArray('0b110') p = np.int_(1) a[p] = '0b1111' assert a == '0b111110' @pytest.mark.skipif(not numpy_installed, reason="numpy not installed.") def test_creation(self): a = BitArray(np.longlong(12)) assert a.hex == '000' def test_bytes_from_list(): s = Bits(bytes=[1, 2]) assert s == '0x0102' s = Bits(bytes=bytearray([1, 2])) assert s == '0x0102' s = BitArray(bytes=[1, 2]) assert s == '0x0102' s = BitArray(bytes=bytearray([1, 2])) assert s == '0x0102' s.bytes = [10, 20] assert s == '0x0a14' bitstring-bitstring-4.2.3/tests/test_bits.py000066400000000000000000000711031462444752600212640ustar00rootroot00000000000000#!/usr/bin/env python 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 import hypothesis.strategies as st 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) 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 == '' @given(st.binary()) def test_creation_from_bytes_roundtrip(self, data): s = Bits(bytes=data) assert s.bytes == data def test_creation_from_bytes_errors(self): with pytest.raises(bitstring.CreationError): 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') 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, '') def test_creation_from_hex_with_whitespace(self): s = Bits(hex=' \n0 X a 4e \r3 \n') assert s.hex == 'a4e3' @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) with pytest.raises(bitstring.CreationError): 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' def test_creation_from_bin_with_whitespace(self): 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') with pytest.raises(bitstring.InterpretError): _ = s.oct with pytest.raises(bitstring.CreationError): _ = Bits('oct=8') def test_creation_from_uint_with_offset(self): with pytest.raises(bitstring.CreationError): Bits(uint=12, length=8, offset=1) def test_creation_from_uint_errors(self): with pytest.raises(bitstring.CreationError): Bits(uint=-1, length=10) with pytest.raises(bitstring.CreationError): Bits(uint=12) with pytest.raises(bitstring.CreationError): Bits(uint=4, length=2) with pytest.raises(bitstring.CreationError): Bits(uint=0, length=0) with pytest.raises(bitstring.CreationError): Bits(uint=12, length=-12) def test_creation_from_int(self): s = Bits(int=0, length=4) assert s.bin == '0000' s = Bits(int=1, length=2) assert s.bin == '01' s = Bits(int=-1, length=11) assert s.bin == '11111111111' s = Bits(int=12, length=7) assert s.int == 12 s = Bits(int=-243, length=108) assert (s.int, s.length) == (-243, 108) for length in range(6, 10): for value in range(-17, 17): s = Bits(int=value, length=length) assert (s.int, s.length) == (value, length) _ = Bits(int=10, length=8) @pytest.mark.parametrize("int_, length", [[-1, 0], [12, None], [4, 3], [-5, 3]]) def test_creation_from_int_errors(self, int_, length): with pytest.raises(bitstring.CreationError): _ = Bits(int=int_, length=length) def test_creation_from_se(self): for i in range(-100, 10): s = Bits(se=i) assert s.se == i with pytest.raises(bitstring.CreationError): _ = Bits(se=10, length=40) def test_creation_from_se_with_offset(self): with pytest.raises(bitstring.CreationError): Bits(se=-13, offset=1) 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') with pytest.raises(bitstring.InterpretError): _ = s.se def test_creation_from_ue(self): for i in range(0, 20): assert Bits(ue=i).ue == i def test_creation_from_ue_with_offset(self): with pytest.raises(bitstring.CreationError): Bits(ue=104, offset=2) def test_creation_from_ue_errors(self): with pytest.raises(bitstring.CreationError): Bits(ue=-1) with pytest.raises(bitstring.CreationError): Bits(ue=1, length=12) 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') 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' def test_creation_from_bool_errors(self): with pytest.raises(ValueError): _ = Bits('bool=3') with pytest.raises(bitstring.CreationError): _ = Bits(bool=0, length=2) def test_creation_keyword_error(self): with pytest.raises(bitstring.CreationError): Bits(squirrel=5) 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' 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] class TestInitialisation: def test_empty_init(self): a = Bits() assert a == '' def test_no_pos(self): a = Bits('0xabcdef') with pytest.raises(AttributeError): _ = a.pos def test_find(self): a = Bits('0xabcd') r = a.find('0xbc') assert r[0] == 4 r = a.find('0x23462346246', bytealigned=True) assert not r def test_rfind(self): a = Bits('0b11101010010010') b = a.rfind('0b010') assert b[0] == 11 def test_find_all(self): a = Bits('0b0010011') b = list(a.findall([1])) assert b == [2, 5, 6] t = BitArray('0b10') tp = list(t.findall('0b1')) assert tp == [0] class TestCut: def test_cut(self): s = Bits('0b000111'*10) for t in s.cut(6): assert t.bin == '000111' class TestInterleavedExpGolomb: def test_creation(self): s1 = Bits(uie=0) s2 = Bits(uie=1) assert s1 == [1] assert s2 == [0, 0, 1] s1 = Bits(sie=0) s2 = Bits(sie=-1) s3 = Bits(sie=1) assert s1 == [1] assert s2 == [0, 0, 1, 1] assert s3 == [0, 0, 1, 0] def test_creation_from_property(self): s = BitArray() s.uie = 45 assert s.uie == 45 s.sie = -45 assert s.sie == -45 def test_interpretation(self): for x in range(101): assert Bits(uie=x).uie == x for x in range(-100, 100): assert Bits(sie=x).sie == x def test_errors(self): for f in ['sie=100, 0b1001', '0b00', 'uie=100, 0b1001']: s = Bits.fromstring(f) with pytest.raises(bitstring.InterpretError): _ = s.sie with pytest.raises(bitstring.InterpretError): _ = s.uie with pytest.raises(ValueError): Bits(uie=-10) class TestFileBased: def setup_method(self): 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' def test_bit_operators(self): x = self.b[4:20] 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:] y = x & self.b[4:20] assert y == self.c assert repr(y) == repr(self.c) def test_addition(self): _ = self.d + '0x1' x = self.a[20:24] + self.c[-4:] + self.c[8:12] assert x == '0x587' x = self.b + x assert x.h == '456789abcdef587' x = BitArray(x) del x[12:24] assert x == '0x456abcdef587' class TestComparisons: def test_unorderable(self): a = Bits(5) b = Bits(5) with pytest.raises(TypeError): _ = a < b with pytest.raises(TypeError): _ = a > b with pytest.raises(TypeError): _ = a <= b with pytest.raises(TypeError): _ = a >= b 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) assert b is True class TestPadToken: def test_creation(self): a = Bits.fromstring('pad:10') assert a == Bits(10) b = Bits('pad:0') assert b == Bits() 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) assert d == Bits(12) 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') assert x == [] def test_unpack_bug(self): 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') c = a + b assert c == '0b011' assert a == '0b0' assert b == '0b11' def test_adding2(self): a = Bits(100) b = Bits(101) c = a + b assert a == Bits(100) assert b == Bits(101) assert c == Bits(201) class TestWrongTypeBug: def test_append_to_bits(self): a = Bits(BitArray()) with pytest.raises(AttributeError): 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'])) 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') b = Bits(a) assert b.length == 8 assert b.hex == 'ff' def test_signed_short(self): a = array.array('h') a.append(10) a.append(-1) b = Bits(a) assert b.length == 32 assert b.bytes == a.tobytes() def test_double(self): 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') 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]) == [] def test_iterate_non_empty_bits(self): assert list(Bits([1, 0])) == [True, False] 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 class TestContainsBug: def test_contains(self): a = Bits('0b1, 0x0001dead0001') assert '0xdead' in a assert not '0xfeed' in a assert '0b1' in Bits('0xf') assert not '0b0' in Bits('0xf') class TestByteStoreImmutablity: def test_immutability_bug_append(self): a = Bits('0b111') b = a + '0b000' c = BitArray(b) c[1] = 0 assert c.bin == '101000' assert a.b3 == '111' assert b.bin == '111000' def test_immutability_bug_prepend(self): a = Bits('0b111') b = '0b000' + a c = BitArray(b) c[1] = 1 assert b.bin == '000111' assert c.bin == '010111' class TestLsb0Indexing: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_get_single_bit(self): a = Bits('0b000001111') assert a[0] is True assert a[3] is True assert a[4] is False assert a[8] is False with pytest.raises(IndexError): _ = a[9] assert a[-1] is False assert a[-5] is False assert a[-6] is True assert a[-9] is True with pytest.raises(IndexError): _ = 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' def test_extended_slicing(self): a = Bits('0b100000100100100') assert a[2::3] == '0b10111' def test_all(self): a = Bits('0b000111') assert a.all(1, [0, 1, 2]) assert a.all(0, [3, 4, 5]) def test_any(self): 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) def test_ends_with(self): a = Bits('0x1234abcd') assert a.endswith('0x123') assert not a.endswith('0xabcd') class TestLsb0Interpretations: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_uint(self): a = Bits('0x01') assert a == '0b00000001' assert a.uint == 1 assert a[0] is True def test_float(self): a = Bits(float=0.25, length=32) try: bitstring.lsb0 = False b = Bits(float=0.25, length=32) finally: bitstring.lsb0 = True assert a.float == 0.25 assert b.float == 0.25 assert a.bin == b.bin def test_golomb(self): with pytest.raises(bitstring.CreationError): _ = Bits(ue=2) with pytest.raises(bitstring.CreationError): _ = Bits(se=2) with pytest.raises(bitstring.CreationError): _ = Bits(uie=2) with pytest.raises(bitstring.CreationError): _ = Bits(sie=2) def test_bytes(self): a = Bits.fromstring('0xabcdef') b = a.bytes assert b == b'\xab\xcd\xef' b = a.bytes3 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') 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' 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') assert a.uint == 0o001122334455_6677 b = Bits('0o123_321_123_321') assert b.uint == 0o123_321_123321 class TestPrettyPrinting: def test_simplest_cases(self): a = Bits('0b101011110000') s = io.StringIO() a.pp(stream=s) assert remove_unprintable(s.getvalue()) == """ [ 0: 10101111 0000 ] """ s = io.StringIO() 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()) == """ [ 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()) == """ [ 0: 00000000 8: 00000000 16: 0000 ] """ def test_separator(self): a = Bits('0x0f0f')*9 s = io.StringIO() 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()) == """ [ 0: 000000000000000000000000000000000000000000000000000000000000000000000000 72: 0000000000000000000000000000 ] """ def test_multiformat(self): a = Bits('0b1111000011110000') s = io.StringIO() 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()) == """ [ 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()) == """ [ 0: 11111111 11111111 11111111 : ff ff ff 24: 11111111 11111111 11111111 : ff ff ff 48: 11111111 11111111 11111111 : ff ff ff 72: 11111111 11111111 11111111 : ff ff ff 96: 11111111 11111111 : ff ff ] """ s = io.StringIO() 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 48: 11111111 11111111 : ff ff 64: 11111111 11111111 : ff ff 80: 11111111 11111111 : ff ff 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""" [ 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') with pytest.raises(ValueError): 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) expected_output = """ [ 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ] """ assert remove_unprintable(s.getvalue()) == expected_output a = Bits(400) s = io.StringIO() a.pp(stream=s, fmt='hex:0', show_offset=False, width=80) expected_output = """ [ 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000 ] """ assert remove_unprintable(s.getvalue()) == expected_output s = io.StringIO() a = Bits(uint=10, length=48) a.pp(stream=s, width=20, fmt='hex:0, oct:0', show_offset=False) expected_output = """ [ 000000 : 00000000 00000a : 00000012 ] """ assert remove_unprintable(s.getvalue()) == expected_output def test_oct(self): a = Bits('0o01234567'*20) s = io.StringIO() a.pp(stream=s, fmt='o', show_offset=False, width=20) expected_output = """ [ 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 0123 4567 ] """ assert remove_unprintable(s.getvalue()) == expected_output t = io.StringIO() a.pp('h, oct:0', width=1, show_offset=False, stream=t) expected_output = """ [ 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 053977 : 01234567 ] """ assert remove_unprintable(t.getvalue()) == expected_output def test_bytes(self): a = Bits(bytes=b'helloworld!!'*5) s = io.StringIO() 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 = ( """ [ helloworld!!helloworld!!helloworld!!hell oworld!!helloworld!! ] """ ) assert remove_unprintable(s.getvalue()) == expected_output def test_bool(self): a = Bits('0b1100') s = io.StringIO() a.pp(stream=s, fmt='bool', show_offset=False, width=20) expected_output = """ [ 1 1 0 0 ] """ assert remove_unprintable(s.getvalue()) == expected_output class TestPrettyPrintingErrors: def test_wrong_formats(self): a = Bits('0x12341234') with pytest.raises(ValueError): a.pp('binary') with pytest.raises(ValueError): a.pp('bin, bin, bin') def test_interpret_problems(self): a = Bits(7) with pytest.raises(InterpretError): a.pp('oct') with pytest.raises(InterpretError): a.pp('hex') with pytest.raises(InterpretError): a.pp('bin, bytes') class TestPrettyPrinting_LSB0: def setup_method(self) -> None: bitstring.lsb0 = True def teardown_method(self) -> None: bitstring.lsb0 = False def test_bin(self): a = Bits(bin='1111 0000 0000 1111 1010') s = io.StringIO() a.pp('bin', stream=s, width=5) assert remove_unprintable(s.getvalue()) == """ [ 11111010 :0 00000000 :8 1111 :16 ] """ class TestPrettyPrinting_NewFormats: def test_float(self): a = Bits('float32=10.5') s = io.StringIO() 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()) == """ [ 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()) == """ [ 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 432: 76 77 78 79 80 81 82 83 84 85 86 87 : 04c 04d 04e 04f 050 051 052 053 054 055 056 057 576: 88 89 90 91 92 93 94 95 96 97 98 99 : 058 059 05a 05b 05c 05d 05e 05f 060 061 062 063 720: 100 101 102 103 104 : 064 065 066 067 068 ] """ def test_float(self): a = BitArray(float=76.25, length=64) + '0b11111' s = io.StringIO() a.pp('i64, float', stream=s) assert remove_unprintable(s.getvalue()) == """ [ 0: 4635066033680416768 : 76.25 ] + trailing_bits = 0b11111 """ class TestCopy: def test_copy_method(self): 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') assert s == t def test_intne(self): s = Bits(intne=-1000, length=64) t = Bits('intne:64=-1000') assert s == t class TestNonNativeEndianIntegers: def setup_method(self) -> None: 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') assert s == t def test_intne(self): s = Bits(intne=-1000, length=64) t = Bits('intne:64=-1000') assert s == t bitstring-bitstring-4.2.3/tests/test_bitstore.py000066400000000000000000000055001462444752600221540ustar00rootroot00000000000000#!/usr/bin/env python import pytest import sys sys.path.insert(0, '..') import bitstring from bitstring.bitstore import BitStore, offset_slice_indices_lsb0 import sys sys.path.insert(0, '..') class TestBasicFunctionality: def test_getting_int(self): a = BitStore('001') assert a.getindex(0) == 0 assert a.getindex(1) == 0 assert a.getindex(2) == 1 assert a.getindex(-1) == 1 assert a.getindex(-2) == 0 assert a.getindex(-3) == 0 with pytest.raises(IndexError): _ = a.getindex(3) with pytest.raises(IndexError): _ = a.getindex(-4) class TestBasicLSB0Functionality: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_getting_int(self): a = BitStore('001') assert a.getindex(0) == 1 assert a.getindex(1) == 0 assert a.getindex(2) == 0 assert a.getindex(-1) == 0 assert a.getindex(-2) == 0 assert a.getindex(-3) == 1 with pytest.raises(IndexError): _ = a.getindex(3) with pytest.raises(IndexError): _ = a.getindex(-4) def test_getting_slice(self): a = BitStore.frombytes(b'12345678') assert a.getslice(None, None).tobytes() == b'12345678' assert a.getslice(None, -8).tobytes() == b'2345678' assert a.getslice(8, None).tobytes() == b'1234567' assert a.getslice(16, 24).tobytes() == b'6' def test_setting_int(self): a = BitStore('00000') a[0] = 1 assert a.slice_to_bin() == '00001' a[-1] = 1 assert a.slice_to_bin() == '10001' with pytest.raises(IndexError): a[5] = 1 with pytest.raises(IndexError): a[-6] = 0 class TestGettingSlices: def teardown_method(self) -> None: bitstring.lsb0 = False def test_everything(self): a = BitStore('010010001000110111001111101101001111') # Try combination of start and stop for msb0 and get the result. # Convert to start and stop needed for lsb0 options = [5, 2, -2, 1, 7, -3, -9, 0, -1, -len(a), len(a), len(a) - 1, -len(a) - 1, -100, 100, None] for start_option in options: for end_option in options: bitstring.lsb0 = True lsb0 = a.getslice(start_option, end_option) bitstring.lsb0 = False msb0 = a.getslice(start_option, end_option) new_slice = offset_slice_indices_lsb0(slice(start_option, end_option, None), len(a)) new_start, new_end = new_slice.start, new_slice.stop assert len(msb0) == len(lsb0), f"[{start_option}: {end_option}] -> [{new_start}: {new_end}] len(msb0)={len(msb0)}, len(lsb0)={len(lsb0)}" bitstring-bitstring-4.2.3/tests/test_bitstream.py000066400000000000000000004030011462444752600223110ustar00rootroot00000000000000#!/usr/bin/env python import pytest import sys sys.path.insert(0, '..') import bitstring import copy import os import collections from bitstring import Bits, BitStream, ConstBitStream, pack, Dtype THIS_DIR = os.path.dirname(os.path.abspath(__file__)) class TestFlexibleInitialisation: def test_flexible_initialisation(self): a = BitStream('uint:8=12') c = BitStream(' uint : 8 = 12') assert a == c == BitStream(uint=12, length=8) assert a.uint == 12 a = BitStream(' int:2= -1') b = BitStream('int :2 = -1') c = BitStream(' int: 2 =-1 ') assert a == b == c == BitStream(int=-1, length=2) def test_flexible_initialisation2(self): h = BitStream('hex=12') o = BitStream('oct=33') b = BitStream('bin=10') assert h == '0x12' assert o == '0o33' assert b == '0b10' def test_flexible_initialisation3(self): for s in ['se=-1', ' se = -1 ', 'se = -1']: a = BitStream(s) assert a.se == -1 for s in ['ue=23', 'ue =23', 'ue = 23']: a = BitStream(s) assert a.ue == 23 def test_multiple_string_initialisation(self): a = BitStream('0b1 , 0x1') assert a == '0b10001' a = BitStream('ue=5, ue=1, se=-2') assert a.read('ue') == 5 assert a.read('ue') == 1 assert a.read('se') == -2 b = BitStream('uint:32 = 12, 0b11') + 'int:100=-100, 0o44' assert b.read(32).uint == 12 assert b.read(2).bin == '11' assert b.read(100).int == -100 class TestReading: def test_read_bits(self): s = BitStream(bytes=b'\x4d\x55') assert s.read(4).hex == '4' assert s.read(8).hex == 'd5' assert s.read(1) == [0] assert s.read(3).bin == '101' assert not s.read(0) def test_read_byte(self): s = BitStream(hex='4d55') assert s.read(8).hex == '4d' assert s.read(8).hex == '55' def test_read_bytes(self): s = BitStream(hex='0x112233448811') assert s.read(3 * 8).hex == '112233' with pytest.raises(ValueError): s.read(-16) s.bitpos += 1 assert s.read(2 * 8).bin == '1000100100010000' def test_read_ue(self): with pytest.raises(bitstring.InterpretError): _ = BitStream('').ue # The numbers 0 to 8 as unsigned Exponential-Golomb codes s = BitStream(bin='1 010 011 00100 00101 00110 00111 0001000 0001001') assert s.pos == 0 for i in range(9): assert s.read('ue') == i with pytest.raises(bitstring.ReadError): s.read('ue') def test_read_se(self): s = BitStream(bin='010 00110 0001010 0001000 00111') assert s.read('se') == 1 assert s.read('se') == 3 assert s.readlist(3 * ['se']) == [5, 4, -3] class TestFind: def test_find1(self): s = ConstBitStream(bin='0b0000110110000') assert s.find(BitStream(bin='11011')) assert s.bitpos == 4 assert s.read(5).bin == '11011' s.bitpos = 0 assert not s.find('0b11001', False) def test_find2(self): s = BitStream(bin='0') assert s.find(s, False) assert s.pos == 0 assert not s.find('0b00', False) with pytest.raises(ValueError): s.find(BitStream()) def test_find_with_offset(self): s = BitStream(hex='0x112233')[4:] assert s.find('0x23', False) assert s.pos == 8 def test_find_corner_cases(self): s = BitStream(bin='000111000111') assert s.find('0b000') assert s.pos == 0 assert s.find('0b000') assert s.pos == 0 assert s.find('0b0111000111') assert s.pos == 2 assert s.find('0b000', start=2) assert s.pos == 6 assert s.find('0b111', start=6) assert s.pos == 9 s.pos += 2 assert s.find('0b1', start=s.pos) def test_find_bytes(self): s = BitStream.fromstring('0x010203040102ff') assert s.find('0x05', bytealigned=True) ==() assert s.find('0x02', bytealigned=True) == (8,) assert s.read(16).hex == '0203' assert s.find('0x02', start=s.bitpos, bytealigned=True) == (40,) s.read(1) assert not s.find('0x02', start=s.bitpos, bytealigned=True) def test_find_bytes_aligned_corner_cases(self): s = BitStream('0xff') assert s.find(s) assert not s.find(BitStream(hex='0x12')) assert not s.find(BitStream(hex='0xffff')) def test_find_bytes_bitpos(self): s = BitStream(hex='0x1122334455') s.pos = 2 s.find('0x66', bytealigned=True) assert s.pos == 2 s.pos = 38 s.find('0x66', bytealigned=True) assert s.pos == 38 def test_find_byte_aligned(self): s = BitStream(hex='0x12345678') assert s.find(BitStream(hex='0x56'), bytealigned=True) assert s.bytepos == 2 s.pos = 0 assert not s.find(BitStream(hex='0x45'), bytealigned=True) s = BitStream('0x1234') s.find('0x1234') assert s.find('0x1234') s += '0b111' s.pos = 3 s.find('0b1', start=17, bytealigned=True) assert not s.find('0b1', start=17, bytealigned=True) assert s.pos == 3 def test_find_byte_aligned_with_offset(self): s = BitStream(hex='0x112233')[4:] assert s.find(BitStream(hex='0x23')) def test_find_byte_aligned_errors(self): s = BitStream(hex='0xffff') with pytest.raises(ValueError): s.find('') with pytest.raises(ValueError): s.find(BitStream()) class TestRfind: def test_rfind(self): a = BitStream('0b001001001') b = a.rfind('0b001') assert b == (6,) assert a.pos == 6 big = BitStream(length=100000) + '0x12' + BitStream(length=10000) found = big.rfind('0x12', bytealigned=True) assert found == (100000,) assert big.pos == 100000 def test_rfind_byte_aligned(self): a = BitStream('0x8888') b = a.rfind('0b1', bytealigned=True) assert b == (8,) assert a.pos == 8 def test_rfind_startbit(self): a = BitStream('0x0000ffffff') b = a.rfind('0x0000', start=1, bytealigned=True) assert b == () assert a.pos == 0 b = a.rfind('0x00', start=1, bytealigned=True) assert b == (8,) assert a.pos == 8 def test_rfind_endbit(self): a = BitStream('0x000fff') b = a.rfind('0b011', start=0, end=14, bytealigned=False) assert bool(b) == True b = a.rfind('0b011', 0, 13, False) assert b == () def test_rfind_errors(self): a = BitStream('0x43234234') with pytest.raises(ValueError): a.rfind('', bytealigned=True) with pytest.raises(ValueError): a.rfind('0b1', start=-99, bytealigned=True) with pytest.raises(ValueError): a.rfind('0b1', end=33, bytealigned=True) with pytest.raises(ValueError): a.rfind('0b1', start=10, end=9, bytealigned=True) class TestShift: def test_shift_left(self): s = BitStream.fromstring('0b1010') t = s << 1 assert s.bin == '1010' assert t.bin == '0100' s = t << 0 assert s == '0b0100' t = s << 100 assert t.bin == '0000' def test_shift_left_errors(self): s = BitStream() with pytest.raises(ValueError): s << 1 s = BitStream('0xf') with pytest.raises(ValueError): s << -1 def test_shift_right(self): s = BitStream('0b1010') t = s >> 1 assert s.bin == '1010' assert t.bin == '0101' q = s >> 0 assert q == '0b1010' q.replace('0b1010', '') t = s >> 100 assert t.bin == '0000' def test_shift_right_errors(self): s = BitStream() with pytest.raises(ValueError): s >> 1 s = BitStream('0xf') with pytest.raises(ValueError): s >> -1 def test_shift_right_in_place(self): s = BitStream.fromstring('0xffff')[4:12] s >>= 1 assert s == '0b01111111' s = BitStream('0b11011') s >>= 2 assert s.bin == '00110' s >>= 100000000000000 assert s.bin == '00000' s = BitStream('0xff') s >>= 1 assert s == '0x7f' s >>= 0 assert s == '0x7f' def test_shift_in_place_whole_bitstring(self): s = BitStream('0xabcd') s >>= len(s) assert s == '0x0000' def test_shift_right_in_place_errors(self): s = BitStream() with pytest.raises(ValueError): s >>= 1 s += '0b11' with pytest.raises(ValueError): s >>= -1 def test_shift_left_in_place(self): s = BitStream('0xffff') t = s[4:12] t <<= 2 assert t == '0b11111100' s = BitStream('0b11011') s <<= 2 assert s.bin == '01100' s <<= 100000000000000000000 assert s.bin == '00000' s = BitStream('0xff') s <<= 1 assert s == '0xfe' s <<= 0 assert s == '0xfe' def test_shift_left_in_place_errors(self): s = BitStream() with pytest.raises(ValueError): s <<= 1 s += '0b11' with pytest.raises(ValueError): s <<= -1 class TestReplace: def test_replace1(self): a = BitStream('0b1') n = a.replace('0b1', '0b0', bytealigned=True) assert a.bin == '0' assert n == 1 n = a.replace('0b1', '0b0', bytealigned=True) assert n == 0 def test_replace2(self): a = BitStream('0b00001111111') n = a.replace('0b1', '0b0', bytealigned=True) assert a.bin == '00001111011' assert n == 1 n = a.replace('0b1', '0b0', bytealigned=False) assert a.bin == '00000000000' assert n == 6 def test_replace3(self): a = BitStream('0b0') n = a.replace('0b0', '0b110011111', bytealigned=True) assert n == 1 assert a.bin == '110011111' n = a.replace('0b11', '', bytealigned=False) assert n == 3 assert a.bin == '001' def test_replace4(self): a = BitStream('0x00114723ef4732344700') n = a.replace('0x47', '0x00', bytealigned=True) assert n == 3 assert a.hex == '00110023ef0032340000' a.replace('0x00', '', bytealigned=True) assert a.hex == '1123ef3234' a.replace('0x11', '', start=1, bytealigned=True) assert a.hex == '1123ef3234' a.replace('0x11', '0xfff', end=7, bytealigned=True) assert a.hex == '1123ef3234' a.replace('0x11', '0xfff', end=8, bytealigned=True) assert a.hex == 'fff23ef3234' def test_replace5(self): a = BitStream.fromstring('0xab') b = BitStream.fromstring('0xcd') c = BitStream.fromstring('0xabef') c.replace(a, b) assert c == '0xcdef' assert a == '0xab' assert b == '0xcd' a = BitStream('0x0011223344') a.pos = 12 a.replace('0x11', '0xfff', bytealigned=True) assert a.pos == 0 assert a == '0x00fff223344' def test_replace_with_self(self): a = BitStream('0b11') a.replace('0b1', a) assert a == '0xf' a.replace(a, a) assert a == '0xf' def test_replace_count(self): a = BitStream('0x223344223344223344') n = a.replace('0x2', '0x0', count=0, bytealigned=True) assert n == 0 assert a.hex == '223344223344223344' n = a.replace('0x2', '0x0', count=1, bytealigned=True) assert n == 1 assert a.hex == '023344223344223344' n = a.replace('0x33', '', count=2, bytealigned=True) assert n == 2 assert a.hex == '02442244223344' n = a.replace('0x44', '0x4444', count=1435, bytealigned=True) assert n == 3 assert a.hex == '02444422444422334444' def test_replace_bitpos(self): a = BitStream('0xff') a.bitpos = 8 a.replace('0xff', '', bytealigned=True) assert a.bitpos == 0 a = BitStream('0b0011110001') a.bitpos = 4 a.replace('0b1', '0b000') assert a.bitpos == 0 a = BitStream('0b1') a.bitpos = 1 a.replace('0b1', '0b11111', bytealigned=True) assert a.bitpos == 0 a.replace('0b11', '0b0', False) assert a.bitpos == 0 a.append('0b00') a.pos = 5 a.replace('0b00', '0b11') assert a.bitpos == 5 def test_replace_errors(self): a = BitStream('0o123415') with pytest.raises(ValueError): a.replace('', Bits(0o7), bytealigned=True) with pytest.raises(ValueError): a.replace('0b1', '0b1', start=-100, bytealigned=True) with pytest.raises(ValueError): a.replace('0b1', '0b1', end=19, bytealigned=True) class TestSliceAssignment: def test_set_slice(self): a = BitStream() a[0:0] = '0xabcdef' assert a.bytepos == 0 a[4:16] = '' assert a == '0xaef' assert a.bitpos == 0 a.pos = 4 a[8:] = '0x00' assert a == '0xae00' assert a.bitpos == 0 a += '0xf' assert a.bitpos == 20 a[8:] = '0xe' assert a == '0xaee' assert a.bitpos == 0 b = BitStream() b[0:800] = '0xffee' assert b == '0xffee' b[4:48] = '0xeed123' assert b == '0xfeed123' b[-800:8] = '0x0000' assert b == '0x0000ed123' a = BitStream('0xabcde') assert a[-100:-90] == '' assert a[-100:-16] == '0xa' a[-100:-16] = '0x0' assert a == '0x0bcde' def test_inserting_using_set_item(self): a = BitStream() a[0:0] = '0xdeadbeef' assert a == '0xdeadbeef' assert a.bytepos == 0 a[16:16] = '0xfeed' assert a == '0xdeadfeedbeef' assert a.bytepos == 0 a[0:0] = '0xa' assert a == '0xadeadfeedbeef' assert a.bitpos == 0 a.bytepos = 6 a[0:8] = '0xff' assert a.bytepos == 6 a[8:0] = '0x000' assert a.startswith('0xff000ead') def test_slice_assignment_bit_pos(self): a = BitStream('int:64=-1') a.pos = 64 a[0:8] = '' assert a.pos == 0 a.pos = 52 a[-16:] = '0x0000' assert a.pos == 52 class TestPack: def test_pack1(self): s = bitstring.pack('uint:6, bin, hex, int:6, se, ue, oct', 10, '0b110', 'ff', -1, -6, 6, '54') t = BitStream('uint:6=10, 0b110, 0xff, int:6=-1, se=-6, ue=6, oct=54') assert s == t with pytest.raises(ValueError): pack('tomato', '0') with pytest.raises(ValueError): pack('uint', 12) with pytest.raises(ValueError): pack('int', 12) with pytest.raises(ValueError): pack('hex', 'penguin') with pytest.raises(ValueError): pack('hex12', '0x12') def test_pack_with_literals(self): s = bitstring.pack('0xf') assert s == '0xf' assert type(s), BitStream s = pack('0b1') assert s == '0b1' s = pack('0o7') assert s == '0o7' s = pack('int:10=-1') assert s == '0b1111111111' s = pack('uint:10=1') assert s == '0b0000000001' s = pack('ue=12') assert s.ue == 12 s = pack('se=-12') assert s.se == -12 s = pack('bin=01') assert s.bin == '01' s = pack('hex=01') assert s.hex == '01' s = pack('oct=01') assert s.oct == '01' def test_pack_with_dict(self): a = pack('uint:6=width, se=height', height=100, width=12) w, h = a.unpack('uint:6, se') assert w == 12 assert h == 100 d = {'w': '0xf', '300': 423, 'e': '0b1101'} a = pack('int:100=300, bin=e, uint:12=300', **d) x, y, z = a.unpack('int:100, bin, uint:12') assert x == 423 assert y == '1101' assert z == 423 def test_pack_with_dict2(self): a = pack('int:5, bin:3=b, 0x3, bin=c, se=12', 10, b='0b111', c='0b1') b = BitStream('int:5=10, 0b111, 0x3, 0b1, se=12') assert a == b a = pack('bits:3=b', b=BitStream('0b101')) assert a == '0b101' a = pack('bits:24=b', b=BitStream('0x001122')) assert a == '0x001122' def test_pack_with_dict3(self): s = pack('hex:4=e, hex:4=0xe, hex:4=e', e='f') assert s == '0xfef' s = pack('sep', sep='0b00') assert s == '0b00' def test_pack_with_dict4(self): s = pack('hello', hello='0xf') assert s == '0xf' s = pack('x, y, x, y, x', x='0b10', y='uint:12=100') t = BitStream('0b10, uint:12=100, 0b10, uint:12=100, 0b10') assert s == t a = [1, 2, 3, 4, 5] s = pack('int:8, div,' * 5, *a, **{'div': '0b1'}) t = BitStream('int:8=1, 0b1, int:8=2, 0b1, int:8=3, 0b1, int:8=4, 0b1, int:8=5, 0b1') assert s == t def test_pack_with_locals(self): width = 352 height = 288 s = pack('uint:12=width, uint:12=height', **locals()) assert s == '0x160120' def test_pack_with_length_restriction(self): _ = pack('bin:3', '0b000') with pytest.raises(bitstring.CreationError): _ = pack('bin:3', '0b0011') with pytest.raises(bitstring.CreationError): _ = pack('bin:3', '0b11') with pytest.raises(bitstring.CreationError): _ = pack('bin:3=0b0011') with pytest.raises(bitstring.CreationError): _ = pack('bin:3=0b11') _ = pack('hex:4', '0xf') with pytest.raises(bitstring.CreationError): _ = pack('hex:4', '0b111') with pytest.raises(bitstring.CreationError): _ = pack('hex:4', '0b11111') with pytest.raises(bitstring.CreationError): _ = pack('hex:8=0xf') _ = pack('oct:6', '0o77') with pytest.raises(bitstring.CreationError): _ = pack('oct:6', '0o1') with pytest.raises(bitstring.CreationError): _ = pack('oct:6', '0o111') with pytest.raises(bitstring.CreationError): _ = pack('oct:3', '0b1') with pytest.raises(bitstring.CreationError): _ = pack('oct:3=hello', hello='0o12') _ = pack('bits:3', BitStream('0b111')) with pytest.raises(bitstring.CreationError): _ = pack('bits:3', BitStream('0b11')) with pytest.raises(bitstring.CreationError): _ = pack('bits:3', BitStream('0b1111')) with pytest.raises(bitstring.CreationError): _ = pack('bits:12=b', b=BitStream('0b11')) def test_pack_null(self): s = pack('') assert not s s = pack(',') assert not s s = pack(',,,,,0b1,,,,,,,,,,,,,0b1,,,,,,,,,,') assert s == '0b11' s = pack(',,uint:12,,bin:3,', 100, '100') a, b = s.unpack('uint:12,bin:3') assert a == 100 assert b == '100' def test_pack_uint(self): s = pack('uint:10, uint:5', 1, 2) a, b = s.unpack('10, 5') assert (a.uint, b.uint) == (1, 2) s = pack('uint:10=150, uint:12=qee', qee=3) assert s == 'uint:10=150, uint:12=3' t = BitStream('uint:100=5') assert t == 'uint:100=5' def test_pack_defualt_uint_errors(self): with pytest.raises(bitstring.CreationError): _ = BitStream('5=-1') def test_packing_long_keyword_bitstring(self): s = pack('bits=b', b=BitStream(128000)) assert s == BitStream(128000) def test_packing_with_list_format(self): f = ['bin', 'hex', 'uint:10'] a = pack(','.join(f), '00', '234', 100) b = pack(f, '00', '234', 100) assert a == b class TestUnpack: def test_unpack1(self): s = BitStream('uint:13=23, hex=e, bin=010, int:41=-554, 0o44332, se=-12, ue=4') s.pos = 11 a, b, c, d, e, f, g = s.unpack('uint:13, hex:4, bin:3, int:41, oct:15, se, ue') assert a == 23 assert b == 'e' assert c == '010' assert d == -554 assert e == '44332' assert f == -12 assert g == 4 assert s.pos == 11 def test_unpack2(self): s = BitStream('0xff, 0b000, uint:12=100') a, b, c = s.unpack('bits:8, bits, uint:12') assert type(s) == BitStream assert a == '0xff' assert type(s) == BitStream assert b == '0b000' assert c == 100 a, b = s.unpack(['bits:11', 'uint']) assert a == '0xff, 0b000' assert b == 100 class TestFromFile: def test_creation_from_file_operations(self): filename = os.path.join(THIS_DIR, 'smalltestfile') s = BitStream(filename=filename) s.append('0xff') assert s.hex == '0123456789abcdefff' s = ConstBitStream(filename=filename) t = BitStream('0xff') + s assert t.hex == 'ff0123456789abcdef' s = BitStream(filename=filename) del s[:1] assert (BitStream('0b0') + s).hex == '0123456789abcdef' s = BitStream(filename=filename) del s[:7 * 8] assert s.hex == 'ef' s = BitStream(filename=filename) s.insert('0xc', 4) assert s.hex == '0c123456789abcdef' s = BitStream(filename=filename) s.prepend('0xf') assert s.hex == 'f0123456789abcdef' s = BitStream(filename=filename) s.overwrite('0xaaa', 12) assert s.hex == '012aaa6789abcdef' s = BitStream(filename=filename) s.reverse() assert s.hex == 'f7b3d591e6a2c480' s = BitStream(filename=filename) del s[-60:] assert s.hex == '0' s = BitStream(filename=filename) del s[:60] assert s.hex == 'f' def test_file_properties(self): s = ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) assert s.hex == '0123456789abcdef' assert s.uint == 81985529216486895 assert s.int == 81985529216486895 assert s.bin == '0000000100100011010001010110011110001001101010111100110111101111' assert s[:-1].oct == '002215053170465363367' s.bitpos = 0 assert s.read('se') == -72 s.bitpos = 0 assert s.read('ue') == 144 assert s.bytes == b'\x01\x23\x45\x67\x89\xab\xcd\xef' assert s.tobytes() == b'\x01\x23\x45\x67\x89\xab\xcd\xef' def test_creation_from_file_with_length(self): test_filename = os.path.join(THIS_DIR, 'test.m1v') s = ConstBitStream(filename=test_filename, length=32) assert s.length == 32 assert s.hex == '000001b3' s = ConstBitStream(filename=test_filename, length=0) assert not s small_test_filename = os.path.join(THIS_DIR, 'smalltestfile') with pytest.raises(bitstring.CreationError): _ = BitStream(filename=small_test_filename, length=65) with pytest.raises(bitstring.CreationError): _ = ConstBitStream(filename=small_test_filename, length=64, offset=1) with pytest.raises(bitstring.CreationError): _ = ConstBitStream(filename=small_test_filename, offset=65) with open(small_test_filename, 'rb') as f: with pytest.raises(bitstring.CreationError): _ = ConstBitStream(f, offset=65) with pytest.raises(bitstring.CreationError): _ = ConstBitStream(f, length=65) with pytest.raises(bitstring.CreationError): _ = ConstBitStream(f, offset=60, length=5) def test_creation_from_file_with_offset(self): filename = os.path.join(THIS_DIR, 'test.m1v') a = BitStream(filename=filename, offset=4) assert a.peek(4 * 8).hex == '00001b31' b = BitStream(filename=filename, offset=28) assert b.peek(8).hex == '31' def test_file_slices(self): s = BitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) assert s[-16:].hex == 'cdef' def test_creataion_from_file_errors(self): with pytest.raises(IOError): _ = BitStream(filename='Idonotexist') def test_find_in_file(self): s = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) assert s.find('0x160120') assert s.bytepos == 4 s3 = s.read(24) assert s3.hex == '160120' s.bytepos = 0 assert s._pos == 0 assert s.find('0x0001b2') assert s.bytepos == 13 def test_hex_from_file(self): s = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) assert s[0:32].hex == '000001b3' assert s[-32:].hex == '000001b7' s.hex = '0x11' assert s.hex == '11' def test_file_operations(self): filename = os.path.join(THIS_DIR, 'test.m1v') s1 = BitStream(filename=filename) s2 = BitStream(filename=filename) assert s1.read(32).hex == '000001b3' assert s2.read(32).hex == '000001b3' s1.bytepos += 4 assert s1.read(8).hex == '02' assert s2.read(5 * 8).hex == '1601208302' s1.pos = s1.len with pytest.raises(ValueError): s1.pos += 1 def test_file_bit_getting(self): s = ConstBitStream(filename=os.path.join(THIS_DIR, 'smalltestfile'), offset=16, length=8) b = s[1] assert b b = s.any(0, [-1, -2, -3]) assert b b = s.all(0, [0, 1, 2]) assert not b class TestCreationErrors: def test_incorrect_bin_assignment(self): s = BitStream() with pytest.raises(bitstring.CreationError): s._setbin_safe('0010020') def test_incorrect_hex_assignment(self): s = BitStream() with pytest.raises(bitstring.CreationError): s.hex = '0xabcdefg' class TestLength: def test_length_zero(self): assert BitStream('').len == 0 def test_length(self): assert BitStream('0x80').len == 8 def test_offset_length_error(self): with pytest.raises(bitstring.CreationError): BitStream(hex='0xffff', offset=-1) class TestSimpleConversions: def test_convert_to_uint(self): assert BitStream('0x10').uint == 16 assert BitStream('0b000111').uint == 7 def test_convert_to_int(self): assert BitStream('0x10').int == 16 assert BitStream('0b11110').int == -2 def test_convert_to_hex(self): assert BitStream(bytes=b'\x00\x12\x23\xff').hex == '001223ff' s = BitStream('0b11111') with pytest.raises(bitstring.InterpretError): _ = s.hex class TestEmpty: def test_empty_bitstring(self): s = BitStream() with pytest.raises(bitstring.ReadError): s.read(1) assert s.bin == '' assert s.hex == '' with pytest.raises(bitstring.InterpretError): _ = s.int with pytest.raises(bitstring.InterpretError): _ = s.uint assert not s def test_non_empty_bit_stream(self): s = BitStream(bin='0') assert not not s.len class TestPosition: def test_bit_position(self): s = BitStream(bytes=b'\x00\x00\x00') assert s.bitpos == 0 s.read(5) assert s.pos == 5 s.pos = s.len with pytest.raises(bitstring.ReadError): s.read(1) def test_byte_position(self): s = BitStream(bytes=b'\x00\x00\x00') assert s.bytepos == 0 s.read(10) with pytest.raises(bitstring.ByteAlignError): _ = s.bytepos s.read(6) assert s.bytepos == 2 def test_seek_to_bit(self): s = BitStream(bytes=b'\x00\x00\x00\x00\x00\x00') s.bitpos = 0 assert s.bitpos == 0 with pytest.raises(ValueError): s.pos = -1 with pytest.raises(ValueError): s.bitpos = 6 * 8 + 1 s.bitpos = 6 * 8 assert s.bitpos == 6 * 8 def test_seek_to_byte(self): s = BitStream(bytes=b'\x00\x00\x00\x00\x00\xab') s.bytepos = 5 assert s.read(8).hex == 'ab' def test_advance_bits_and_bytes(self): s = BitStream(bytes=b'\x00\x00\x00\x00\x00\x00\x00\x00') s.pos += 5 assert s.pos == 5 s.bitpos += 16 assert s.pos == 2 * 8 + 5 s.pos -= 8 assert s.pos == 8 + 5 def test_retreat_bits_and_bytes(self): a = BitStream(length=100) a.pos = 80 a.bytepos -= 5 assert a.bytepos == 5 a.pos -= 5 assert a.pos == 35 class TestAppend: def test_append(self): s1 = BitStream('0b00000') s1.append(BitStream(bool=True)) assert s1.bin == '000001' assert (BitStream('0x0102') + BitStream('0x0304')).hex == '01020304' def test_append_same_bitstring(self): s1 = BitStream('0xf0')[:6] s1.append(s1) assert s1.bin == '111100111100' def test_append_with_offset(self): s = BitStream(bytes=b'\x28\x28', offset=1) s.append('0b0') assert s.hex == '5050' class TestByteAlign: def test_byte_align(self): s = BitStream(hex='0001ff23') s.bytealign() assert s.bytepos == 0 s.pos += 11 s.bytealign() assert s.bytepos == 2 s.pos -= 10 s.bytealign() assert s.bytepos == 1 def test_insert_byte_aligned(self): s = BitStream('0x0011') s.insert(BitStream('0x22'), 8) assert s.hex == '002211' s = BitStream(0) s.insert(BitStream(bin='101'), 0) assert s.bin == '101' class TestTruncate: def test_truncate_start(self): s = BitStream('0b1') del s[0] assert not s s = BitStream(hex='1234') assert s.hex == '1234' del s[:4] assert s.hex == '234' del s[:9] assert s.bin == '100' del s[:2] assert s.bin == '0' assert s.len == 1 del s[:1] assert not s def test_truncate_end(self): s = BitStream('0b1') del s[-1:] assert not s s = BitStream(bytes=b'\x12\x34') assert s.hex == '1234' del s[-4:] assert s.hex == '123' del s[-9:] assert s.bin == '000' del s[-3:] assert not s s = BitStream('0b001') del s[:2] del s[-1:] assert not s class TestSlice: def test_byte_aligned_slice(self): s = BitStream(hex='0x123456') assert s[8:16].hex == '34' s = s[8:24] assert s.len == 16 assert s.hex == '3456' s = s[0:8] assert s.hex == '34' s.hex = '0x123456' assert s[8:24][0:8].hex == '34' def test_slice(self): s = BitStream(bin='000001111100000') s1 = s[0:5] s2 = s[5:10] s3 = s[10:15] assert s1.bin == '00000' assert s2.bin == '11111' assert s3.bin == '00000' class TestInsert: def test_insert(self): s1 = BitStream(hex='0x123456') s2 = BitStream(hex='0xff') s1.bytepos = 1 s1.insert(s2) assert s1.bytepos == 2 assert s1.hex == '12ff3456' s1.insert('0xee', 24) assert s1.hex == '12ff34ee56' assert s1.bitpos == 32 with pytest.raises(ValueError): s1.insert('0b1', -1000) with pytest.raises(ValueError): s1.insert('0b1', 1000) def test_insert_null(self): s = BitStream(hex='0x123') s.insert(BitStream(), 3) assert s.hex == '123' def test_insert_bits(self): one = BitStream(bin='1') zero = BitStream(bin='0') s = BitStream(bin='00') s.insert(one, 0) assert s.bin == '100' s.insert(zero, 0) assert s.bin == '0100' s.insert(one, s.len) assert s.bin == '01001' s.insert(s, 2) assert s.bin == '0101001001' class TestResetting: def test_set_hex(self): s = BitStream() s.hex = '0' assert s.hex == '0' s.hex = '0x010203045' assert s.hex == '010203045' with pytest.raises(bitstring.CreationError): s.hex = '0x002g' def test_set_bin(self): s = BitStream(bin="000101101") assert s.bin == '000101101' assert s.len == 9 s.bin = '0' assert s.bin == '0' assert s.len == 1 def test_set_empty_bin(self): s = BitStream(hex='0x000001b3') s.bin = '' assert s.len == 0 assert s.bin == '' def test_set_invalid_bin(self): s = BitStream() with pytest.raises(bitstring.CreationError): s.bin = '00102' class TestOverwriting: def test_overwrite_bit(self): s = BitStream(bin='0') s.overwrite(BitStream(bin='1'), 0) assert s.bin == '1' def test_overwrite_limits(self): s = BitStream(bin='0b11111') s.overwrite(BitStream(bin='000'), 0) assert s.bin == '00011' s.overwrite('0b000', 2) assert s.bin == '00000' def test_overwrite_null(self): s = BitStream(hex='342563fedec') s2 = BitStream(s) s.overwrite(BitStream(bin=''), 23) assert s.bin == s2.bin def test_overwrite_position(self): s1 = BitStream(hex='0123456') s2 = BitStream(hex='ff') s1.bytepos = 1 s1.overwrite(s2) assert (s1.hex, s1.bytepos) == ('01ff456', 2) s1.overwrite('0xff', 0) assert (s1.hex, s1.bytepos) == ('ffff456', 1) def test_overwrite_with_self(self): s = BitStream('0x123') s.overwrite(s) assert s == '0x123' class TestSplit: def test_split_byte_aligned_corner_cases(self): s = BitStream() bsl = s.split(BitStream(hex='0xff')) assert next(bsl).hex == '' with pytest.raises(StopIteration): _ = next(bsl) s = BitStream(hex='aabbcceeddff') delimiter = BitStream() bsl = s.split(delimiter) with pytest.raises(ValueError): _ = next(bsl) delimiter = BitStream(hex='11') bsl = s.split(delimiter) assert next(bsl).hex == s.hex def test_split_byte_aligned(self): s = BitStream(hex='0x1234aa1234bbcc1234ffff') delimiter = BitStream(hex='1234') bsl = s.split(delimiter) assert [b.hex for b in bsl] == ['', '1234aa', '1234bbcc', '1234ffff'] assert s.pos == 0 def test_split_byte_aligned_with_intial_bytes(self): s = BitStream(hex='aa471234fedc43 47112233 47 4723 472314') delimiter = BitStream(hex='47') s.find(delimiter) assert s.bytepos == 1 bsl = s.split(delimiter, start=0) assert [b.hex for b in bsl] == ['aa', '471234fedc43', '47112233', '47', '4723', '472314'] assert s.bytepos == 1 def test_split_byte_aligned_with_overlapping_delimiter(self): s = BitStream(hex='aaffaaffaaffaaffaaff') bsl = s.split(BitStream(hex='aaffaa')) assert [b.hex for b in bsl] == ['', 'aaffaaff', 'aaffaaffaaff'] class TestAdding: def test_adding(self): s1 = BitStream(hex='0x0102') s2 = BitStream(hex='0x0304') s3 = s1 + s2 assert s1.hex == '0102' assert s2.hex == '0304' assert s3.hex == '01020304' s3 += s1 assert s3.hex == '010203040102' assert s2[9:16].bin == '0000100' assert s1[0:9].bin == '000000010' s4 = BitStream(bin='000000010') + BitStream(bin='0000100') assert s4.bin == '0000000100000100' s5 = s1[0:9] + s2[9:16] assert s5.bin == '0000000100000100' def test_more_adding(self): s = BitStream(bin='00') + BitStream(bin='') + BitStream(bin='11') assert s.bin == '0011' s = '0b01' s += BitStream('0b11') assert s.bin == '0111' s = BitStream('0x00') t = BitStream('0x11') s += t assert s.hex == '0011' assert t.hex == '11' s += s assert s.hex == '00110011' def test_radd(self): s = '0xff' + BitStream('0xee') assert s.hex == 'ffee' def test_truncate_asserts(self): s = BitStream('0x001122') s.bytepos = 2 del s[-s.len:] # self.assertEqual(s.bytepos, 0) s.append('0x00') s.append('0x1122') s.bytepos = 2 del s[:s.len] # self.assertEqual(s.bytepos, 0) s.append('0x00') def test_overwrite_errors(self): s = BitStream(bin='11111') with pytest.raises(ValueError): s.overwrite(BitStream(bin='1'), -10) with pytest.raises(ValueError): s.overwrite(BitStream(bin='1'), 6) s.overwrite('bin=0', 5) assert s.b == '111110' s.overwrite(BitStream(hex='0x00'), 1) assert s.b == '100000000' def test_delete_bits(self): s = BitStream(bin='000111100000') s.bitpos = 4 del s[4:8] assert s.bin == '00010000' del s[4:1004] assert s.bin, '0001' def test_delete_bits_with_position(self): s = BitStream(bin='000111100000') del s[4:8] assert s.bin == '00010000' def test_delete_bytes(self): s = BitStream('0x00112233') del s[8:8] assert s.hex == '00112233' assert s.pos == 0 del s[8:16] assert s.hex == '002233' assert s.bytepos == 0 del s[:24] assert not s assert s.pos == 0 def test_get_item_with_positive_position(self): s = BitStream(bin='0b1011') assert s[0] == True assert s[1] == False assert s[2] == True assert s[3] == True with pytest.raises(IndexError): _ = s[4] def test_get_item_with_negative_position(self): s = BitStream(bin='1011') assert s[-1] == True assert s[-2] == True assert s[-3] == False assert s[-4] == True with pytest.raises(IndexError): _ = s[-5] def test_slicing(self): s = ConstBitStream(hex='0123456789') assert s[0:8].hex == '01' assert not s[0:0] assert not s[23:20] assert s[8:12].bin == '0010' assert s[32:80] == '0x89' def test_negative_slicing(self): s = ConstBitStream(hex='012345678') assert s[:-8].hex == '0123456' assert s[-16:-8].hex == '56' assert s[-24:].hex == '345678' assert s[-1000:-24] == '0x012' def test_len(self): s = BitStream() assert len(s) == 0 s.append(BitStream(bin='001')) assert len(s) == 3 def test_join(self): s1 = BitStream(bin='0') s2 = BitStream(bin='1') s3 = BitStream(bin='000') s4 = BitStream(bin='111') strings = [s1, s2, s1, s3, s4] s = BitStream().join(strings) assert s.bin == '010000111' def test_join2(self): s1 = BitStream(hex='00112233445566778899aabbccddeeff') s2 = BitStream(bin='0b000011') bsl = [s1[0:32], s1[4:12], s2, s2, s2, s2] s = ConstBitStream().join(bsl) assert s.hex == '00112233010c30c3' bsl = [BitStream(uint=j, length=12) for j in range(10) for _ in range(10)] s = BitStream().join(bsl) assert s.length == 1200 def test_join_with_ints(self): with pytest.raises(TypeError): s = BitStream().join([1, 2]) def test_pos(self): s = BitStream(bin='1') assert s.bitpos == 0 s.read(1) assert s.bitpos == 1 def test_writing_data(self): strings = [BitStream(bin=x) for x in ['0', '001', '0011010010', '010010', '1011']] s = BitStream().join(strings) s2 = BitStream(bytes=s.bytes) assert s2.bin == '000100110100100100101011' s2.append(BitStream(bin='1')) s3 = BitStream(bytes=s2.tobytes()) assert s3.bin == '00010011010010010010101110000000' def test_writing_data_with_offsets(self): s1 = BitStream(bytes=b'\x10') s2 = BitStream(bytes=b'\x08\x00', length=8, offset=1) s3 = BitStream(bytes=b'\x04\x00', length=8, offset=2) assert s1 == s2 assert s2 == s3 assert s1.bytes == s2.bytes assert s2.bytes == s3.bytes def test_various_things1(self): hexes = ['12345678', '87654321', 'ffffffffff', 'ed', '12ec'] bins = ['001010', '1101011', '0010000100101110110110', '11', '011'] bsl = [] for (hex_, bin_) in list(zip(hexes, bins)) * 5: bsl.append(BitStream(hex=hex_)) bsl.append(BitStream(bin=bin_)) s = BitStream().join(bsl) for (hex_, bin_) in list(zip(hexes, bins)) * 5: h = s.read(4 * len(hex_)) b = s.read(len(bin_)) assert h.hex == hex_ assert b.bin == bin_ def test_various_things2(self): s1 = BitStream(hex="0x1f08")[:13] assert s1.bin == '0001111100001' s2 = BitStream(bin='0101') assert s2.bin == '0101' s1.append(s2) assert s1.length == 17 assert s1.bin == '00011111000010101' s1 = s1[3:8] assert s1.bin == '11111' def test_various_things3(self): s1 = BitStream(hex='0x012480ff')[2:27] s2 = s1 + s1 assert s2.length == 50 s3 = s2[0:25] s4 = s2[25:50] assert s3.bin == s4.bin def test_peek_bit(self): s = BitStream(bin='01') assert s.peek(1) == [0] assert s.peek(1) == [0] assert s.read(1) == [0] assert s.peek(1) == [1] assert s.peek(1) == [1] s = BitStream(bytes=b'\x1f', offset=3) assert s.len == 5 assert s.peek(5).bin == '11111' assert s.peek(5).bin == '11111' s.pos += 1 with pytest.raises(bitstring.ReadError): _ = s.peek(5) s = BitStream(hex='001122334455') assert s.peek(8).hex == '00' assert s.read(8).hex == '00' s.pos += 33 with pytest.raises(bitstring.ReadError): _ = s.peek(8) s = BitStream(hex='001122334455') assert s.peek(8 * 2).hex == '0011' assert s.read(8 * 3).hex == '001122' assert s.peek(8 * 3).hex == '334455' with pytest.raises(bitstring.ReadError): _ = s.peek(25) def test_advance_bit(self): s = BitStream(hex='0xff') s.bitpos = 6 s.pos += 1 assert s.bitpos == 7 s.bitpos += 1 with pytest.raises(ValueError): s.pos += 1 def test_advance_byte(self): s = BitStream(hex='0x010203') s.bytepos += 1 assert s.bytepos == 1 s.bytepos += 1 assert s.bytepos == 2 s.bytepos += 1 with pytest.raises(ValueError): s.bytepos += 1 def test_retreat_bit(self): s = BitStream(hex='0xff') with pytest.raises(ValueError): s.pos -= 1 s.pos = 5 s.pos -= 1 assert s.pos == 4 def test_retreat_byte(self): s = BitStream(hex='0x010203') with pytest.raises(ValueError): s.bytepos -= 1 s.bytepos = 3 s.bytepos -= 1 assert s.bytepos == 2 assert s.read(8).hex == '03' def test_creation_by_auto(self): s = BitStream('0xff') assert s.hex == 'ff' s = BitStream('0b00011') assert s.bin == '00011' with pytest.raises(bitstring.CreationError): _ = BitStream('hello') s1 = BitStream(bytes=b'\xf5', length=3, offset=5) with pytest.raises(TypeError): _ = BitStream(1.2) def test_creation_by_auto2(self): s = BitStream('bin=001') assert s.bin == '001' s = BitStream('oct=0o007') assert s.oct == '007' s = BitStream('hex=123abc') assert s == '0x123abc' s = BitStream('bin2=01') assert s == '0b01' for s in ['bin:1=01', 'bits:4=0b1', 'oct3=000', 'hex4=0x1234']: with pytest.raises(bitstring.CreationError): _ = BitStream(s) def test_insert_using_auto(self): s = BitStream('0xff') s.insert('0x00', 4) assert s.hex == 'f00f' with pytest.raises(ValueError): s.insert('ff') def test_overwrite_using_auto(self): s = BitStream('0x0110') s.overwrite('0b1') assert s.hex == '8110' s.overwrite('') assert s.hex == '8110' with pytest.raises(ValueError): s.overwrite('0bf') def test_find_using_auto(self): s = BitStream('0b000000010100011000') assert s.find('0b101') assert s.pos == 7 def test_findbytealigned_using_auto(self): s = BitStream('0x00004700') assert s.find('0b01000111', bytealigned=True) assert s.bytepos == 2 def test_append_using_auto(self): s = BitStream('0b000') s.append('0b111') assert s.bin == '000111' s.append('0b0') assert s.bin == '0001110' def test_split_byte_aligned_using_auto(self): s = BitStream('0x000143563200015533000123') sections = s.split('0x0001') assert next(sections).hex == '' assert next(sections).hex == '0001435632' assert next(sections).hex == '00015533' assert next(sections).hex == '000123' pytest.raises(StopIteration, next, sections) def test_split_byte_aligned_with_self(self): s = BitStream('0x1234') sections = s.split(s) assert next(sections).hex == '' assert next(sections).hex == '1234' with pytest.raises(StopIteration): next(sections) def test_prepend(self): s = BitStream('0b000') s.prepend('0b11') assert s.bin == '11000' s.prepend(s) assert s.bin == '1100011000' s.prepend('') assert s.bin == '1100011000' def test_null_slice(self): s = BitStream('0x111') t = s[1:1] assert len(t) == 0 def test_multiple_autos(self): s = BitStream('0xa') s.prepend('0xf') s.append('0xb') assert s == '0xfab' s.prepend(s) s.append('0x100') s.overwrite('0x5', 4) assert s == '0xf5bfab100' def test_reverse(self): s = BitStream('0b0011') s.reverse() assert s.bin == '1100' s = BitStream('0b10') s.reverse() assert s.bin == '01' s = BitStream() s.reverse() assert s.bin == '' def test_init_with_concatenated_strings(self): s = BitStream('0xff 0Xee 0xd 0xcc') assert s.hex == 'ffeedcc' s = BitStream('0b0 0B111 0b001') assert s.bin == '0111001' s += '0b1' + '0B1' assert s.bin == '011100111' s = BitStream(hex='ff0xee') assert s.hex == 'ffee' s = BitStream(bin='000b0b11') assert s.bin == '0011' s = BitStream(' 0o123 0O 7 0 o1') assert s.oct == '12371' s += ' 0 o 332' assert s.oct == '12371332' def test_equals(self): s1 = BitStream('0b01010101') s2 = BitStream('0b01010101') assert s1 == s2 s3 = BitStream() s4 = BitStream() assert s3 == s4 assert not s3 != s4 s5 = BitStream(bytes=b'\xff', offset=2, length=3) s6 = BitStream('0b111') assert s5 == s6 class A(object): pass assert not s5 == A() def test_large_equals(self): s1 = BitStream(1000000) s2 = BitStream(1000000) s1.set(True, [-1, 55, 53214, 534211, 999999]) s2.set(True, [-1, 55, 53214, 534211, 999999]) assert s1 == s2 s1.set(True, 800000) assert s1 != s2 def test_not_equals(self): s1 = BitStream('0b0') s2 = BitStream('0b1') assert s1 != s2 assert not s1 != BitStream('0b0') def test_equality_with_auto_initialised(self): a = BitStream('0b00110111') assert a == '0b00110111' assert a == '0x37' assert '0b0011 0111' == a assert '0x3 0x7' == a assert not a == '0b11001000' assert not '0x3737' == a def test_invert_special_method(self): s = BitStream('0b00011001') assert (~s).bin == '11100110' assert (~BitStream('0b0')).bin == '1' assert (~BitStream('0b1')).bin == '0' assert ~~s == s def test_invert_bit_position(self): s = ConstBitStream('0xefef') s.pos = 8 t = ~s assert s.pos == 8 assert t.pos == 0 def test_invert_special_method_errors(self): s = BitStream() with pytest.raises(bitstring.Error): _ = ~s def test_join_with_auto(self): s = BitStream().join(['0xf', '0b00', BitStream(bin='11')]) assert s == '0b11110011' def test_auto_bit_string_copy(self): s = BitStream('0xabcdef') t = BitStream(s) assert t.hex == 'abcdef' del s[-8:] assert t.hex == 'abcdef' class TestMultiplication: def test_multiplication(self): a = BitStream('0xff') b = a * 8 assert b == '0xffffffffffffffff' b = 4 * a assert b == '0xffffffff' assert 1 * a == a * 1 == a c = a * 0 assert not c a *= 3 assert a == '0xffffff' a *= 0 assert not a one = BitStream('0b1') zero = BitStream('0b0') mix = one * 2 + 3 * zero + 2 * one * 2 assert mix == '0b110001111' q = BitStream() q *= 143 assert not q q += [True, True, False] assert q.bitpos == 3 q *= 0 assert not q assert q.bitpos == 0 def test_multiplication_with_files(self): a = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) b = a.len a *= 3 assert a.len == 3 * b def test_multiplication_errors(self): a = BitStream('0b1') b = BitStream('0b0') with pytest.raises(ValueError): _ = a * -1 with pytest.raises(ValueError): a *= -1 with pytest.raises(ValueError): _ = -1 * a with pytest.raises(TypeError): _ = a * 1.2 with pytest.raises(TypeError): _ = b * a with pytest.raises(TypeError): a *= b class TestBitWise: def test_bitwise_and(self): a = BitStream('0b01101') b = BitStream('0b00110') assert (a & b).bin == '00100' assert (a & '0b11111') == a with pytest.raises(ValueError): _ = a & '0b1' with pytest.raises(ValueError): _ = b & '0b110111111' c = BitStream('0b0011011') c.pos = 4 d = c & '0b1111000' assert d.pos == 0 assert d.bin == '0011000' d = '0b1111000' & c assert d.bin == '0011000' def test_bitwise_or(self): a = BitStream('0b111001001') b = BitStream('0b011100011') c = a | b assert c.bin == '111101011' assert (a | '0b000000000') == a with pytest.raises(ValueError): _ = a | '0b0000' with pytest.raises(ValueError): _ = b | (a + '0b1') a = '0xff00' | BitStream('0x00f0') assert a.hex == 'fff0' def test_bitwise_xor(self): a = BitStream('0b111001001') b = BitStream('0b011100011') c = a ^ b assert c.bin == '100101010' assert (a ^ '0b111100000').bin == '000101001' with pytest.raises(ValueError): _ = a ^ '0b0000' with pytest.raises(ValueError): _ = b ^ (a + '0b1') a = '0o707' ^ BitStream('0o777') assert a.oct == '070' class TestSplit2: def test_split(self): a = BitStream('0b0 010100111 010100 0101 010') a.pos = 20 subs = [i.bin for i in a.split('0b010')] assert subs == ['0', '010100111', '010100', '0101', '010'] assert a.pos == 20 def test_split_corner_cases(self): a = BitStream('0b000000') bsl = a.split('0b1', False) assert next(bsl) == a with pytest.raises(StopIteration): next(bsl) b = BitStream() bsl = b.split('0b001', False) assert not next(bsl) with pytest.raises(StopIteration): _ = next(bsl) def test_split_errors(self): a = BitStream('0b0') b = a.split('', False) with pytest.raises(ValueError): _ = next(b) def test_slice_with_offset(self): a = BitStream(bytes=b'\x00\xff\x00', offset=7) b = a[7:12] assert b.bin == '11000' def test_split_with_maxsplit(self): a = BitStream('0xaabbccbbccddbbccddee') assert len(list(a.split('0xbb', bytealigned=True))) == 4 bsl = list(a.split('0xbb', count=1, bytealigned=True)) assert (len(bsl), bsl[0]) == (1, '0xaa') bsl = list(a.split('0xbb', count=2, bytealigned=True)) assert len(bsl) == 2 assert bsl[0] == '0xaa' assert bsl[1] == '0xbbcc' def test_split_more(self): s = BitStream('0b1100011001110110') for i in range(10): a = list(s.split('0b11', False, count=i)) b = list(s.split('0b11', False))[:i] assert a == b b = s.split('0b11', count=-1) with pytest.raises(ValueError): _ = next(b) def test_split_startbit(self): a = BitStream('0b0010101001000000001111') bsl = a.split('0b001', bytealigned=False, start=1) assert [x.bin for x in bsl] == ['010101', '001000000', '001111'] b = a.split('0b001', start=-100) with pytest.raises(ValueError): _ = next(b) b = a.split('0b001', start=23) with pytest.raises(ValueError): _ = next(b) b = a.split('0b1', start=10, end=9) with pytest.raises(ValueError): _ = next(b) def test_split_startbit_byte_aligned(self): a = BitStream('0x00ffffee') bsl = list(a.split('0b111', start=9, bytealigned=True)) assert [x.bin for x in bsl] == ['1111111', '11111111', '11101110'] def test_split_endbit(self): a = BitStream('0b000010001001011') bsl = list(a.split('0b1', bytealigned=False, end=14)) assert [x.bin for x in bsl] == ['0000', '1000', '100', '10', '1'] assert list(a[4:12].split('0b0', False)) == list(a.split('0b0', start=4, end=12)) try: list(a.split('0xffee', end=15)) except ValueError: pytest.fail("ValueError raised unexpectedly") # Whereas this one will when we call next() bsl = a.split('0xffee', end=16) with pytest.raises(ValueError): _ = next(bsl) def test_split_endbit_byte_aligned(self): a = BitStream('0xff00ff')[:22] bsl = list(a.split('0b 0000 0000 111', end=19)) assert [x.bin for x in bsl] == ['11111111', '00000000111'] bsl = list(a.split('0b 0000 0000 111', end=18)) assert [x.bin for x in bsl] == ['111111110000000011'] def test_split_max_split(self): a = BitStream('0b1' * 20) for i in range(10): bsl = list(a.split('0b1', count=i)) assert len(bsl) == i ####################### def test_explicit_auto(self): with pytest.raises(bitstring.CreationError): a = BitStream(auto='0x1') def test_position_in_slice(self): a = BitStream('0x00ffff00') a.bytepos = 2 b = a[8:24] assert b.bytepos == 0 def test_find_byte_aligned_with_bits(self): a = BitStream('0x00112233445566778899') a.find('0b0001', bytealigned=True) assert a.bitpos == 8 def test_find_startbit_not_byte_aligned(self): a = BitStream('0b0010000100') found = a.find('0b1', start=4) assert (found, a.bitpos) == ((7,), 7) found = a.find('0b1', start=2) assert (found, a.bitpos) == ((2,), 2) found = a.find('0b1', bytealigned=False, start=8) assert (found, a.bitpos) == ((), 2) def test_find_endbit_not_byte_aligned(self): a = BitStream('0b0010010000') found = a.find('0b1', bytealigned=False, end=2) assert (found, a.bitpos) == ((), 0) found = a.find('0b1', end=3) assert (found, a.bitpos) == ((2,), 2) found = a.find('0b1', bytealigned=False, start=3, end=5) assert (found, a.bitpos) == ((), 2) found = a.find('0b1', start=3, end=6) assert (found[0], a.bitpos) == (5, 5) def test_find_startbit_byte_aligned(self): a = BitStream('0xff001122ff0011ff') a.pos = 40 found = a.find('0x22', start=23, bytealigned=True) assert (found, a.bytepos) == ((24,), 3) a.bytepos = 4 found = a.find('0x22', start=24, bytealigned=True) assert (found, a.bytepos) == ((24,), 3) found = a.find('0x22', start=25, bytealigned=True) assert (found, a.pos) == ((), 24) found = a.find('0b111', start=40, bytealigned=True) assert (found, a.pos) == ((56,), 56) def test_find_endbit_byte_aligned(self): a = BitStream('0xff001122ff0011ff') found = a.find('0x22', end=31, bytealigned=True) assert not found assert a.pos == 0 found = a.find('0x22', end=32, bytealigned=True) assert found assert a.pos == 24 assert found[0] == 24 def test_find_start_endbit_errors(self): a = BitStream('0b00100') with pytest.raises(ValueError): _ = a.find('0b1', bytealigned=False, start=-100) with pytest.raises(ValueError): _ = a.find('0b1', end=6) with pytest.raises(ValueError): _ = a.find('0b1', start=4, end=3) b = BitStream('0x0011223344') with pytest.raises(ValueError): _ = b.find('0x22', bytealigned=True, start=-100) with pytest.raises(ValueError): _ = b.find('0x22', end=41, bytealigned=True) def test_prepend_and_append_again(self): c = BitStream('0x1122334455667788') c.bitpos = 40 c.append('0b1') assert c.bitpos == len(c) c = BitStream() c.prepend('0x1234') assert c.bytepos == 0 c = BitStream() c.append('0x1234') assert c.bytepos == 2 s = BitStream(bytes=b'\xff\xff', offset=2) assert s.length == 14 t = BitStream(bytes=b'\x80', offset=1, length=2) s.prepend(t) assert s == '0x3fff' def test_find_all(self): a = BitStream('0b11111') p = a.findall('0b1') assert list(p) == [0, 1, 2, 3, 4] p = a.findall('0b11') assert list(p) == [0, 1, 2, 3] p = a.findall('0b10') assert list(p) == [] a = BitStream('0x4733eeff66554747335832434547') p = a.findall('0x47', bytealigned=True) assert list(p) == [0, 6 * 8, 7 * 8, 13 * 8] p = a.findall('0x4733', bytealigned=True) assert list(p) == [0, 7 * 8] a = BitStream('0b1001001001001001001') p = a.findall('0b1001', bytealigned=False) assert list(p) == [0, 3, 6, 9, 12, 15] assert a.pos == 0 def test_find_all_generator(self): a = BitStream('0xff1234512345ff1234ff12ff') p = a.findall('0xff', bytealigned=True) assert next(p) == 0 assert next(p) == 6 * 8 assert next(p) == 9 * 8 assert next(p) == 11 * 8 with pytest.raises(StopIteration): _ = next(p) def test_find_all_count(self): s = BitStream('0b1') * 100 for i in [0, 1, 23]: assert len(list(s.findall('0b1', count=i))) == i with pytest.raises(ValueError): _ = s.findall('0b1', bytealigned=True, count=-1) def test_contains(self): a = BitStream('0b1') + '0x0001dead0001' assert '0xdead' in a assert a.pos == 0 assert not '0xfeed' in a def test_repr(self): max_ = bitstring.bits.MAX_CHARS bls = ['', '0b1', '0o5', '0x43412424f41', '0b00101001010101'] for bs in bls: a = BitStream(bs) b = eval(a.__repr__()) assert a == b filename = os.path.join(THIS_DIR, 'test.m1v') for f in [ConstBitStream(filename=filename), ConstBitStream(filename=filename, length=17), ConstBitStream(filename=filename, length=23, offset=23102)]: f2 = eval(f.__repr__()) assert f2.tobytes() == f.tobytes() a = BitStream('0b1') assert repr(a) == "BitStream('0b1')" a += '0b11' a.pos = 2 assert repr(a) == "BitStream('0b111', pos=2)" a.pos = 0 a += '0b1' assert repr(a) == "BitStream('0xf', pos=4)" a.pos = 0 a *= max_ assert repr(a) == "BitStream('0x" + "f" * max_ + "')" a += '0xf' assert repr(a) == "BitStream('0x" + "f" * max_ + "...', pos=1004) # length=%d" % (max_ * 4 + 4) def test_print(self): s = BitStream(hex='0x00') assert '0x' + s.hex == s.__str__() s = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) assert '0x' + s[0: bitstring.bits.MAX_CHARS * 4].hex + '...' == s.__str__() assert BitStream().__str__() == '' s = BitStream('0b11010') assert '0b' + s.bin == s.__str__() s = BitStream('0x12345678901234567890,0b1') assert '0x12345678901234567890, 0b1' == s.__str__() def test_iter(self): a = BitStream('0b001010') b = BitStream() for bit in a: b.append(ConstBitStream(bool=bit)) assert a == b def test_delitem(self): a = BitStream('0xffee') del a[0:8] assert a.hex == 'ee' del a[0:8] assert not a del a[10:12] assert not a def test_non_zero_bits_at_start(self): a = BitStream(bytes=b'\xff', offset=2) b = BitStream('0b00') b += a assert b == '0b0011 1111' assert a.tobytes() == b'\xfc' def test_non_zero_bits_at_end(self): a = BitStream(bytes=b'\xff', length=5) b = BitStream('0b00') a += b assert a == '0b1111100' assert a.tobytes() == b'\xf8' with pytest.raises(ValueError): _ = a.bytes def test_new_offset_errors(self): with pytest.raises(bitstring.CreationError): _ = BitStream(hex='ff', offset=-1) with pytest.raises(bitstring.CreationError): _ = BitStream('0xffffffff', offset=33) def test_slice_step(self): a = BitStream('0x3') b = a[::1] assert a == b assert a[2:4:1] == '0b11' assert a[0:2:1] == '0b00' assert a[:3] == '0o1' a = BitStream('0x0011223344556677') assert a[-8:] == '0x77' assert a[:-24] == '0x0011223344' assert a[-1000:-24] == '0x0011223344' def test_interesting_slice_step(self): a = BitStream('0b0011000111') assert a[7:3:-1] == '0b1000' assert a[9:2:-1] == '0b1110001' assert a[8:2:-2] == '0b100' assert a[100:-20:-3] == '0b1010' assert a[100:-20:-1] == '0b1110001100' assert a[10:2:-1] == '0b1110001' assert a[100:2:-1] == '0b1110001' def test_insertion_order_and_bitpos(self): b = BitStream() b[0:0] = '0b0' b[0:0] = '0b1' assert b == '0b10' assert b.bitpos == 0 a = BitStream() a.insert('0b0') a.insert('0b1') assert a == '0b01' assert a.bitpos == 2 def test_overwrite_order_and_bitpos(self): a = BitStream('0xff') a.overwrite('0xa') assert a == '0xaf' assert a.bitpos == 4 a.overwrite('0xb') assert a == '0xab' assert a.bitpos == 8 a.overwrite('0xa', 4) assert a == '0xaa' assert a.bitpos == 8 a.overwrite(a, 0) assert a == '0xaa' def test_init_slice_with_int(self): a = BitStream(length=8) a[:] = 100 assert a.uint == 100 a[0] = 1 assert a.bin == '11100100' a[1] = 0 assert a.bin == '10100100' a[-1] = -1 assert a.bin == '10100101' a[-3:] = -2 assert a.bin == '10100110' def test_init_slice_with_int_errors(self): a = BitStream('0b0000') with pytest.raises(ValueError): a[0:4] = 16 with pytest.raises(ValueError): a[0:4] = -9 with pytest.raises(ValueError): a[0] = 2 with pytest.raises(ValueError): a[0] = -2 def test_reverse_with_slice(self): a = BitStream('0x0012ff') a.reverse() assert a == '0xff4800' a.reverse(8, 16) assert a == '0xff1200' b = a[8:16] b.reverse() a[8:16] = b assert a == '0xff4800' def test_reverse_with_slice_errors(self): a = BitStream('0x123') with pytest.raises(ValueError): a.reverse(-1, 4) with pytest.raises(ValueError): a.reverse(10, 9) with pytest.raises(ValueError): a.reverse(1, 10000) def test_initialise_from_list(self): a = BitStream([]) assert not a a = BitStream([True, False, [], [0], 'hello']) assert a == '0b10011' a += [] assert a == '0b10011' a += [True, False, True] assert a == '0b10011101' a.find([12, 23]) assert a.pos == 3 assert [1, 0, False, True] == BitStream('0b1001') a = [True] + BitStream('0b1') assert a == '0b11' def test_initialise_from_tuple(self): a = BitStream(()) assert not a a = BitStream((0, 1, '0', '1')) assert '0b0111' == a a.replace((True, True), []) assert a == (False, True) def test_cut(self): a = BitStream('0x00112233445') b = list(a.cut(8)) assert b == ['0x00', '0x11', '0x22', '0x33', '0x44', '0x5'] b = list(a.cut(4, 8, 16)) assert b == ['0x1', '0x1'] b = list(a.cut(4, 0, 44, 4)) assert b == ['0x0', '0x0', '0x1', '0x1'] a = BitStream() b = list(a.cut(10)) assert not b def test_cut_errors(self): a = BitStream('0b1') b = a.cut(1, 1, 2) with pytest.raises(ValueError): _ = next(b) b = a.cut(1, -2, 1) with pytest.raises(ValueError): _ = next(b) b = a.cut(0) with pytest.raises(ValueError): _ = next(b) b = a.cut(1, count=-1) with pytest.raises(ValueError): _ = next(b) def test_cut_problem(self): s = BitStream('0x1234') for n in list(s.cut(4)): s.prepend(n) assert s == '0x43211234' def test_join_functions(self): a = BitStream().join(['0xa', '0xb', '0b1111']) assert a == '0xabf' a = BitStream('0b1').join(['0b0' for _ in range(10)]) assert a == '0b0101010101010101010' a = BitStream('0xff').join([]) assert not a a = BitStream('0xff').join([Bits(5), '0xab', '0xabc']) assert a == '0b00000, 0xffabffabc' def test_adding_bitpos(self): a = BitStream('0xff') b = BitStream('0x00') a.bitpos = b.bitpos = 8 c = a + b assert c.bitpos == 0 def test_intelligent_read1(self): a = BitStream(uint=123, length=23) u = a.read('uint:23') assert u == 123 assert a.pos == a.len b = BitStream(int=-12, length=44) i = b.read('int:44') assert i == -12 assert b.pos == b.len u2, i2 = (a + b).readlist('uint:23, int:44') assert (u2, i2) == (123, -12) def test_intelligent_read2(self): a = BitStream(ue=822) u = a.read('ue') assert u == 822 assert a.pos == a.len b = BitStream(se=-1001) s = b.read('se') assert s == -1001 assert b.pos == b.len s, u1, u2 = (b + 2 * a).readlist('se, ue, ue') assert (s, u1, u2) == (-1001, 822, 822) def test_intelligent_read3(self): a = BitStream('0x123') + '0b11101' h = a.read('hex:12') assert h == '123' b = a.read(' bin : 5 ') assert b == '11101' c = '0b' + b + a b, h = c.readlist('bin:5, hex:12') assert (b, h) == ('11101', '123') def test_intelligent_read4(self): a = BitStream('0o007') o = a.read('oct:9') assert o == '007' assert a.pos == a.len def test_intelligent_read5(self): a = BitStream('0x00112233') c0, c1, c2 = a.readlist('bits:8, bits:8, bits:16') assert (c0, c1, c2) == (BitStream('0x00'), BitStream('0x11'), BitStream('0x2233')) a.pos = 0 c = a.read('bits:16') assert c == BitStream('0x0011') def test_intelligent_read6(self): a = BitStream('0b000111000') b1, b2, b3 = a.readlist('bin :3, int: 3, int:3') assert b1 == '000' assert b2 == -1 assert b3 == 0 def test_intelligent_read7(self): a = BitStream('0x1234') a1, a2, a3, a4 = a.readlist('bin:0, oct:0, hex:0, bits:0') assert a1 == a2 == a3 == '' assert not a4 with pytest.raises(ValueError): _ = a.read('int:0') with pytest.raises(ValueError): _ = a.read('uint:0') assert a.pos == 0 def test_intelligent_read8(self): a = BitStream('0x123456') for t in ['hex:1', 'oct:1', '-5', 'fred', 'bin:-2', 'uint:p', 'uint:-2', 'int:u', 'int:-3', 'ses', 'uee', '-14']: with pytest.raises(ValueError): _ = a.read(t) def test_intelligent_read9(self): a = BitStream('0xff') assert a.read('intle8') == -1 def test_intelligent_peek(self): a = BitStream('0b01, 0x43, 0o4, uint:23=2, se=5, ue=3') b, c, e = a.peeklist('bin:2, hex:8, oct:3') assert (b, c, e) == ('01', '43', '4') assert a.pos == 0 a.pos = 13 f, g, h = a.peeklist('uint:23, se, ue') assert (f, g, h) == (2, 5, 3) assert a.pos == 13 def test_read_multiple_bits(self): s = BitStream('0x123456789abcdef') a, b = s.readlist([4, 4]) assert a == '0x1' assert b == '0x2' c, d, e = s.readlist([8, 16, 8]) assert c == '0x34' assert d == '0x5678' assert e == '0x9a' def test_peek_multiple_bits(self): s = BitStream('0b1101, 0o721, 0x2234567') a, b, c, d = s.peeklist([2, 1, 1, 9]) assert a == '0b11' assert bool(b) == True assert bool(c) == True assert d == '0o721' assert s.pos == 0 a, b = s.peeklist([4, 9]) assert a == '0b1101' assert b == '0o721' s.pos = 13 a, b = s.peeklist([16, 8]) assert a == '0x2234' assert b == '0x56' assert s.pos == 13 def test_difficult_prepends(self): a = BitStream('0b1101011') b = BitStream() for i in range(10): b.prepend(a) assert b == a * 10 def test_packing_wrong_number_of_things(self): with pytest.raises(bitstring.CreationError): _ = pack('bin:1') with pytest.raises(bitstring.CreationError): _ = pack('', 100) def test_pack_with_various_keys(self): a = pack('uint10', uint10='0b1') assert a == '0b1' b = pack('0b110', **{'0b110': '0xfff'}) assert b == '0xfff' def test_pack_with_variable_length(self): for i in range(1, 11): a = pack('uint:n', 0, n=i) assert a.bin == '0' * i def test_to_bytes(self): a = BitStream(bytes=b'\xab\x00') b = a.tobytes() assert a.bytes == b for i in range(7): del a[-1:] assert a.tobytes() == b'\xab\x00' del a[-1:] assert a.tobytes() == b'\xab' def test_to_file(self): filename = os.path.join(THIS_DIR, 'temp_bitstring_unit_testing_file') a = BitStream('0x0000ff')[:17] with open(filename, 'wb') as f: a.tofile(f) b = BitStream(filename=filename) assert b == '0x000080' a = BitStream('int:1000000=-1') assert a.int == -1 with open(filename, 'wb') as f: a.tofile(f) b = BitStream(filename=filename) assert b.int == -1 assert b.len == 1000000 def test_token_parser(self): tp = bitstring.utils.tokenparser assert tp('hex') == (True, [('hex', None, None)]) assert tp('hex=14') == (True, [('hex', None, '14')]) assert tp('0xef') == (False, [('0x', None, 'ef')]) assert tp('uint:12') == (False, [('uint', 12, None)]) assert tp('int:30=-1') == (False, [('int', 30, '-1')]) assert tp('bits10') == (False, [('bits', 10, None)]) assert tp('bits:10') == (False, [('bits', 10, None)]) assert tp('123') == (False, [('bits', 123, None)]) assert tp('123') == (False, [('bits', 123, None)]) assert tp('hex12', ('hex12',)) == (False, [('hex12', None, None)]) assert tp('2*bits:6') == (False, [('bits', 6, None), ('bits', 6, None)]) def test_token_parser_struct_codes(self): tp = bitstring.utils.tokenparser assert tp('>H') == (False, [('uintbe', 16, None)]) assert tp('b') == (False, [('int', 8, None)]) assert tp('b', 23) == BitStream('intbe:8=23') assert pack('>B', 23) == BitStream('uintbe:8=23') assert pack('>h', 23) == BitStream('intbe:16=23') assert pack('>H', 23) == BitStream('uintbe:16=23') assert pack('>l', 23) == BitStream('intbe:32=23') assert pack('>L', 23) == BitStream('uintbe:32=23') assert pack('>q', 23) == BitStream('intbe:64=23') assert pack('>Q', 23) == BitStream('uintbe:64=23') with pytest.raises(bitstring.CreationError): _ = pack('2L', 40, 40) def test_struct_tokens3(self): s = pack('>hhl', 1, 2, 3) a, b, c = s.unpack('>hhl') assert (a, b, c) == (1, 2, 3) s = pack('Q \tL', 1001, 43, 21, 9999) assert s.unpack('QL') == [1001, 43, 21, 9999] def test_struct_tokens_multiplicative_factors(self): s = pack('<2h', 1, 2) a, b = s.unpack('<2h') assert (a, b) == (1, 2) s = pack('<100q', *range(100)) assert s.len == 100 * 64 assert s[44*64:45*64].uintle == 44 s = pack('@L0B2h', 5, 5, 5) assert s.unpack('@Lhh') == [5, 5, 5] def test_struct_tokens_errors(self): for f in ['>>q', '<>q', 'q>', '2q', 'q', '>-2q', '@a', '>int:8', '>q2']: with pytest.raises(bitstring.CreationError): _ = pack(f, 100) def test_immutable_bit_streams(self): a = ConstBitStream('0x012345') assert a == '0x012345' b = BitStream('0xf') + a assert b == '0xf012345' with pytest.raises(AttributeError): a.append(b) with pytest.raises(AttributeError): a.prepend(b) with pytest.raises(TypeError): a[0] = '0b1' with pytest.raises(TypeError): del a[5] with pytest.raises(AttributeError): a.replace('0b1', '0b0') with pytest.raises(AttributeError): a.insert('0b11', 4) with pytest.raises(AttributeError): a.reverse() with pytest.raises(AttributeError): a.reversebytes() assert a == '0x012345' assert isinstance(a, ConstBitStream) def test_reverse_bytes(self): a = BitStream('0x123456') a.byteswap() assert a == '0x563412' b = a + '0b1' b.byteswap() assert '0x123456, 0b1' == b a = BitStream('0x54') a.byteswap() assert a == '0x54' a = BitStream() a.byteswap() assert not a def test_reverse_bytes2(self): a = BitStream() a.byteswap() assert not a a = BitStream('0x00112233') a.byteswap(0, 0, 16) assert a == '0x11002233' a.byteswap(0, 4, 28) assert a == '0x12302103' a.byteswap(start=0, end=18) assert a == '0x30122103' with pytest.raises(ValueError): a.byteswap(0, 10, 2) with pytest.raises(ValueError): a.byteswap(0, -4, 4) with pytest.raises(ValueError): a.byteswap(0, 24, 48) a.byteswap(0, 24) assert a == '0x30122103' a.byteswap(0, 11, 11) assert a == '0x30122103' def test_capitals_in_pack(self): a = pack('A', A='0b1') assert a == '0b1' format_ = 'bits:4=BL_OFFT, uint:12=width, uint:12=height' d = {'BL_OFFT': '0b1011', 'width': 352, 'height': 288} s = bitstring.pack(format_, **d) assert s == '0b1011, uint:12=352, uint:12=288' a = pack('0X0, uint:8, hex', 45, '0XABcD') assert a == '0x0, uint:8=45, 0xabCD' def test_other_capitals(self): a = ConstBitStream('0XABC, 0O0, 0B11') assert a == 'hex=0Xabc, oct=0, bin=0B11' def test_efficient_overwrite(self): a = BitStream(100000000) a.overwrite([1], 123456) assert a[123456] == True a.overwrite('0xff', 1) assert a[0:32:1] == '0x7f800000' b = BitStream('0xffff') b.overwrite('0x0000') assert b == '0x0000' assert b.pos == 16 c = BitStream(length=1000) c.overwrite('0xaaaaaaaaaaaa', 81) assert c[81:81 + 6 * 8] == '0xaaaaaaaaaaaa' assert len(list(c.findall('0b1'))) == 24 s = BitStream(length=1000) s = s[5:] s.overwrite('0xffffff', 500) s.pos = 500 assert s.read(4 * 8) == '0xffffff00' s.overwrite('0xff', 502) assert s[502:518] == '0xffff' def test_peek_and_read_list_errors(self): a = BitStream('0x123456') with pytest.raises(ValueError): _ = a.read('hex:8, hex:8') with pytest.raises(ValueError): _ = a.peek('hex:8, hex:8') with pytest.raises(TypeError): _ = a.read(10, 12) with pytest.raises(TypeError): _ = a.peek(12, 14) with pytest.raises(TypeError): _ = a.read(8, 8) with pytest.raises(TypeError): _ = a.peek(80, 80) def test_startswith(self): a = BitStream() assert a.startswith(BitStream()) assert not a.startswith('0b0') a = BitStream('0x12ff') assert a.startswith('0x1') assert a.startswith('0b0001001') assert a.startswith('0x12ff') assert not a.startswith('0x12ff, 0b1') assert not a.startswith('0x2') def test_startswith_start_end(self): s = BitStream('0x123456') assert s.startswith('0x234', 4) assert not s.startswith('0x123', end=11) assert s.startswith('0x123', end=12) assert s.startswith('0x34', 8, 16) assert not s.startswith('0x34', 7, 16) assert not s.startswith('0x34', 9, 16) assert not s.startswith('0x34', 8, 15) def test_endswith(self): a = BitStream() assert a.endswith('') assert not a.endswith(BitStream('0b1')) a = BitStream('0xf2341') assert a.endswith('0x41') assert a.endswith('0b001') assert a.endswith('0xf2341') assert not a.endswith('0x1f2341') assert not a.endswith('0o34') def test_endswith_start_end(self): s = BitStream('0x123456') assert s.endswith('0x234', end=16) assert not s.endswith('0x456', start=13) assert s.endswith('0x456', start=12) assert s.endswith('0x34', 8, 16) assert s.endswith('0x34', 7, 16) assert not s.endswith('0x34', 9, 16) assert not s.endswith('0x34', 8, 15) def test_unhashability(self): s = BitStream('0xf') with pytest.raises(TypeError): _ = {s} with pytest.raises(TypeError): _ = hash([s]) def test_const_bit_stream_set_creation(self): sl = [ConstBitStream(uint=i, length=7) for i in range(15)] s = set(sl) assert len(s) == 15 s.add(ConstBitStream('0b0000011')) assert len(s) == 15 with pytest.raises(TypeError): s.add(BitStream('0b0000011')) def test_const_bit_stream_functions(self): s = ConstBitStream('0xf, 0b1') assert type(s) == ConstBitStream t = copy.copy(s) assert type(t) == ConstBitStream a = s + '0o3' assert type(a) == ConstBitStream b = a[0:4] assert type(b) == ConstBitStream b = a[4:3] assert type(b) == ConstBitStream b = a[5:2:-1] assert type(b) == ConstBitStream b = ~a assert type(b) == ConstBitStream b = a << 2 assert type(b) == ConstBitStream b = a >> 2 assert type(b) == ConstBitStream b = a * 2 assert type(b) == ConstBitStream b = a * 0 assert type(b) == ConstBitStream b = a & ~a assert type(b) == ConstBitStream b = a | ~a assert type(b) == ConstBitStream b = a ^ ~a assert type(b) == ConstBitStream b = a._slice(4, 4) assert type(b) == ConstBitStream b = a.read(4) assert type(b) == ConstBitStream def test_const_bit_stream_properties(self): a = ConstBitStream('0x123123') with pytest.raises(AttributeError): a.hex = '0x234' with pytest.raises(AttributeError): a.oct = '0o234' with pytest.raises(AttributeError): a.bin = '0b101' with pytest.raises(AttributeError): a.ue = 3453 with pytest.raises(AttributeError): a.se = -123 with pytest.raises(AttributeError): a.int = 432 with pytest.raises(AttributeError): a.uint = 4412 with pytest.raises(AttributeError): a.intle = 123 with pytest.raises(AttributeError): a.uintle = 4412 with pytest.raises(AttributeError): a.intbe = 123 with pytest.raises(AttributeError): a.uintbe = 4412 with pytest.raises(AttributeError): a.intne = 123 with pytest.raises(AttributeError): a.uintne = 4412 with pytest.raises(AttributeError): a.bytes = b'hello' def test_const_bit_stream_misc(self): a = ConstBitStream('0xf') b = a a += '0xe' assert b == '0xf' assert a == '0xfe' c = BitStream(a) assert a == c a = ConstBitStream('0b1') a += a assert a == '0b11' assert type(a) == ConstBitStream a._addleft(a) assert a == '0b1111' assert type(a) == ConstBitStream def test_const_bit_stream_hashibility(self): a = ConstBitStream('0x1') b = ConstBitStream('0x2') c = ConstBitStream('0x1') c.pos = 3 s = {a, b, c} assert len(s) == 2 assert hash(a) == hash(c) def test_const_hashability_again(self): a = ConstBitStream(uint=1 << 300, length=10000) b = ConstBitStream(uint=2 << 300, length=10000) c = ConstBitStream(uint=3 << 300, length=10000) s = {a, b, c} assert len(s) == 3 def test_hash_edge_cases(self): a = ConstBitStream('0xabcd') b = ConstBitStream('0xabcd') c = b[1:] assert hash(a) == hash(b) assert hash(a) != hash(c) def test_const_bit_stream_copy(self): a = ConstBitStream('0xabc') a.pos = 11 b = copy.copy(a) b.pos = 4 assert id(a._bitstore) == id(b._bitstore) assert a.pos == 11 assert b.pos == 4 def test_python26stuff(self): s = BitStream('0xff') assert isinstance(s.tobytes(), bytes) assert isinstance(s.bytes, bytes) def test_read_from_bits(self): a = ConstBitStream('0xaabbccdd') b = a.read(8) assert b == '0xaa' assert a[0:8] == '0xaa' assert a[-1] == True a.pos = 0 assert a.read(4).uint == 10 class TestSet: def test_set(self): a = BitStream(length=16) a.set(True, 0) assert a == '0b10000000 00000000' a.set(1, 15) assert a == '0b10000000 00000001' b = a[4:12] b.set(True, 1) assert b == '0b01000000' b.set(True, -1) assert b == '0b01000001' b.set(1, -8) assert b == '0b11000001' with pytest.raises(IndexError): b.set(True, -9) with pytest.raises(IndexError): b.set(True, 8) def test_set_negative_index(self): a = BitStream(10) a.set(1, -1) assert a.bin == '0000000001' a.set(1, [-1, -10]) assert a.bin == '1000000001' with pytest.raises(IndexError): a.set(1, [-11]) def test_file_based_set_unset(self): filename = os.path.join(THIS_DIR, 'test.m1v') a = BitStream(filename=filename) a.set(True, (0, 1, 2, 3, 4)) assert a[0:32] == '0xf80001b3' a = BitStream(filename=filename) a.set(False, (28, 29, 30, 31)) assert a.startswith('0x000001b0') def test_set_list(self): a = BitStream(length=18) a.set(True, range(18)) assert a.int == -1 a.set(False, range(18)) assert a.int == 0 def test_unset(self): a = BitStream(length=16, int=-1) a.set(False, 0) assert ~a == '0b10000000 00000000' a.set(0, 15) assert ~a == '0b10000000 00000001' b = a[4:12] b.set(False, 1) assert ~b == '0b01000000' b.set(False, -1) assert ~b == '0b01000001' b.set(False, -8) assert ~b == '0b11000001' with pytest.raises(IndexError): b.set(False, -9) with pytest.raises(IndexError): b.set(False, 8) def test_set_whole_bit_stream(self): a = BitStream(10000) a.set(1) assert a.all(1) a.set(0) assert a.all(0) class TestInvert: def test_invert_bits(self): a = BitStream('0b111000') a.invert(range(a.len)) assert a == '0b000111' a.invert([0, 1, -1]) assert a == '0b110110' def test_invert_whole_bit_stream(self): a = BitStream('0b11011') a.invert() assert a == '0b00100' def test_invert_single_bit(self): a = BitStream('0b000001') a.invert(0) assert a.bin == '100001' a.invert(-1) assert a.bin == '100000' def test_invert_errors(self): a = BitStream(10) with pytest.raises(IndexError): a.invert(10) with pytest.raises(IndexError): a.invert(-11) with pytest.raises(IndexError): a.invert([1, 2, 10]) def test_ior(self): a = BitStream('0b1101001') a |= '0b1110000' assert a == '0b1111001' b = a[2:] c = a[1:-1] b |= c assert c == '0b11100' assert b == '0b11101' def test_iand(self): a = BitStream('0b0101010101000') a &= '0b1111110000000' assert a == '0b0101010000000' s = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v'), offset=26, length=24) s &= '0xff00ff' assert s == '0xcc0004' def test_ixor(self): a = BitStream('0b11001100110011') a ^= '0b11111100000010' assert a == '0b00110000110001' def test_logical_inplace_errors(self): a = BitStream(4) with pytest.raises(ValueError): a |= '0b111' with pytest.raises(ValueError): a &= '0b111' with pytest.raises(ValueError): a ^= '0b111' class TestAllAndAny: def test_all(self): a = BitStream('0b0111') assert a.all(True, (1, 3)) assert not a.all(True, (0, 1, 2)) assert a.all(True, [-1]) assert not a.all(True, [0]) def test_file_based_all(self): filename = os.path.join(THIS_DIR, 'test.m1v') a = BitStream(filename=filename) assert a.all(True, [31]) a = BitStream(filename=filename) assert a.all(False, (0, 1, 2, 3, 4)) def test_file_based_any(self): filename = os.path.join(THIS_DIR, 'test.m1v') a = BitStream(filename=filename) assert a.any(True, (31, 12)) a = BitStream(filename=filename) assert a.any(False, (0, 1, 2, 3, 4)) b = ConstBitStream(filename=filename, offset=16) assert b.startswith('0x01') assert not b.any(True, range(0, 7)) assert b.any(True, range(0, 8)) assert b.any(True) def test_any(self): a = BitStream('0b10011011') assert a.any(True, (1, 2, 3, 5)) assert not a.any(True, (1, 2, 5)) assert a.any(True, (-1,)) assert not a.any(True, (1,)) def test_all_false(self): a = BitStream('0b0010011101') assert a.all(False, (0, 1, 3, 4)) assert not a.all(False, (0, 1, 2, 3, 4)) def test_any_false(self): a = BitStream('0b01001110110111111111111111111') assert a.any(False, (4, 5, 6, 2)) assert not a.any(False, (1, 15, 20)) def test_any_empty_bitstring(self): a = ConstBitStream() assert not a.any(True) assert not a.any(False) def test_all_empty_bit_stream(self): a = ConstBitStream() assert a.all(True) assert a.all(False) def test_any_whole_bitstring(self): a = ConstBitStream('0xfff') assert a.any(True) assert not a.any(False) def test_all_whole_bitstring(self): a = ConstBitStream('0xfff') assert a.all(True) assert not a.all(False) def test_errors(self): a = BitStream('0xf') with pytest.raises(IndexError): a.all(True, [5]) with pytest.raises(IndexError): a.all(True, [-5]) with pytest.raises(IndexError): a.any(True, [5]) with pytest.raises(IndexError): a.any(True, [-5]) ################### def test_float_initialisation(self): for f in (0.000001, -1.0, 1.0, 0.2, -3.14159265): a = BitStream(float=f, length=64) a.pos = 6 assert a.float == f a = BitStream('float:64=%s' % str(f)) a.pos = 6 assert a.float == f a = BitStream('floatbe:64=%s' % str(f)) a.pos = 6 assert a.floatbe == f a = BitStream('floatle:64=%s' % str(f)) a.pos = 6 assert a.floatle == f a = BitStream('floatne:64=%s' % str(f)) a.pos = 6 assert a.floatne == f b = BitStream(float=f, length=32) b.pos = 6 assert b.float / f == pytest.approx(1.0) b = BitStream('float:32=%s' % str(f)) b.pos = 6 assert b.float / f == pytest.approx(1.0) b = BitStream('floatbe:32=%s' % str(f)) b.pos = 6 assert b.floatbe / f == pytest.approx(1.0) b = BitStream('floatle:32=%s' % str(f)) b.pos = 6 assert b.floatle / f == pytest.approx(1.0) b = BitStream('floatne:32=%s' % str(f)) b.pos = 6 assert b.floatne / f == pytest.approx(1.0) a = BitStream(float=f, length=16) a.pos = 6 assert a.float == pytest.approx(f, abs=0.01) a = BitStream('float:16=%s' % str(f)) a.pos = 6 assert a.float == pytest.approx(f, abs=0.01) a = BitStream('floatbe:16=%s' % str(f)) a.pos = 6 assert a.floatbe == pytest.approx(f, abs=0.01) a = BitStream('floatle:16=%s' % str(f)) a.pos = 6 assert a.floatle == pytest.approx(f, abs=0.01) a = BitStream('floatne:16=%s' % str(f)) a.pos = 6 assert a.floatne == pytest.approx(f, abs=0.01) a = BitStream('0x12345678') a.pos = 6 a.f = 23 assert a.f == 23.0 def test_float_init_strings(self): for s in ('5', '+0.0001', '-1e101', '4.', '.2', '-.65', '43.21E+32'): a = BitStream('float:64=%s' % s) assert a.float == float(s) for s in ('5', '+0.5', '-1e2', '4.', '.25', '-.75'): a = BitStream('float:16=%s' % s) assert a.f == float(s) def test_float_packing(self): a = pack('>d', 0.01) assert a.float == 0.01 assert a.floatbe == 0.01 a.byteswap() assert a.floatle == 0.01 b = pack('>f', 1e10) assert b.float / 1e10 == pytest.approx(1.0) c = pack('5d', 10.0, 5.0, 2.5, 1.25, 0.1) assert d.unpack('>5d') == [10.0, 5.0, 2.5, 1.25, 0.1] e = pack('>3e', -100, 100, 0.25) assert e.unpack('>3e') == [-100.0, 100.0, 0.25] def test_float_reading(self): a = BitStream('floatle:64=12, floatbe:64=-0.01, floatne:64=3e33') x, y, z = a.readlist('floatle:64, floatbe:64, floatne:64') assert x == 12.0 assert y == -0.01 assert z == 3e33 a = BitStream('floatle:16=12, floatbe:32=-0.01, floatne:32=3e33') x, y, z = a.readlist('floatle:16, floatbe:32, floatne:32') assert x / 12.0 == pytest.approx(1.0) assert y / -0.01 == pytest.approx(1.0) assert z / 3e33 == pytest.approx(1.0) a = BitStream('0b11, floatle:64=12, 0xfffff') a.pos = 2 floatle64 = Dtype('floatle64') assert a.read(floatle64) == 12.0 b = BitStream(floatle=20, length=32) b.floatle = 10.0 b = [0] + b assert b[1:].floatle == 10.0 def test_non_aligned_float_reading(self): s = BitStream('0b1, float:32 = 10.0') x, y = s.readlist('1, float:32') assert y == 10.0 s[1:] = 'floatle:32=20.0' x, y = s.unpack('1, floatle:32') assert y == 20.0 def test_float_errors(self): a = BitStream('0x3') with pytest.raises(bitstring.InterpretError): _ = a.float with pytest.raises(bitstring.CreationError): a.float = -0.2 for le in (8, 10, 12, 18, 30, 128, 200): with pytest.raises(ValueError): _ = BitStream(float=1.0, length=le) with pytest.raises(bitstring.CreationError): _ = BitStream(floatle=0.3, length=0) with pytest.raises(bitstring.CreationError): _ = BitStream(floatle=0.3, length=1) with pytest.raises(bitstring.CreationError): _ = BitStream(float=2) with pytest.raises(bitstring.InterpretError): _ = a.read('floatle:2') def test_read_error_changes_pos(self): a = BitStream('0x123123') with pytest.raises(ValueError): a.read('10, 5') def test_ror(self): a = BitStream('0b11001') a.ror(0) assert a == '0b11001' a.ror(1) assert a == '0b11100' a.ror(5) assert a == '0b11100' a.ror(101) assert a == '0b01110' a = BitStream('0b1') a.ror(1000000) assert a == '0b1' def test_ror_errors(self): a = BitStream() with pytest.raises(bitstring.Error): a.ror(0) a += '0b001' with pytest.raises(ValueError): a.ror(-1) def test_rol(self): a = BitStream('0b11001') a.rol(0) assert a == '0b11001' a.rol(1) assert a == '0b10011' a.rol(5) assert a == '0b10011' a.rol(101) assert a == '0b00111' a = BitStream('0b1') a.rol(1000000) assert a == '0b1' def test_rol_from_file(self): a = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) m = a.len a.rol(1) assert a.startswith('0x000003') assert a.len == m assert a.endswith('0x0036e') def test_ror_from_file(self): a = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) m = a.len a.ror(1) assert a.startswith('0x800000') assert a.len == m assert a.endswith('0x000db') def test_rol_errors(self): a = BitStream() with pytest.raises(bitstring.Error): a.rol(0) a += '0b001' with pytest.raises(ValueError): a.rol(-1) def test_bytes_token(self): a = BitStream('0x510203') b = a.read('bytes:1') assert isinstance(b, bytes) assert b == b'\x51' x, y, z = a.unpack('uint:4, bytes:2, uint') assert x == 5 assert y == b'\x10\x20' assert z == 3 s = pack('bytes:4', b'abcd') assert s.bytes == b'abcd' def test_bytes_token_more_thoroughly(self): a = BitStream('0x0123456789abcdef') a.pos += 16 assert a.read('bytes:1') == b'\x45' assert a.read('bytes:3') == b'\x67\x89\xab' x, y, z = a.unpack('bits:28, bytes, bits:12') assert y == b'\x78\x9a\xbc' def test_dedicated_read_functions(self): a = BitStream('0b11, uint:43=98798798172, 0b11111') x = a[2:45].uint assert x == 98798798172 assert a.pos == 0 a.pos = 2 x = a.read(Dtype('int43')) assert x == 98798798172 assert a.pos == 45 a = BitStream('0b11, uintbe:48=98798798172, 0b11111') a.pos = 2 x = a.read(Dtype('uintbe48')) assert x == 98798798172 assert a.pos == 50 a = BitStream('0b111, uintle:40=123516, 0b111') a.pos = 3 assert a.read('uintle:40') == 123516 b = BitStream('0xff, uintle:800=999, 0xffff') assert b[8:800].uintle == 999 a = BitStream('0b111, intle:48=999999999, 0b111111111111') a.pos = 3 assert a.read('intle48') == 999999999 b = BitStream('0xff, intle:200=918019283740918263512351235, 0xfffffff') b.pos = 8 assert b.read(Dtype('intle', length=200)) == 918019283740918263512351235 a = BitStream('0b111, bfloat:16=-5.25, 0xffffffff') a.pos = 3 assert a.read('bfloatbe') == -5.25 a = BitStream('0b111, floatle:64=9.9998, 0b111') a.pos = 3 assert a.read('floatle64') == 9.9998 def test_auto_init_with_int(self): a = BitStream(0) assert not a a = BitStream(1) assert a == '0b0' a = BitStream(1007) assert a == BitStream(length=1007) with pytest.raises(bitstring.CreationError): _ = BitStream(-1) assert ConstBitStream(13) == Bits(13) with pytest.raises(TypeError): a += 10 def test_reading_problems(self): a = BitStream('0x000001') b = a.read('uint:24') assert b == 1 a.pos = 0 with pytest.raises(bitstring.ReadError): _ = a.read('bytes:4') @pytest.mark.skip("Bug #266") def test_pos_reset_bug(self): a = BitStream('0x0120310230123', pos=23) assert a.pos == 23 a.u8 = 14 assert a.pos == 0 a.pos = 5 a.u8 = 9 assert a.pos == 0 def test_creation_exception_bug(self): with pytest.raises(ValueError): _ = BitStream(bin=1) def test_add_verses_in_place_add(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 += '0xdef' assert a1 == '0xabcdef' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 + '0x0' a2 += '0xdef' assert a2 == '0xabcdef' assert b2 == '0xabcdef' assert c2 == '0xabc0' def test_and_verses_in_place_and(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 &= '0xf0f' assert a1 == '0xa0c' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 & '0x00f' a2 &= '0xf0f' assert a2 == '0xa0c' assert b2 == '0xa0c' assert c2 == '0x00c' def test_or_verses_in_place_or(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 |= '0xf0f' assert a1 == '0xfbf' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 | '0x00f' a2 |= '0xf0f' assert a2 == '0xfbf' assert b2 == '0xfbf' assert c2 == '0xabf' def test_xor_verses_in_place_xor(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 ^= '0xf0f' assert a1 == '0x5b3' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 ^ '0x00f' a2 ^= '0xf0f' assert a2 == '0x5b3' assert b2 == '0x5b3' assert c2 == '0xab3' def test_mul_verses_in_place_mul(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 *= 3 assert a1 == '0xabcabcabc' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 * 2 a2 *= 3 assert a2 == '0xabcabcabc' assert b2 == '0xabcabcabc' assert c2 == '0xabcabc' def test_lshift_verses_in_place_lshift(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 <<= 4 assert a1 == '0xbc0' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 << 8 a2 <<= 4 assert a2 == '0xbc0' assert b2 == '0xbc0' assert c2 == '0xc00' def test_rshift_verses_in_place_rshift(self): a1 = ConstBitStream('0xabc') b1 = a1 a1 >>= 4 assert a1 == '0x0ab' assert b1 == '0xabc' a2 = BitStream('0xabc') b2 = a2 c2 = a2 >> 8 a2 >>= 4 assert a2 == '0x0ab' assert b2 == '0x0ab' assert c2 == '0x00a' def test_auto_from_bool(self): with pytest.raises(TypeError): a = ConstBitStream() + True + False + True class TestBugs: def test_bug_in_replace(self): s = BitStream('0x00112233') li = list(s.split('0x22', start=8, bytealigned=True)) assert li == ['0x11', '0x2233'] s = BitStream('0x00112233') s.replace('0x22', '0xffff', start=8, bytealigned=True) assert s == '0x0011ffff33' s = BitStream('0x0123412341234') s.replace('0x23', '0xf', start=9, bytealigned=True) assert s == '0x012341f41f4' def test_truncateleft_bug(self): a = BitStream('0b000000111')[2:] a._truncateleft(6) assert a == '0b1' def test_null_bits(self): s = ConstBitStream(bin='') t = ConstBitStream(oct='') u = ConstBitStream(hex='') v = ConstBitStream(bytes=b'') assert not s assert not t assert not u assert not v def test_multiplicative_factors_creation(self): s = BitStream('1*0b1') assert s == '0b1' s = BitStream('4*0xc') assert s == '0xcccc' s = BitStream('0b1, 0*0b0') assert s == '0b1' s = BitStream('0b1, 3*uint:8=34, 2*0o755') assert s == '0b1, uint:8=34, uint:8=34, uint:8=34, 0o755755' s = BitStream('0*0b1001010') assert not s def test_multiplicative_factors_reading(self): s = BitStream('0xc') * 5 a, b, c, d, e = s.readlist('5*uint:4') assert a == b == c == d == e == 12 s = ConstBitStream('2*0b101, 4*uint:7=3') a, b, c, d, e = s.readlist('2*bin:3, 3*uint:7') assert a == b == '101' assert c == d == e == 3 def test_multiplicative_factors_packing(self): s = pack('3*bin', '1', '001', '101') assert s == '0b1001101' s = pack('hex, 2*se=-56, 3*uint:37', '34', 1, 2, 3) a, b, c, d, e, f = s.unpack('hex:8, 2*se, 3*uint:37') assert a == '34' assert b == -56 assert c == -56 assert (d, e, f) == (1, 2, 3) def test_multiplicative_factors_unpacking(self): s = ConstBitStream('0b10111') a, b, c, d = s.unpack('3*bool, bin') assert (a, b, c) == (True, False, True) assert d == '11' def test_packing_default_int_with_keyword(self): s = pack('uint:12', 100) assert s.unpack('12')[0].uint == 100 s = pack('int:oh_no_not_the_eyes=33', oh_no_not_the_eyes=17) assert s.int == 33 assert s.len == 17 def test_init_from_iterable(self): assert isinstance(range(10), collections.abc.Iterable) s = ConstBitStream(range(12)) assert s == '0x7ff' def test_function_negative_indices(self): # insert s = BitStream('0b0111') s.insert('0b0', -1) assert s == '0b01101' with pytest.raises(ValueError): s.insert('0b0', -1000) # reverse s.reverse(-2) assert s == '0b01110' t = BitStream('0x778899abcdef') t.reverse(-12, -4) assert t == '0x778899abc7bf' # reversebytes t.byteswap(0, -40, -16) assert t == '0x77ab9988c7bf' # overwrite t.overwrite('0x666', -20) assert t == '0x77ab998666bf' # find found = t.find('0x998', bytealigned=True, start=-31) assert not found found = t.find('0x998', bytealigned=True, start=-32) assert found assert t.pos == 16 t.pos = 0 found = t.find('0x988', bytealigned=True, end=-21) assert not found found = t.find('0x998', bytealigned=True, end=-20) assert found assert t.pos == 16 # findall s = BitStream('0x1234151f') li = list(s.findall('0x1', bytealigned=True, start=-15)) assert li == [24] li = list(s.findall('0x1', bytealigned=True, start=-16)) assert li == [16, 24] li = list(s.findall('0x1', bytealigned=True, end=-5)) assert li == [0, 16] li = list(s.findall('0x1', bytealigned=True, end=-4)) assert li == [0, 16, 24] # rfind found = s.rfind('0x1f', end=-1) assert not found found = s.rfind('0x12', start=-31) assert not found # cut s = BitStream('0x12345') li = list(s.cut(4, start=-12, end=-4)) assert li == ['0x3', '0x4'] # split s = BitStream('0xfe0012fe1200fe') li = list(s.split('0xfe', bytealigned=True, end=-1)) assert li == ['', '0xfe0012', '0xfe1200f, 0b111'] li = list(s.split('0xfe', bytealigned=True, start=-8)) assert li == ['', '0xfe'] # startswith assert s.startswith('0x00f', start=-16) assert s.startswith('0xfe00', end=-40) assert not s.startswith('0xfe00', end=-41) # endswith assert s.endswith('0x00fe', start=-16) assert not s.endswith('0x00fe', start=-15) assert not s.endswith('0x00fe', end=-1) assert s.endswith('0x00f', end=-4) # replace s.replace('0xfe', '', end=-1) assert s == '0x00121200fe' s.replace('0x00', '', start=-24) assert s == '0x001212fe' def test_rotate_start_and_end(self): a = BitStream('0b110100001') a.rol(1, 3, 6) assert a == '0b110001001' a.ror(1, start=-4) assert a == '0b110001100' a.rol(202, end=-5) assert a == '0b001101100' a.ror(3, end=4) assert a == '0b011001100' with pytest.raises(ValueError): a.rol(5, start=-4, end=-6) def test_byte_swap_int(self): s = pack('5*uintle:16', *range(10, 15)) assert list(range(10, 15)) == s.unpack('5*uintle:16') swaps = s.byteswap(2) assert list(range(10, 15)) == s.unpack('5*uintbe:16') assert swaps == 5 s = BitStream('0xf234567f') swaps = s.byteswap(1, start=4) assert swaps == 3 assert s == '0xf234567f' s.byteswap(2, start=4) assert s == '0xf452367f' s.byteswap(2, start=4, end=-4) assert s == '0xf234567f' s.byteswap(3) assert s == '0x5634f27f' s.byteswap(2, repeat=False) assert s == '0x3456f27f' swaps = s.byteswap(5) assert swaps == 0 swaps = s.byteswap(4, repeat=False) assert swaps == 1 assert s == '0x7ff25634' def test_byte_swap_pack_code(self): s = BitStream('0x0011223344556677') swaps = s.byteswap('b') assert s == '0x0011223344556677' assert swaps == 8 swaps = s.byteswap('>3h', repeat=False) assert s == '0x1100332255446677' assert swaps == 1 def test_byte_swap_iterable(self): s = BitStream('0x0011223344556677') swaps = s.byteswap(range(1, 4), repeat=False) assert swaps == 1 assert s == '0x0022115544336677' swaps = s.byteswap([2], start=8) assert s == '0x0011224455663377' assert 3 == swaps swaps = s.byteswap([2, 3], start=4) assert swaps == 1 assert s == '0x0120156452463377' def test_byte_swap_errors(self): s = BitStream('0x0011223344556677') with pytest.raises(ValueError): s.byteswap('z') with pytest.raises(ValueError): s.byteswap(-1) with pytest.raises(ValueError): s.byteswap([-1]) with pytest.raises(ValueError): s.byteswap([1, 'e']) with pytest.raises(ValueError): s.byteswap('!h') with pytest.raises(ValueError): s.byteswap(2, start=-1000) with pytest.raises(TypeError): s.byteswap(5.4) def test_byte_swap_from_file(self): s = BitStream(filename=os.path.join(THIS_DIR, 'smalltestfile')) swaps = s.byteswap('2bh') assert s == '0x0123674589abefcd' assert swaps == 2 def test_bracket_expander(self): be = bitstring.utils.expand_brackets assert be('hello') == 'hello' assert be('(hello)') == 'hello' assert be('1*(hello)') == 'hello' assert be('2*(hello)') == 'hello,hello' assert be('1*(a,b)') == 'a,b' assert be('2*(a,b)') == 'a,b,a,b' assert be('2*(a),3*(b)') == 'a,a,b,b,b' assert be('2*(a,b,3*(c,d),e)') == 'a,b,c,d,c,d,c,d,e,a,b,c,d,c,d,c,d,e' with pytest.raises(ValueError): _ = be('2*(x,y()') def test_bracket_tokens(self): s = BitStream('3*(0x0, 0b1)') assert s == '0x0, 0b1, 0x0, 0b1, 0x0, 0b1' s = pack('2*(uint:12, 3*(uint:7, uint:6))', *range(3, 17)) a = s.unpack('12, 7, 6, 7, 6, 7, 6, 12, 7, 6, 7, 6, 7, 6') assert [x.uint for x in a] == list(range(3, 17)) b = s.unpack('2*(12,3*(7,6))') assert a == b def test_pack_code_dicts(self): assert sorted(bitstring.utils.REPLACEMENTS_BE.keys()) == \ sorted(bitstring.utils.REPLACEMENTS_LE.keys()) assert sorted(bitstring.utils.REPLACEMENTS_BE.keys()) == \ sorted(bitstring.utils.PACK_CODE_SIZE.keys()) for key in bitstring.utils.PACK_CODE_SIZE: be = pack(bitstring.utils.REPLACEMENTS_BE[key], 0) le = pack(bitstring.utils.REPLACEMENTS_LE[key], 0) assert be.len == bitstring.utils.PACK_CODE_SIZE[key] * 8 assert le.len == be.len def test_unicode(self): a = ConstBitStream(u'uint:12=34') assert a.uint == 34 a += u'0xfe' assert a[12:] == '0xfe' a = BitStream('0x1122') c = a.byteswap(u'h') assert c == 1 assert a == u'0x2211' class TestUnpackWithDict: def test_length_keywords(self): a = ConstBitStream('2*int:13=100, 0b111') x, y, z = a.unpack('13, int:m, bin:q', m=13, q=3) assert x == 'uint:13=100' assert y == 100 assert z == '111' def test_length_keywords_with_stretch(self): a = ConstBitStream('0xff, 0b000, 0xf') x, y, z = a.unpack('hex:a, bin, hex:b', a=8, b=4) assert y == '000' def test_unused_keyword(self): a = ConstBitStream('0b110') x, = a.unpack('bin:3', notused=33) assert x == '110' def test_length_keyword_errors(self): a = pack('uint:p=33', p=12) with pytest.raises(ValueError): a.unpack('uint:p') with pytest.raises(ValueError): a.unpack('uint:p', p='a_string') class TestReadWithDict: def test_length_keywords(self): s = BitStream('0x0102') x, y = s.readlist('bits8, hex:b', b=4) assert (x, y) == ('0x01', '0') assert s.pos == 12 def test_bytes_keyword_problem(self): s = BitStream('0x01') x, = s.unpack('bytes:a', a=1) assert x == b'\x01' s = BitStream('0x000ff00a') x, y, z = s.unpack('12, bytes:x, bits', x=2) assert (x.int, y, z) == (0, b'\xff\x00', '0xa') class TestPeekWithDict: def test_length_keywords(self): s = BitStream('0x0102') x, y = s.peeklist('8, hex:b', b=4) assert (x, y) == ('0x01', '0') assert s.pos == 0 class TestBoolToken: def test_interpretation(self): a = ConstBitStream('0b1') assert a.bool == True assert a.read('bool') == True assert a.unpack('bool')[0] == True b = ConstBitStream('0b0') assert b.bool == False assert b.peek('bool') == False assert b.unpack('bool')[0] == False def test_pack(self): a = pack('bool=True') b = pack('bool=False') assert a.bool == True assert b.bool == False c = pack('4*bool', False, True, 'False', 'True') assert c == '0b0101' def test_assignment(self): a = BitStream() a.bool = True assert a.bool == True a.hex = 'ee' a.bool = False assert a.bool == False a.bool = 'False' assert a.bool == False a.bool = 'True' assert a.bool == True a.bool = 0 assert a.bool == False a.bool = 1 assert a.bool == True def test_errors(self): with pytest.raises(bitstring.CreationError): pack('bool', 'hello') with pytest.raises(bitstring.CreationError): pack('bool=true') with pytest.raises(bitstring.CreationError): pack('True') with pytest.raises(bitstring.CreationError): pack('bool', 2) with pytest.raises(bitstring.CreationError): _ = pack('bool', 'hello') with pytest.raises(bitstring.CreationError): _ = pack('bool=true') with pytest.raises(bitstring.CreationError): _ = pack('True') with pytest.raises(bitstring.CreationError): _ = pack('bool', 2) a = BitStream('0b11') with pytest.raises(bitstring.InterpretError): _ = a.bool b = BitStream() with pytest.raises(bitstring.InterpretError): _ = b.bool with pytest.raises(bitstring.CreationError): b.bool = 'false' def test_length_with_bool_read(self): a = ConstBitStream('0xf') with pytest.raises(ValueError): _ = a.read('bool:0') with pytest.raises(ValueError): _ = a.read('bool:2') class TestReadWithIntegers: def test_read_int(self): a = ConstBitStream('0xffeedd') b = a.read(8) assert b.hex == 'ff' assert a.pos == 8 b = a.peek(8) assert b.hex == 'ee' assert a.pos == 8 b = a.peek(1) assert b == '0b1' b = a.read(1) assert b == '0b1' def test_read_int_list(self): a = ConstBitStream('0xab, 0b110') b, c = a.readlist([8, 3]) assert b.hex == 'ab' assert c.bin == '110' # class FileReadingStrategy(unittest.TestCase): # # # def testBitStreamIsAlwaysRead(self): # filename = os.path.join(THIS_DIR, 'smalltestfile') # a = BitStream(filename=filename) # self.assertTrue(isinstance(a._datastore, bitstring.ByteStore)) # with open(filename, 'rb') as f: # b = BitStream(f) # self.assertTrue(isinstance(b._datastore, bitstring.ByteStore)) # # # def testBitsIsNeverRead(self): # filename = os.path.join(THIS_DIR, 'smalltestfile') # a = ConstBitStream(filename=filename) # self.assertTrue(isinstance(a._datastore.rawarray, bitstring.MmapByteArray)) # with open(filename, 'rb') as f: # b = ConstBitStream(f) # self.assertTrue(isinstance(b._datastore.rawarray, bitstring.MmapByteArray)) class TestCount: def test_count(self): a = ConstBitStream('0xf0f') assert a.count(True) == 8 assert a.count(False) == 4 b = BitStream() assert b.count(True) == 0 assert b.count(False) == 0 def test_count_with_offset_data(self): a = ConstBitStream('0xff0120ff') b = a[1:-1] assert b.count(1) == 16 assert b.count(0) == 14 class TestZeroBitReads: def test_integer(self): a = ConstBitStream('0x123456') with pytest.raises(bitstring.InterpretError): _ = a.read('uint:0') with pytest.raises(bitstring.InterpretError): _ = a.read('float:0') class TestInitialiseFromBytes: def test_bytes_behaviour(self): a = ConstBitStream(b'uint:5=2') b = ConstBitStream(b'') c = ConstBitStream(bytes=b'uint:5=2') assert a.bytes == b'uint:5=2' assert not b assert c == b'uint:5=2' def test_bytearray_behaviour(self): a = ConstBitStream(bytearray(b'uint:5=2')) b = ConstBitStream(bytearray(4)) c = ConstBitStream(bytes=bytearray(b'uint:5=2')) assert a.bytes == b'uint:5=2' assert b == '0x00000000' assert c.bytes == b'uint:5=2' class TestCoverageCompletion: def test_ue_read_error(self): s = ConstBitStream('0b000000001') with pytest.raises(bitstring.ReadError): _ = s.read('ue') def test_overwrite_with_self(self): s = BitStream('0b1101') s.overwrite(s) assert s == '0b1101' class TestSubclassing: def test_is_instance(self): b = BitStream() assert isinstance(b, BitStream) class SubBits(BitStream): pass a = SubBits() assert isinstance(a, SubBits) def test_class_type(self): class SubBits(BitStream): pass assert SubBits().__class__ == SubBits class TestBytesProblems: def test_offset_but_no_length(self): b = BitStream(bytes=b'\x00\xaa', offset=8) assert b.hex == 'aa' b = BitStream(bytes=b'\x00\xaa', offset=4) assert b.hex == '0aa' def test_invert(self): b = BitStream(bytes=b'\x00\xaa', offset=8, length=8) assert b.hex == 'aa' b.invert() assert b.hex == '55' def test_prepend(self): b = BitStream(bytes=b'\xaa\xbb', offset=8, length=4) assert b.hex == 'b' b.prepend('0xe') assert b.hex == 'eb' b = BitStream(bytes=b'\x00\xaa', offset=8, length=8) b.prepend('0xee') assert b.hex == 'eeaa' def test_byte_swap(self): b = BitStream(bytes=b'\x01\x02\x03\x04', offset=8) b.byteswap() assert b == '0x040302' def test_bin_property(self): b = BitStream(bytes=b'\x00\xaa', offset=8, length=4) assert b.bin == '1010' class TestLsb0Streaming: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_simple_bit_positions(self): s = BitStream('0x00000f') assert s.pos == 0 v = s.read('uint:8') assert v == 15 assert s.pos == 8 v = s.read(10) assert v == Bits(10) assert s.pos == 18 def test_bit_pos_after_find(self): s = BitStream('0b01100001000011 0000') s.find('0b11', start=1) assert s.pos == 4 def test_iter(self): s = BitStream('0b11000') assert list(s) == [False, False, False, True, True] def test_bit_pos_after_rfind(self): s = BitStream('0b011 000010000110000') s.rfind('0b11') assert s.pos == 15 def test_bit_pos_after_findall(self): pass def test_bit_pos_after_insert(self): pass def test_bit_pos_after_overwrite(self): pass def test_bit_pos_after_replace(self): pass def test_read_list(self): a = BitStream('0x0123456789abcdef') vals = a.readlist('uint:4, uint:4, uint:24, uint:12, uint:12, uint:8') assert vals == [15, 14, 0x89abcd, 0x567, 0x234, 1] class TestLsb0PackingUnpacking: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_bin(self): lsb0 = bitstring.pack('2*b4', '0b0000', '1111') assert lsb0 == '0b11110000' a, b = lsb0.unpack('2*h4') assert [a, b] == ['0', 'f'] a, b = lsb0.unpack('2*bits4') assert [a, b] == ['0x0', '0xf'] a, b = lsb0.unpack('2*bin4') assert [a, b] == ['0000', '1111'] def test_float(self): lsb0 = bitstring.pack('2*bfloat', 0.5, 15) assert lsb0 == '0x4170 3f00' a, b = lsb0.unpack('2*bfloat') assert [a, b] == [0.5, 15] def test_simplest(self): lsb0 = bitstring.pack('uint:2', 1) assert lsb0.unpack('uint:2') == [1] lsb0 = bitstring.pack('0xab, 0xc') assert lsb0.unpack('hex8, hex4') == ['ab', 'c'] def test_slightly_harder(self): lsb0 = bitstring.pack('float:32, hex', 0.25, 'ac') x = lsb0.unpack('float:32, hex') assert x == [0.25, 'ac'] def test_more_complex(self): lsb0 = bitstring.pack('uint:10, hex, int:13, 0b11', 130, '3d', -23) x = lsb0.unpack('uint:10, hex, int:13, bin:2') assert x == [130, '3d', -23, '11'] def test_golomb_codes(self): v = [10, 8, 6, 4, 100, -9] # Exp-Golomb codes can only be read in msb0 mode. So also doesn't # make sense for creation with pack with pytest.raises(bitstring.CreationError): _ = bitstring.pack('5*ue, sie', *v) # with self.assertRaises(bitstring.CreationError): # _ = BitStream('ue=34') lsb0 = BitStream('0b0010010') with pytest.raises(bitstring.ReadError): _ = lsb0.unpack('5*ue, sie') with pytest.raises(bitstring.ReadError): _ = lsb0.read('ue') with pytest.raises(bitstring.ReadError): _ = lsb0.read('uie') with pytest.raises(bitstring.ReadError): _ = lsb0.read('se') with pytest.raises(bitstring.ReadError): _ = lsb0.read('sie') class TestRepr: def test_without_pos(self): a = BitStream('0x12345', pos=0) assert repr(a) == "BitStream('0x12345')" def test_with_pos(self): a = BitStream('0b00111', pos=-1) assert a.pos == 4 assert repr(a) == "BitStream('0b00111', pos=4)" class TestFormat: def test_simple_format_strings(self): a = Bits('0xabc') s = f'{a}' assert s == '0xabc' a += '0b0' assert f'{a}' == '0b1010101111000' b = BitStream(10, pos=4) assert f'{b}' == '0b0000000000' c = BitStream(filename=os.path.join(THIS_DIR, 'test.m1v')) assert f'{c}'[0:10] == '0x000001b3' def test_format_strings_with_interpretation(self): a = Bits('0xf') assert f'{a.bin}' == '1111' class TestCacheingIssues: def test_cache_with_offset(self): y = BitStream('0xdeadbeef1000') with pytest.raises(bitstring.CreationError): x = BitStream('0xdeadbeef1000', offset=8) def test_cache_with_pos(self): y = BitStream('0xdeadbeef1001', pos=3) assert y.pos == 3 x = BitStream('0xdeadbeef1001', pos=5) assert x.pos == 5 def test_cache_with_length(self): y = BitStream('0xdeadbeef002') with pytest.raises(bitstring.CreationError): x = BitStream('0xdeadbeef002', length=16) def test_unpack_error(): format_with_commas = ',bytes:2,,bytes:1,' dp = BitStream(hex='010203').unpack(fmt=format_with_commas) assert dp == [b'\x01\x02', b'\x03'] def test_add_pos_issue(): x = BitStream() y = x + Bits('0xff') assert x.pos == 0 assert y == '0xff' z = x + bitstring.BitArray('0xff') assert z == '0xff' q = x + ConstBitStream('0xff') assert q == '0xff' xx = ConstBitStream() yy = xx + Bits('0xff') zz = xx + bitstring.BitArray('0xff') qq = xx + BitStream('0xff') assert yy == zz == qq == '0xff' bitstring-bitstring-4.2.3/tests/test_bitstring.py000066400000000000000000000146271462444752600223400ustar00rootroot00000000000000#!/usr/bin/env python """ Module-level unit tests. """ import io from unittest import mock from contextlib import redirect_stdout import bitstring import copy from collections import abc import sys import os from bitstring import __main__ sys.path.insert(0, '..') THIS_DIR = os.path.dirname(os.path.abspath(__file__)) class TestModuleData: def test_all(self): exported = ['ConstBitStream', 'BitStream', 'BitArray', 'Bits', 'pack', 'Error', 'ReadError', 'Array', 'InterpretError', 'ByteAlignError', 'CreationError', 'bytealigned', 'lsb0', 'Dtype', 'options'] assert set(bitstring.__all__) == set(exported) def test_pyproject_version(self): filename = os.path.join(THIS_DIR, '../pyproject.toml') try: with open(filename, 'r') as pyprojectfile: found = False for line in pyprojectfile.readlines(): if line.startswith("version"): assert not found assert bitstring.__version__ in line found = True assert found except FileNotFoundError: pass # Doesn't run on CI. class TestCopy: def test_const_bit_array_copy(self): cba = bitstring.Bits(100) cba_copy = copy.copy(cba) assert cba is cba_copy def test_bit_array_copy(self): ba = bitstring.BitArray(100) ba_copy = copy.copy(ba) assert not ba is ba_copy assert not ba._bitstore is ba_copy._bitstore assert ba == ba_copy def test_const_bit_stream_copy(self): cbs = bitstring.ConstBitStream(100) cbs.pos = 50 cbs_copy = copy.copy(cbs) assert cbs_copy.pos == 0 assert cbs._bitstore is cbs_copy._bitstore assert cbs == cbs_copy def test_bit_stream_copy(self): bs = bitstring.BitStream(100) bs.pos = 50 bs_copy = copy.copy(bs) assert bs_copy.pos == 0 assert not bs._bitstore is bs_copy._bitstore assert bs == bs_copy class TestInterning: def test_bits(self): a = bitstring.Bits('0xf') b = bitstring.Bits('0xf') assert a._bitstore is b._bitstore c = bitstring.Bits('0b1111') assert not a is c def test_cbs(self): a = bitstring.ConstBitStream('0b11000') b = bitstring.ConstBitStream('0b11000') assert a._bitstore is b._bitstore assert not a is b class TestLSB0: def test_getting_and_setting(self): assert bitstring.lsb0 == False bitstring.lsb0 = True assert bitstring.lsb0 == True bitstring.lsb0 = False assert bitstring.lsb0 == False class TestMain: def test_running_module_directly_help(self): with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['bitstring.py', '-h']): bitstring.__main__.main() s = f.getvalue() assert s.find("command-line parameters") >= 0 with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['renamed.py']): bitstring.__main__.main() s = f.getvalue() assert s.find("command-line parameters") >= 0 def test_running_module_with_single_parameter(self): with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['', 'uint:12=352']): bitstring.__main__.main() s = f.getvalue() assert s == '0x160\n' def test_running_module_with_single_parameter_and_interpretation(self): with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['ignored', 'u12=352', 'i']): bitstring.__main__.main() s = f.getvalue() assert s == '352\n' def test_running_module_with_multiple_parameters(self): with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['b.py', 'uint12=352', '0b101', '0o321', 'f32=51', 'bool=1']): bitstring.__main__.main() s = f.getvalue() assert s == '0x160ad1424c0000, 0b1\n' def test_running_module_with_multiple_parameters_and_interpretation(self): with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['b.py', 'ue=1000', '0xff.bin']): bitstring.__main__.main() s = f.getvalue() assert s == '000000000111110100111111111\n' def test_short_interpretations(self): with redirect_stdout(io.StringIO()) as f: with mock.patch('sys.argv', ['b.py', 'bin=001.b']): bitstring.__main__.main() s = f.getvalue() assert s == '001\n' class TestABCs: def test_base_classes(self): # The classes deliberately do not conform to the sequence ABCs. # see https://github.com/scott-griffiths/bitstring/issues/261 bits = bitstring.Bits() assert not isinstance(bits, abc.Sequence) assert not isinstance(bits, abc.MutableSequence) bitarray = bitstring.BitArray() assert not isinstance(bitarray, abc.MutableSequence) assert not isinstance(bitarray, abc.Sequence) constbitstream = bitstring.ConstBitStream() assert not isinstance(constbitstream, abc.Sequence) assert not isinstance(constbitstream, abc.MutableSequence) bitstream = bitstring.BitArray() assert not isinstance(bitstream, abc.MutableSequence) assert not isinstance(bitstream, abc.Sequence) class TestNoFixedLengthPackingBug: def test_packing_bytes_with_no_length(self): a = bitstring.pack('bytes', b'abcd') assert a.bytes == b'abcd' def test_packing_bin_with_no_length(self): a = bitstring.pack('bin', '0001') assert a.bin == '0001' def test_packing_hex_with_no_length(self): a = bitstring.pack('hex', 'abcd') assert a.hex == 'abcd' def test_reading_bytes_with_no_length(self): a = bitstring.BitStream(b'hello') b = a.read('bytes') assert b == b'hello' def test_reading_bin_with_no_length(self): a = bitstring.BitStream('0b1101') b = a.read('bin') assert b == '1101' def test_reading_uint_with_no_length(self): a = bitstring.BitStream('0b1101') b = a.read('uint') assert b == 13 def test_reading_float_with_no_length(self): a = bitstring.BitStream(float=14, length=16) b = a.read('float') assert b == 14.0 bitstring-bitstring-4.2.3/tests/test_constbitstream.py000066400000000000000000000165131462444752600233700ustar00rootroot00000000000000#!/usr/bin/env python import pytest import sys import bitstring import io import os from bitstring import ConstBitStream as CBS import platform sys.path.insert(0, '..') THIS_DIR = os.path.dirname(os.path.abspath(__file__)) class TestAll: def test_from_file(self): s = CBS(filename=os.path.join(THIS_DIR, 'test.m1v')) assert s[0:32].hex == '000001b3' assert s.read(8 * 4).hex == '000001b3' width = s.read(12).uint height = s.read(12).uint assert (width, height) == (352, 288) def test_from_file_with_offset_and_length(self): s = CBS(filename=os.path.join(THIS_DIR, 'test.m1v'), offset=24, length=8) assert s.h == 'b3' reconstructed = '' for bit in s: reconstructed += '1' if bit is True else '0' assert reconstructed == s.bin class TestInterleavedExpGolomb: def test_reading(self): s = CBS(uie=333) a = s.read('uie') assert a == 333 s = CBS('uie=12, sie=-9, sie=9, uie=1000000') u = s.unpack('uie, 2*sie, uie') assert u == [12, -9, 9, 1000000] def test_reading_errors(self): s = CBS(10) with pytest.raises(bitstring.ReadError): s.read('uie') assert s.pos == 0 with pytest.raises(bitstring.ReadError): s.read('sie') assert s.pos == 0 class TestReadTo: def test_byte_aligned(self): a = CBS('0xaabb00aa00bb') b = a.readto('0x00', bytealigned=True) assert b == '0xaabb00' assert a.bytepos == 3 b = a.readto('0xaa', bytealigned=True) assert b == '0xaa' with pytest.raises(bitstring.ReadError): b.readto('0xcc', bytealigned=True) def test_not_aligned(self): a = CBS('0b00111001001010011011') a.pos = 1 assert a.readto('0b00') == '0b011100' assert a.readto('0b110') == '0b10010100110' with pytest.raises(ValueError): a.readto('') def test_disallow_integers(self): a = CBS('0x0f') with pytest.raises(ValueError): a.readto(4) def test_reading_lines(self): s = b"This is a test\nof reading lines\nof text\n" b = CBS(bytes=s) n = bitstring.Bits(bytes=b'\n') assert b.readto(n).bytes == b'This is a test\n' assert b.readto(n).bytes == b'of reading lines\n' assert b.readto(n).bytes == b'of text\n' class TestSubclassing: def test_is_instance(self): class SubBits(CBS): pass a = SubBits() assert isinstance(a, SubBits) def test_class_type(self): class SubBits(CBS): pass assert SubBits().__class__ == SubBits class TestPadToken: def test_read(self): s = CBS('0b100011110001') a = s.read('pad:1') assert a == None assert s.pos == 1 a = s.read(3) assert a == CBS('0b000') a = s.read('pad:1') assert a == None assert s.pos == 5 def test_read_list(self): s = CBS.fromstring('0b10001111001') t = s.readlist('pad:1, uint:3, pad:4, uint:3') assert t == [0, 1] s.pos = 0 t = s.readlist('pad:1, pad:5') assert t == [] assert s.pos == 6 s.pos = 0 t = s.readlist('pad:1, bin, pad:4, uint:3') assert t == ['000', 1] s.pos = 0 t = s.readlist('pad, bin:3, pad:4, uint:3') assert t == ['000', 1] class TestReadingBytes: def test_unpacking_bytes(self): s = CBS(80) t = s.unpack('bytes:1') assert t[0] == b'\x00' a, b, c = s.unpack('bytes:1, bytes, bytes2') assert a == b'\x00' assert b == b'\x00'*7 assert c == b'\x00'*2 def test_unpacking_bytes_with_keywords(self): s = CBS('0x55'*10) t = s.unpack('pad:a, bytes:b, bytes, pad:a', a=4, b=6) assert t == [b'\x55'*6, b'\x55'*3] class TestReadingBitsAsDefault: def test_read_bits(self): s = CBS('uint:31=14') v = s.read(31) assert v.uint == 14 s.pos = 0 def test_read_list_bits(self): s = CBS('uint:5=3, uint:3=0, uint:11=999') v = s.readlist([5, 3, 11]) assert [x.uint for x in v] == [3, 0, 999] s.pos = 0 v = s.readlist(['5', '3', 11]) assert [x.uint for x in v] == [3, 0, 999] class TestLsb0Reading: @classmethod def setup_class(cls): bitstring.lsb0 = True @classmethod def teardown_class(cls): bitstring.lsb0 = False def test_reading_hex(self): s = CBS('0xabcdef') assert s.read('hex:4') == 'f' assert s.read(4) == '0xe' assert s.pos == 8 def test_reading_oct(self): s = CBS('0o123456') assert s.read('o6') == '56' assert s.pos == 6 def test_reading_bin(self): s = CBS('0b00011') assert s.read('bin:3') == '011' assert s.pos == 3 def test_reading_bytes(self): s = CBS(bytes=b'54321') assert s.pos == 0 s.pos = 8 assert s.read('bytes:2') == b'32' class TestBytesIOCreation: def test_simple_creation(self): f = io.BytesIO(b"\x12\xff\x77helloworld") s = CBS(f) assert s[0:8] == '0x12' assert len(s) == 13 * 8 s = CBS(f, offset=8, length=12) assert s == '0xff7' def test_exceptions(self): f = io.BytesIO(b"123456789") _ = CBS(f, length=9*8) with pytest.raises(bitstring.CreationError): _ = CBS(f, length=9*8 + 1) with pytest.raises(bitstring.CreationError): _ = CBS(f, length=9*8, offset=1) class TestCreationWithPos: def test_default_creation(self): s = CBS('0xabc') assert s.pos == 0 def test_positive_pos(self): s = CBS('0xabc', pos=0) assert s.pos == 0 s = CBS('0xabc', pos=1) assert s.pos == 1 s = CBS('0xabc', pos=12) assert s.pos == 12 with pytest.raises(bitstring.CreationError): _ = CBS('0xabc', pos=13) def test_negative_pos(self): s = CBS('0xabc', pos=-1) assert s.pos == 11 s = CBS('0xabc', pos=-12) assert s.pos == 0 with pytest.raises(bitstring.CreationError): _ = CBS('0xabc', pos=-13) def test_string_representation(self): s = CBS('0b110', pos=2) assert s.__repr__() == "ConstBitStream('0b110', pos=2)" def test_string_representation_from_file(self): filename = os.path.join(THIS_DIR, 'test.m1v') s = CBS(filename=filename, pos=2001) assert s.__repr__() == f"ConstBitStream(filename={repr(str(filename))}, length=1002400, pos=2001)" s.pos = 0 assert s.__repr__() == f"ConstBitStream(filename={repr(str(filename))}, length=1002400)" def test_windows_file_lock_bug(): path = os.path.join(THIS_DIR, 'temp_unit_test_file') # Create the file with open(path, mode='w') as f: f.write('Hello') # Will this lock it? _ = CBS(filename=path) try: with open(path, mode='w') as _: pass except OSError: if platform.system() == 'Windows': # Expected failure. See bug #308 pass def test_readerrors(): s = CBS('0b110') s.read(3) with pytest.raises(bitstring.ReadError): _ = s.read(1) s.pos = 1 with pytest.raises(bitstring.ReadError): _ = s.read('u3')bitstring-bitstring-4.2.3/tests/test_dtypes.py000066400000000000000000000101251462444752600216300ustar00rootroot00000000000000 import pytest import sys import bitstring as bs from bitstring import Dtype, DtypeDefinition, Bits sys.path.insert(0, '..') class TestBasicFunctionality: def test_setting_bool(self): b = Dtype('bool') assert str(b) == 'bool' assert b.name == 'bool' assert b.length == 1 b2 = Dtype('bool:1') assert b == b2 # self.assertTrue(b is b2) def test_reading(self): b = Dtype('u8') a = bs.BitStream('0xff00ff') x = a.read(b) assert x == 255 x = a.read(b) assert x == 0 def test_setting_with_length(self): d = Dtype('uint', 12) assert str(d) == 'uint12' assert d.length == 12 assert d.name == 'uint' def test_build_errors(self): dtype = Dtype('uint8') value = 'not_an_integer' with pytest.raises(ValueError): dtype.build(value) def test_build(self): dtype = Dtype('se') x = dtype.build(10001) assert x.se == 10001 def test_parse(self): dtype = Dtype('uint:12') x = dtype.parse('0x3ff') assert x == 1023 def test_immutability(self): d = Dtype('e3m2mxfp') with pytest.raises(AttributeError): d.length = 8 with pytest.raises(AttributeError): d.name = 'uint8' with pytest.raises(AttributeError): d.scale = 2 def test_variable_lengths(self): d = Dtype('ue') a = bs.BitStream().join([d.build(v) for v in [1, 100, 3, 17, 4]]) assert a.read(d) == 1 assert a.read(d) == 100 assert a.read(d) == 3 assert a.read(d) == 17 assert a.read(d) == 4 a.pos = 0 ds = Dtype('ue', scale=-3) assert a.read(ds) == -3 assert a.read(ds) == -300 assert a.read(ds) == -9 assert a.read(ds) == -51 assert a.read(ds) == -12 def test_building_bits(self): d = Dtype('bits3') a = d.build('0b101') assert a == '0b101' with pytest.raises(ValueError): d.build('0b1010') def test_building_bin(self): d = Dtype('bin9') a = d.build('0b000111000') assert a == '0b000111000' with pytest.raises(ValueError): d.build('0b0001110000') def test_building_ints(self): d = Dtype('i3') a = d.build(-3) assert a == '0b101' with pytest.raises(ValueError): d.build(4) class TestChangingTheRegister: def test_retrieving_meta_dtype(self): r = bs.dtype_register u = r['uint'] u2 = r['u'] assert u == u2 with pytest.raises(KeyError): i = r['integer'] def test_removing_type(self): r = bs.dtype_register del r['bfloat'] with pytest.raises(KeyError): i = r['bfloat'] with pytest.raises(KeyError): del r['penguin'] class TestCreatingNewDtypes: def test_new_alias(self): bs.dtype_register.add_dtype_alias('bin', 'cat') a = bs.BitStream('0b110110') assert a.cat == '110110' assert a.read('cat4') == '1101' a.cat = '11110000' assert a.unpack('cat') == ['11110000'] def test_new_type(self): md = DtypeDefinition('uint_r', bs.Bits._setuint, bs.Bits._getuint) bs.dtype_register.add_dtype(md) a = bs.BitArray('0xf') assert a.uint_r == 15 a.uint_r = 1 assert a == '0x1' a += 'uint_r100=0' assert a == '0x1, 0b' + '0'*100 def test_new_type_with_getter(self): def get_fn(bs): return bs.count(1) md = DtypeDefinition('counter', None, get_fn) bs.dtype_register.add_dtype(md) a = bs.BitStream('0x010f') assert a.counter == 5 assert a.readlist('2*counter8') == [1, 4] assert a.unpack('counter7, counter') == [0, 5] with pytest.raises(AttributeError): a.counter = 4 def test_invalid_dtypes(self): with pytest.raises(TypeError): _ = Dtype() with pytest.raises(ValueError): _ = Dtype('float17') bitstring-bitstring-4.2.3/tests/test_fp8.py000066400000000000000000000354571462444752600210340ustar00rootroot00000000000000from __future__ import annotations import pytest import sys import array import math import bitstring from bitstring import Bits, BitArray, BitStream, Dtype from bitstring.fp8 import p4binary_fmt, p3binary_fmt from bitstring.mxfp import e4m3mxfp_saturate_fmt, e5m2mxfp_saturate_fmt, e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt from gfloat.formats import (format_info_ocp_e4m3, format_info_ocp_e5m2, format_info_p3109, format_info_ocp_e3m2, format_info_ocp_e2m3, format_info_ocp_e2m1, format_info_ocp_int8, format_info_ocp_e8m0) import gfloat sys.path.insert(0, '..') class TestFp8: def test_creation(self): a = Bits(p4binary=-14.0) assert a.p4binary == -14.0 b = Bits('p3binary=3.0') assert b.p3binary == 3.0 assert len(b) == 8 c = Bits('p4binary=1000000000') assert c.hex == '7f' d = Bits('p3binary=-1e15774') assert d.hex == 'ff' e = Bits(p3binary=float('nan')) assert math.isnan(e.p3binary) assert e.hex == '80' def test_reassignment(self): a = BitArray() a.p4binary = -0.25 assert a.p4binary == -0.25 a.p3binary = float('inf') assert a.hex == '7f' assert a.p4binary == float('inf') a.p4binary = -9000.0 assert a.p4binary == float('-inf') a.p3binary = -0.00000000001 assert a.p3binary == 0.0 def test_reading(self): a = BitStream('0x00fff') x = a.read('p3binary') assert x == 0.0 assert a.pos == 8 x = a.read('p4binary') assert x == -float('inf') assert a.pos == 16 def test_read_list(self): v = [-6, -2, 0.125, 7, 10] a = bitstring.pack('5*p4binary', *v) vp = a.readlist('5*p4binary') assert v == vp def test_interpretations(self): a = BitArray('0x00') assert a.p4binary == 0.0 assert a.p3binary == 0.0 a += '0b1' with pytest.raises(bitstring.InterpretError): _ = a.p4binary with pytest.raises(bitstring.InterpretError): _ = a.p3binary def createLUT_for_int8_to_float(exp_bits, bias) -> array.array[float]: """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(uint=i, length=8) sign = b[0] exponent = b[1:1 + exp_bits].u significand = b[1 + exp_bits:] if exponent == 0: significand.prepend([0]) exponent = -bias + 1 else: significand.prepend([1]) exponent -= bias f = float(significand.u) / (2.0 ** (7 - exp_bits)) f *= 2 ** exponent i2f.append(f if not sign else -f) # One special case for minus zero i2f[0b10000000] = float('nan') # and for positive and negative infinity i2f[0b01111111] = float('inf') i2f[0b11111111] = float('-inf') return array.array('f', i2f) class TestCheckLUTs: def test_lut_int8_to_p4binary(self): lut_stored = p4binary_fmt.lut_binary8_to_float assert len(lut_stored) == 256 lut_calculated = createLUT_for_int8_to_float(4, 8) for i in range(len(lut_stored)): if lut_stored[i] != lut_calculated[i]: # Either they're equal or they're both nan (which doesn't compare as equal). assert math.isnan(lut_stored[i]) assert math.isnan(lut_calculated[i]) def test_lut_int8_to_p3binary(self): lut_stored = p3binary_fmt.lut_binary8_to_float assert len(lut_stored) == 256 lut_calculated = createLUT_for_int8_to_float(5, 16) for i in range(len(lut_stored)): if lut_stored[i] != lut_calculated[i]: # Either they're equal or they're both nan (which doesn't compare as equal). assert math.isnan(lut_stored[i]) assert math.isnan(lut_calculated[i]) # def test_strange_failure(): # # x = (b'x\x01\xed\xdd\x05\xba\x96\x05\x00\x05\xe1\x9f\xee\x06\xe9FZA\xa4\xbb' # b'\xbb;\xa4SB\xba\xeb\xd2\xdd\x8dt\x97\x92J(\xa14\xa2\x84\x92\x8a\xa4\x82\xd2' # b'\x1d\x12.c\x9e\xcb7\xef\x0e\xcel\xe0\x84B\xb2\x80\x05,`\x01\x0bX\xc0\x02' # b'\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05\xde\xd7\x02\x11d' # b'\x01\x0b\x04\xb6@D\x05\xba@$\x05\xba@\xe4\x80\x8b\x12pQ\x03.Z\xc0E' # b'\x87\xc5\x80\xc5\x84\xc5\x82\xc5\x86\xc5\x81\xc5\x85\xc5\x83\xc5\x87%\x80%' # b'\x84%\x82%\x86%\x81}\x00K\nK\x06K\x0eK\x01K\tK\x05K\rK\x03K\x0bK' # b'\x07K\x0f\xcb\x00\xcb\x08\xcb\x04\xfb\x10\x96\x19\x96\x05\x96\x15\x96\r\x96' # b"\x1d\x96\x03\x96\x13\xf6\x11\xeccX.Xn\xd8'\xb0<\xb0Oaya\xf9`\xf9a\x05`" # b'\x05a\x85`\x85aE`Ea\xc5`\xc5a%`%a\xa5`\xa5ae`ea\xe5`\xe5a\x15`\x15a\x95`' # b'\x95aU`Ua\xd5`\xd5a5`5a\xb5`\xb5au`ua\xf5`\xf5a\r`\ra\x8d`\x8daM`' # b'\x9f\xc1\x9a\xc2\x9a\xc1\x9a\xc3Z\xc0Z\xc2Z\xc1Z\xc3\xda\xc0\xda\xc2' # b'\xda\xc1\xda\xc3:\xc0>\x87u\x84u\x82u\x86u\x81}\x01\xeb\n\xeb\x06\xeb\x0e' # b'\xeb\x01\xeb\t\xeb\x05\xeb\r\xeb\x03\xeb\x0b\xeb\x07\xeb\x0f\x1b\x00\x1b\x08' # b'\x1b\x04\x1b\x0c\x1b"\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0' # b'\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0b' # b'X\xc0\x02\x16\xb0\x80\x05,\x10\xce\x0b\x0c\r\x93\x05,\x10\xd4\x02\xef' # b'\xeb\xaf\x99\xbb,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX' # b'\xc0\x02\x16\xb0\x80\x05,`\x81Ph\x98,`\x81\xc0\x16\x18\xae@\x17\x18\xa1@' # b'\x17\x18\x19p\xa3\x02nt\xc0\x8d\t\xb8\xb1\xb0q\xb0\xf1\xb0\t\xb0' # b'\x89\xb0I\xb0\xc9\xb0)\xb0\xa9\xb0i\xb0\xe9\xb0\x19\xb0\x99\xb0Y\xb0' # b'\xd9\xb09\xb0\xb9\xb0y\xb0\xf9\xb0/a\x0b`\x0ba\x8b`\x8baK`Ka\xcb`\xcba+`+a' # b'\xab`\xabak`ka\xeb`\xeba\x1b`_\xc1\xbe\x86m\x84m\x82m\x86m\x81m\x85' # b'm\x83}\x03\xfb\x16\xb6\x1d\xb6\x03\xb6\x13\xb6\x0b\xf6\x1d\xec{\xd8n' # b'\xd8\x1e\xd8^\xd8>\xd8\x0f\xb0\x1fa\xfba\x07`\x07a\x87`\x87aG`Ga\xc7`?' # b'\xc1\x8e\xc3~\x86\xfd\x02;\x01;\t;\x05;\r\xfb\x15\xf6\x1b\xec\x0c\xec,\xec' # b'\x1c\xec<\xec\x02\xec"\xecw\xd8\x1f\xb0K\xb0?a\x97aW`Wa\xd7`\xd7a7`' # b'\x7f\xc1\xfe\x86\xdd\x84\xdd\x82\xfd\x03\xfb\x17v\x1bv\x07v\x17v\x0f' # b'v\x1f\xf6\x00\xf6\x10\xf6\x08\xf6\x18\xf6\x04\xf6\x14\xf6\x0c' # b'\xf6\x1c\xf6\x02\xf6\x12\xf6\n\xf6\x1f\xec5\xec\r\xec\xad,`\x01\x0b' # b'X\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0' # b'\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0@8/\xf0.\xa8\xc7\xe7\xee\xb6' # b'\x80\x05\xc2\xfe\x07j_rx') # # y = (b'x\x01\xed\xdde\x9a\x90\x05\x00Ea\x1aIQZ\x1a\xa4\x94\x06\xa5A:\x04\xa4' # b'T\xba\x1b\xe9n\x90\x90.%$\xa5KP\x91\xeeV\xba\xbb\xbb\xbb\x9bE\xcc\x8f' # b'\xf3\xcc|\xe7\xdd\xc1=\x1b\xb8\xe1\xc2\xc9\x02\x16\xb0\x80\x05,`\x01' # b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x810_ \xbc,`\x01\x0bX \x88\x05"\xc8\x02A.' # b'\x10QA.\x10Il\x81\xc8\xc1\x16%\xe8\xa2\x06\xdbG\xa1]4VtZ\x0cVLZ,Vl\xda\xc7' # b'\xac8\xb4OX\x9f\xd2\xe2\xb2\xe2\xd1\xe2\xb3\x12\xd0\x12\xb2\x12\xd1\x12' # b'\xb3>\xa3%a%\xa5%c%\xa7\xa5`\xa5\xa4\xa5b\xa5\xa6\xa5a}NK\xcbJGK' # b"\xcf\xca@\xcb\xc8\xfa\x82\xf6%+\x13-3+\x0b-++\x1b-;+\x07-'+\x17\xed" # b'+\xd6\xd7\xb4\xdc\xac<\xb4\xbc\xac|\xb4\xfc\xac\x02\xb4\x82\xacB\xb4' # b'\xc2\xacohEXEi\xc5X\xc5i%X%i\xa5X\xa5ieXei\xdf\xb2\xca\xd1\xca\xb3*\xd0' # b'\xbecU\xa4UbU\xa6UaU\xa5}\xcf\xfa\x81\xf6#\xab\x1a\xad:\xab\x06\xad&\xab\x16' # b'\xad6\xab\x0e\xad.\xab\x1e\xad>\xab\x01\xad!\xab\x11\xad1\xab\t\xad)\xab\x19' # b"\xad9\xab\x05\xad%\xeb'Z+VkZ\x1bV[Z;V{Z\x07VGZ'VgZ\x17VWZ7VwZ\x0fVOZ/V\xef" # b'\x90\xea#\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`' # b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05' # b',\x10\xca\x0b\xf4\x95\x05,\x10\xd4\x02a\xfe\xdf\xd0\x81\x16\xb0\x80\x05' # b',`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\x08p\x81~\n' # b't\x81\x9fe\x81 \x17\xe8\xaf \x17\x18 \xb6\xc0\xc0`\x1b\x14t\xbf\x04\xdb\xe0' # b'\xd0n\x08k(m\x18k8m\x04k$m\x14k4m\x0ck,\xedW\xd6o\xb4q\xac\xf1\xb4\t\xac' # b'\x89\xb4\xdfY\x93h\x93YShSY\xd3h\xd3Y\x7f\xd0f\xb0f\xd2f\xb1f\xd3\xe6\xb0' # b'\xe6\xd2\xe6\xb1\xe6\xd3\x16\xb0\x16\xd2\x16\xb1\xfe\xa4-f-\xa1\xfd\xc5' # b'\xfa\x9b\xf6\x0fk)\xed_\xd62\xdar\xd6\n\xdaJ\xd6*\xdaj\xd6\x1a\xdaZ' # b'\xd6:\xdaz\xd6\x06\xdaF\xd6&\xdaf\xd6\x16\xdaV\xd66\xdav\xd6\x0e\xda\x7f' # b"\xac\xffi;Y\xbbh\xbbY{h{Y\xfbh\xfbY\x07h\x07Y\x87h\x87YGhGY\xc7h\xc7Y'h'" # b'Y\xa7h\xa7YghgY\xe7h\xe7Y\x17h\x17Y\x97h\x97YWhWY\xd7h\xd7Y7h7Y\xb7h\xb7Ywhw' # b'Y\xf7h\xf7Y\x0fh\x0fY\x8fh\x8fYOhOY\xcfh\xcfY/h/Y\xafh\xafYohoY\xefB\xea' # b'\xbd,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,' # b'`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0@(/\x10\xd4' # b'\xe3sw[\xc0\x02}?\x00\xacO\xfe\xf9') # # import zlib # x = zlib.decompress(x) # y = zlib.decompress(y) # assert len(x) == len(y) == 65536 # assert x == y class TestConversionToFP8: def test_some143_values(self): zero = bitstring.Bits('0b0000 0000') assert p4binary_fmt.lut_binary8_to_float[zero.uint] == 0.0 max_normal = bitstring.Bits('0b0111 1110') assert p4binary_fmt.lut_binary8_to_float[max_normal.uint] == 224.0 max_normal_neg = bitstring.Bits('0b1111 1110') assert p4binary_fmt.lut_binary8_to_float[max_normal_neg.uint] == -224.0 min_normal = bitstring.Bits('0b0000 1000') assert p4binary_fmt.lut_binary8_to_float[min_normal.uint] == 2**-7 min_subnormal = bitstring.Bits('0b0000 0001') assert p4binary_fmt.lut_binary8_to_float[min_subnormal.uint] == 2**-10 max_subnormal = bitstring.Bits('0b0000 0111') assert p4binary_fmt.lut_binary8_to_float[max_subnormal.uint] == 0.875 * 2**-7 nan = bitstring.Bits('0b1000 0000') assert math.isnan(p4binary_fmt.lut_binary8_to_float[nan.uint]) def test_some152_values(self): zero = bitstring.Bits('0b0000 0000') assert p3binary_fmt.lut_binary8_to_float[zero.uint] == 0.0 max_normal = bitstring.Bits('0b0111 1110') assert p3binary_fmt.lut_binary8_to_float[max_normal.uint] == 49152.0 max_normal_neg = bitstring.Bits('0b1111 1110') assert p3binary_fmt.lut_binary8_to_float[max_normal_neg.uint] == -49152.0 min_normal = bitstring.Bits('0b0000 0100') assert p3binary_fmt.lut_binary8_to_float[min_normal.uint] == 2**-15 min_subnormal = bitstring.Bits('0b0000 0001') assert p3binary_fmt.lut_binary8_to_float[min_subnormal.uint] == 0.25 * 2**-15 max_subnormal = bitstring.Bits('0b0000 0011') assert p3binary_fmt.lut_binary8_to_float[max_subnormal.uint] == 0.75 * 2**-15 nan = bitstring.Bits('0b1000 0000') assert math.isnan(p3binary_fmt.lut_binary8_to_float[nan.uint]) def test_round_trip(self): # For each possible 8bit int, convert to float, then convert that float back to an int for fmt in [p4binary_fmt, p3binary_fmt]: for i in range(1 << 8): f = fmt.lut_binary8_to_float[i] ip = fmt.float_to_int8(f) assert ip == i def test_compare_8bit_floats_with_gfloat(self): for fi, lut in [(format_info_p3109(4), p4binary_fmt.lut_binary8_to_float), (format_info_p3109(3), p3binary_fmt.lut_binary8_to_float), (format_info_ocp_e4m3, e4m3mxfp_saturate_fmt.lut_int_to_float), (format_info_ocp_e5m2, e5m2mxfp_saturate_fmt.lut_int_to_float), ]: for i in range(1 << 8): f = lut[i] g = gfloat.decode_float(fi, i).fval if math.isnan(g): assert math.isnan(f) else: # The floats should be bitwise equal. assert f == g def test_conversion_from_nan(self): x = BitArray(p4binary8=float('nan')) assert x == '0x80' x = BitArray(p3binary8=float('nan')) assert x == '0x80' def test_conversion_from_inf(self): x = BitArray(p4binary8=float('inf')) assert x == '0x7f' x = BitArray(p3binary8=float('inf')) assert x == '0x7f' x = BitArray(p4binary8=float('-inf')) assert x == '0xff' x = BitArray(p3binary8=float('-inf')) assert x == '0xff' def test_round_to_nearest(self): # Some exact values x = BitArray(p3binary=48.0) assert x.p3binary == 48.0 x = BitArray(p3binary=56.0) assert x.p3binary == 56.0 x = BitArray(p3binary=64.0) assert x.p3binary == 64.0 x = BitArray(p3binary=51.9) assert x.p3binary == 48.0 x = BitArray(p3binary=52.0) assert x.p3binary == 48.0 assert x.bin[-1] == '0' x = BitArray(p3binary=52.1) assert x.p3binary == 56.0 x = BitArray(p3binary=60.0) assert x.p3binary == 64.0 assert x.bin[-1] == '0' def test_compare_mxint8_with_gfloat(): for i in range(1 << 8): f = Dtype('mxint8').parse(BitArray(uint=i, length=8)) g = gfloat.decode_float(format_info_ocp_int8, i).fval assert f == g def test_compare_e8m0_with_gfloat(): for i in range(1 << 8): f = Dtype('e8m0mxfp').parse(BitArray(uint=i, length=8)) g = gfloat.decode_float(format_info_ocp_e8m0, i).fval if math.isnan(g): assert math.isnan(f) else: assert f == g def test_compare_6bit_floats_with_gfloat(): for fi, lut in [(format_info_ocp_e3m2, e3m2mxfp_fmt.lut_int_to_float), (format_info_ocp_e2m3, e2m3mxfp_fmt.lut_int_to_float)]: for i in range(1 << 6): f = lut[i] g = gfloat.decode_float(fi, i).fval if math.isnan(g): assert math.isnan(f) else: assert f == g def test_compare_4bit_floats_with_gfloat(): fi = format_info_ocp_e2m1 lut = e2m1mxfp_fmt.lut_int_to_float for i in range(1 << 4): f = lut[i] g = gfloat.decode_float(fi, i).fval if math.isnan(g): assert math.isnan(f) else: assert f == g def test_rounding_consistent_to_gfloat(): for fi, dt in [[format_info_p3109(4), Dtype('p4binary')], [format_info_p3109(3), Dtype('p3binary')]]: for i in range(0, 1 << 16): f = BitArray(uint=i, length=16).float mine = dt.parse(dt.build(f)) theirs = gfloat.round_float(fi, f) if math.isnan(mine): assert math.isnan(theirs) else: assert mine == theirs bitstring-bitstring-4.2.3/tests/test_mxfp.py000066400000000000000000000354021462444752600212770ustar00rootroot00000000000000from __future__ import annotations import sys import math from bitstring import BitArray, Dtype, Array, options import pytest import gfloat sys.path.insert(0, '..') def test_creation_e3m2mxfp(): x = BitArray('0b000000') assert x.e3m2mxfp == 0.0 x.e3m2mxfp = 0.0 assert x.e3m2mxfp == 0.0 assert len(x) == 6 def test_getting_e4m3mxfp_values(): assert BitArray('0b00000000').e4m3mxfp == 0.0 assert BitArray('0b01111110').e4m3mxfp == 448.0 assert BitArray('0b11111110').e4m3mxfp == -448.0 assert BitArray('0b00001000').e4m3mxfp == 2**-6 assert math.isnan(BitArray('0b01111111').e4m3mxfp) assert math.isnan(BitArray('0b11111111').e4m3mxfp) def test_setting_e4m3mxfp_values(): x = BitArray('0b00000000') x.e4m3mxfp = 0.0 assert x == '0b00000000' x.e4m3mxfp = -(2 ** -9) assert x == '0b10000001' x.e4m3mxfp = 99999 assert x.e4m3mxfp == 448.0 def test_getting_e5m2mxfp_values(): assert BitArray('0b00000000').e5m2mxfp == 0.0 assert BitArray('0b01111011').e5m2mxfp == 57344 for b in ['0b01111101', '0b11111101', '0b01111110', '0b11111110', '0b01111111', '0b11111111']: assert math.isnan(BitArray(b).e5m2mxfp) assert BitArray('0b01111100').e5m2mxfp == float('inf') assert BitArray('0b11111100').e5m2mxfp == float('-inf') assert BitArray('0b00000001').e5m2mxfp == 2 ** -16 assert BitArray('0b00000100').e5m2mxfp == 2 ** -14 def test_setting_e5m2mxfp_values(): x = BitArray('0b00000000') x.e5m2mxfp = 0.0 assert x == '0b00000000' x.e5m2mxfp = -(2 ** -16) assert x == '0b10000001' x.e5m2mxfp = 99999 assert x.e5m2mxfp == 57344.0 def test_getting_e3m2mxfp_values(): assert BitArray('0b000000').e3m2mxfp == 0.0 assert BitArray('0b000001').e3m2mxfp == 0.0625 assert BitArray('0b000011').e3m2mxfp == 0.1875 assert BitArray('0b000100').e3m2mxfp == 0.25 assert BitArray('0b011111').e3m2mxfp == 28.0 def test_setting_e3m2mxfp_values(): x = BitArray('0b000000') x.e3m2mxfp = 0.0 assert x == '0b000000' x.e3m2mxfp = -0.0625 assert x == '0b100001' x.e3m2mxfp = -0.1875 assert x == '0b100011' x.e3m2mxfp = -0.25 assert x == '0b100100' x.e3m2mxfp = -28.0 assert x == '0b111111' def test_getting_e2m3mxfp_values(): assert BitArray('0b000000').e2m3mxfp == 0.0 assert BitArray('0b000001').e2m3mxfp == 0.125 assert BitArray('0b000111').e2m3mxfp == 0.875 assert BitArray('0b001000').e2m3mxfp == 1.0 assert BitArray('0b011111').e2m3mxfp == 7.5 def test_setting_e2m3mxfp_values(): x = BitArray('0b000000') x.e2m3mxfp = 0.0 assert x == '0b000000' x.e2m3mxfp = -0.125 assert x == '0b100001' x.e2m3mxfp = -0.875 assert x == '0b100111' x.e2m3mxfp = -1.0 assert x == '0b101000' x.e2m3mxfp = -7.5 assert x == '0b111111' def test_getting_e2m1mxfp_values(): assert BitArray('0b0000').e2m1mxfp == 0.0 assert BitArray('0b0001').e2m1mxfp == 0.5 assert BitArray('0b0010').e2m1mxfp == 1.0 assert BitArray('0b0111').e2m1mxfp == 6.0 def test_setting_e2m1mxfp_values(): x = BitArray('0b0000') x.e2m1mxfp = 0.0 assert x == '0b0000' x.e2m1mxfp = -0.5 assert x == '0b1001' x.e2m1mxfp = -1.0 assert x == '0b1010' x.e2m1mxfp = -6.0 assert x == '0b1111' def test_e8m0mxfp_value(): x = BitArray('0x00') assert x.e8m0mxfp == 2.0 ** -127 x.uint = 127 assert x.e8m0mxfp == 1.0 x.uint = 254 assert x.e8m0mxfp == 2.0 ** 127 x.bin = '11111111' assert math.isnan(x.e8m0mxfp) def test_getting_mxint_values(): assert BitArray('0b00000000').mxint == 0.0 assert BitArray('0b00000001').mxint == 1 * 2 ** -6 assert BitArray('0b00000011').mxint == 3 * 2 ** -6 assert BitArray('0b01111111').mxint == 127 * 2 ** -6 assert BitArray('0b10000000').mxint == -2.0 assert BitArray('0b11111111').mxint == -1 * 2 ** -6 assert BitArray('0b11111110').mxint == -2 * 2 ** -6 assert BitArray('0b10000001').mxint == -127 * 2 ** -6 def test_setting_mxint_values(): x = BitArray('0b00000000') x.mxint = 0.0 assert x == '0b00000000' x.mxint = 1 * 2 ** -6 assert x == '0b00000001' x.mxint = -1 * 2 ** -6 assert x == '0b11111111' def test_scaled_array(): sa = Array(Dtype('uint8', scale=2),[100, 200, 300, 400, 500]) assert sa.dtype.scale == 2 assert sa[0] == 100 assert sa[:] == [100, 200, 300, 400, 500] sa.dtype = Dtype('uint8', scale=4) assert sa.dtype.scale == 4 assert sa[0] == 200 sa.dtype = Dtype('uint8', scale=0.25) assert sa.tolist() == [12.5, 25.0, 37.5, 50.0, 62.5] def test_setting_scaled_array(): sa = Array('e3m2mxfp') sa.append(4.0) assert sa[0] == 4.0 assert sa.dtype.scale is None sa.dtype = Dtype('e3m2mxfp', scale = 0.5) assert sa[0] == 2.0 sa.append(6.0) assert sa[1] == 6.0 assert sa[:] == [2.0, 6.0] sa *= 2 assert sa[:] == [4.0, 12.0] sa[:] = [0.0, 0.5, 1.0] assert sa[:] == [0.0, 0.5, 1.0] sa.dtype = Dtype('e3m2mxfp', scale = 1) assert sa[:] == [0.0, 1.0, 2.0] def test_multiple_scaled_arrays(): d = bytes(b'hello_everyone!') s1 = Array(Dtype('e2m1mxfp', scale=1), d) s2 = Array(Dtype('e2m1mxfp', scale=2**10), d) s3 = Array(Dtype('e2m1mxfp', scale=2**-10), d) assert s1.dtype.scale == 1 assert s2.dtype.scale == 2**10 assert s3.dtype.scale == 2**-10 assert s1[0] * 2**10 == s2[0] assert s1[0] * 2**-10 == s3[0] def test_multiple_scaled_arrays2(): b = BitArray('0b011111') assert b.e3m2mxfp == 28.0 s1 = Array('e3m2mxfp', [28]) assert s1[0] == 28.0 assert b.e3m2mxfp == 28.0 s2 = Array('e3m2mxfp', [28]) s2.dtype = Dtype('e3m2mxfp', scale=2**4) assert s1[0] == 28.0 assert s2[0] == 28.0 * 2 ** 4 assert b.e3m2mxfp == 28.0 def test_setting_from_outside_range(): b = BitArray(e2m1mxfp=0.0) b.e2m1mxfp = 6.0 assert b.e2m1mxfp == 6.0 b.e2m1mxfp = 7.0 assert b.e2m1mxfp == 6.0 b.e2m1mxfp = 10000000000.0 assert b.e2m1mxfp == 6.0 s = Array('e2m1mxfp', [-1000.0, 6.0, 7.0, 10000.0]) assert s.tolist() == [-6.0, 6.0, 6.0, 6.0] s = Array(Dtype('e2m1mxfp', scale=2), [-1000.0, 6.0, 7.0, 10000000000.0]) x = s.tolist() assert x == [-12.0, 6.0, 8.0, 12.0] def test_ops(): s = Array(Dtype('e8m0mxfp', scale=2**2), [0.5, 1.0, 2.0, 4.0, 8.0]) t = s * 2 assert type(t) is Array assert t.tolist() == [1.0, 2.0, 4.0, 8.0, 16.0] assert t.dtype.scale == 2 ** 2 def test_auto_scaling(): f = [0.0, 100.0, 256.0, -150.0] for dtype in ['e3m2mxfp', 'e2m3mxfp', 'e2m1mxfp', 'mxint8', 'p3binary', 'p4binary']: d = Dtype(dtype, scale='auto') a = Array(d, f) assert a[2] == 256.0 def test_auto_scaling2(): some_floats = [-4, 100.0, -9999, 0.5, 42, 666] a = Array(Dtype('float16', scale='auto'), some_floats) assert a[2] == -10000.0 a = Array(Dtype('e2m1mxfp', scale='auto'), [1e200]) assert a.dtype.scale == 2 ** 127 a = Array(Dtype('e2m1mxfp', scale='auto'), [1e-200]) assert a.dtype.scale == 2 ** -127 a = Array(Dtype('e2m1mxfp', scale='auto'), [0, 0, 0, 0]) assert a.dtype.scale == 1 def test_auto_scaling_error(): a = Array(Dtype('e3m2mxfp', scale='auto'), [0.3]) expected_scale = 0.25 / 16.0 assert a.dtype.scale == expected_scale b = Array(Dtype('e2m1mxfp', scale='auto'), [-0.9, 0.6, 0.0001]) expected_scale = 0.5 / 4.0 assert b.dtype.scale == expected_scale def test_scaled_array_errors(): with pytest.raises(ValueError): _ = Array(Dtype('bfloat', scale='auto'), [0.0, 100.0, 256.0, -150.0]) with pytest.raises(ValueError): _ = Array(Dtype('uint9', scale='auto'), [0.0, 100.0, 256.0, -150.0]) with pytest.raises(ValueError): _ = Dtype('e3m2mxfp', scale=0) with pytest.raises(TypeError): _ = Array(Dtype('e3m2mxfp', scale='auto'), b'hello') with pytest.raises(TypeError): _ = Array(Dtype('e3m2mxfp', scale='auto'), 100) def test_changing_to_auto_scaled_array(): a = Array('int16', [0, 2003, -43, 104, 6, 1, 99]) with pytest.raises(ValueError): a.dtype = Dtype('e3m2mxfp', scale='auto') with pytest.raises(ValueError): _ = Array(Dtype('e3m2mxfp', scale='auto')) with pytest.raises(ValueError): _ = Array(Dtype('e3m2mxfp', scale='auto'), []) def test_conversion_to_e8m0(): x = BitArray(e8m0mxfp=1.0) assert x.e8m0mxfp == 1.0 x = BitArray(e8m0mxfp=2**127) assert x.e8m0mxfp == 2**127 x = BitArray(e8m0mxfp=2**-127) assert x.e8m0mxfp == 2**-127 x = BitArray(e8m0mxfp=float('nan')) assert math.isnan(x.e8m0mxfp) with pytest.raises(ValueError): _ = BitArray(e8m0mxfp=2**128) with pytest.raises(ValueError): _ = BitArray(e8m0mxfp=-2**128) with pytest.raises(ValueError): _ = BitArray(e8m0mxfp=0.0) with pytest.raises(ValueError): _ = BitArray(e8m0mxfp=1.1) def test_rounding_to_even(): # When rounding to even, the value chosen when two are equidistant should end in a zero bit. x = BitArray(e4m3mxfp=21.0) assert x.e4m3mxfp == 20.0 a = BitArray(e4m3mxfp=20.0).bin b = BitArray(e4m3mxfp=22.0).bin assert a[:-1] == b[:-1] assert a[-1] == '0' assert b[-1] == '1' x = BitArray(e4m3mxfp=22.0) assert x.e4m3mxfp == 22.0 x = BitArray(e4m3mxfp=23.0) assert x.e4m3mxfp == 24.0 x = BitArray(e4m3mxfp=24.0) assert x.e4m3mxfp == 24.0 x = BitArray(e4m3mxfp=-50) # Midway between -48 and -52 assert x.e4m3mxfp == -48.0 # Rounds towards zero assert x.bin[-1] == '0' x = BitArray(e4m3mxfp=-54) # Midway between -52 and -56 assert x.e4m3mxfp == -56.0 # Rounds away from zero assert x.bin[-1] == '0' def test_rounding_consistent_to_gfloat(): for fi, dt in [[gfloat.formats.format_info_ocp_e4m3, Dtype('e4m3mxfp')], [gfloat.formats.format_info_ocp_e5m2, Dtype('e5m2mxfp')]]: for i in range(1 << 16): f = BitArray(uint=i, length=16).float mine = dt.parse(dt.build(f)) theirs = gfloat.round_float(fi, f, sat=True) if math.isnan(mine): assert math.isnan(theirs) else: assert mine == theirs @pytest.mark.usefixtures('switch_to_overflow') def test_rounding_consistent_to_gfloat_with_overflow(): assert options.mxfp_overflow == 'overflow' for fi, dt in [[gfloat.formats.format_info_ocp_e4m3, Dtype('e4m3mxfp')], [gfloat.formats.format_info_ocp_e5m2, Dtype('e5m2mxfp')]]: for i in range(1 << 16): f = BitArray(uint=i, length=16).float mine = dt.parse(dt.build(f)) theirs = gfloat.round_float(fi, f, sat=False) if math.isnan(mine): assert math.isnan(theirs) else: if mine != theirs: print(mine, theirs) def test_conversion_from_nan(): x = BitArray(e4m3mxfp=float('nan')) assert x == '0b11111111' x = BitArray(e5m2mxfp=float('nan')) assert x == '0b11111111' with pytest.raises(ValueError): _ = BitArray(e3m2mxfp=float('nan')) with pytest.raises(ValueError): _ = BitArray(e2m3mxfp=float('nan')) with pytest.raises(ValueError): _ = BitArray(e2m1mxfp=float('nan')) with pytest.raises(ValueError): _ = BitArray(mxint=float('nan')) x = BitArray(e8m0mxfp=float('nan')) assert x == '0b11111111' def test_conversion_from_inf(): x = BitArray(e3m2mxfp=float('inf')) assert x.e3m2mxfp == 28.0 x = BitArray(e3m2mxfp=float('-inf')) assert x.e3m2mxfp == -28.0 x = BitArray(e2m3mxfp=float('inf')) assert x.e2m3mxfp == 7.5 x = BitArray(e2m3mxfp=float('-inf')) assert x.e2m3mxfp == -7.5 x = BitArray(e2m1mxfp=float('inf')) assert x.e2m1mxfp == 6.0 x = BitArray(e2m1mxfp=float('-inf')) assert x.e2m1mxfp == -6.0 x = BitArray(mxint=float('inf')) assert x.mxint == 1.984375 x = BitArray(mxint=float('-inf')) assert x.mxint == -2.0 with pytest.raises(ValueError): _ = BitArray(e8m0mxfp=float('inf')) with pytest.raises(ValueError): _ = BitArray(e8m0mxfp=float('-inf')) def test_conversion_to_8bit_with_saturate(): assert options.mxfp_overflow == 'saturate' x = BitArray(e5m2mxfp=float('inf')) assert x.e5m2mxfp == 57344.0 x = BitArray(e5m2mxfp=float('-inf')) assert x.e5m2mxfp == -57344.0 x = BitArray(e5m2mxfp=1e10) assert x.e5m2mxfp == 57344.0 x = BitArray(e5m2mxfp=-1e10) assert x.e5m2mxfp == -57344.0 x = BitArray(e4m3mxfp=float('inf')) assert x.e4m3mxfp == 448.0 x = BitArray(e4m3mxfp=float('-inf')) assert x.e4m3mxfp == -448.0 x = BitArray(e4m3mxfp=1e10) assert x.e4m3mxfp == 448.0 x = BitArray(e4m3mxfp=-1e10) assert x.e4m3mxfp == -448.0 @pytest.fixture def switch_to_overflow(): options.mxfp_overflow = 'overflow' yield options.mxfp_overflow = 'saturate' @pytest.mark.usefixtures('switch_to_overflow') def test_conversion_to_8bit_with_overflow(): x = BitArray(e5m2mxfp=float('inf')) assert x.e5m2mxfp == float('inf') x = BitArray(e5m2mxfp=float('-inf')) assert x.e5m2mxfp == float('-inf') x = BitArray(e5m2mxfp=1e10) assert x.e5m2mxfp == float('inf') x = BitArray(e5m2mxfp=-1e10) assert x.e5m2mxfp == float('-inf') x = BitArray(e4m3mxfp=float('inf')) assert math.isnan(x.e4m3mxfp) x = BitArray(e4m3mxfp=float('-inf')) assert math.isnan(x.e4m3mxfp) x = BitArray(e4m3mxfp=1e10) assert math.isnan(x.e4m3mxfp) x = BitArray(e4m3mxfp=-1e10) assert math.isnan(x.e4m3mxfp) def test_mxint_rounding(): x = BitArray('mxint=0.0') assert x == '0x00' x.mxint = -2.0 assert x.mxint == -2.0 x.mxint = 1.0/64 assert x.mxint == 1.0/64 assert x == '0x01' x.mxint = 1000 assert x.mxint == 1.0 + 63.0/64.0 x.mxint = 1.4 / 64.0 assert x.mxint == 1.0 / 64.0 x.mxint = 1.6 / 64.0 assert x.mxint == 2.0 / 64.0 x.mxint = 1.5 / 64.0 assert x.mxint == 2.0 / 64.0 # Round to even x.mxint = 2.5 / 64.0 assert x.mxint == 2.0 / 64.0 # Round to even x.mxint = -2.5 assert x.mxint == -2.0 x.mxint = -1.5 / 64.0 assert x.mxint == -2.0 / 64.0 x.mxint = -2.5 / 64.0 assert x.mxint == -2.0 / 64.0 x.mxint = -3.5 / 64.0 assert x.mxint == -4.0 / 64.0 @pytest.mark.usefixtures('switch_to_overflow') @pytest.mark.parametrize("fmt", [Dtype(x) for x in ['e5m2mxfp', 'e4m3mxfp', 'e2m3mxfp', 'e3m2mxfp', 'e2m1mxfp', 'e8m0mxfp', 'mxint', 'p3binary', 'p4binary']]) def test_roundtrips(fmt): # Note that e5m2mxfp in saturate mode will not pass this test, as inf -> inf -> max_value. for i in range(1 << fmt.bitlength): b = BitArray(u=i, length=fmt.bitlength) v = fmt.parse(b) b2 = fmt.build(v) if math.isnan(fmt.parse(b)): assert math.isnan(fmt.parse(b2)) else: assert b == b2 bitstring-bitstring-4.2.3/tests/test_scaled_dtypes.py000066400000000000000000000003721462444752600231460ustar00rootroot00000000000000from __future__ import annotations import sys from bitstring import BitArray, Dtype sys.path.insert(0, '..') def test_simple_scaled_dtype(): d = Dtype('u4', scale=2) assert d.scale == 2 b = BitArray('0x01') assert d.parse(b) == 2