diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b566f17c..131d8009 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,7 +140,7 @@ jobs: - name: Run pytest run: | - python -m pytest tests/ + python -m pytest tests/ --benchmark-disable all: name: All successful diff --git a/MANIFEST.in b/MANIFEST.in index 3272f06a..1d3e2fbd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,7 @@ include tests/test.m1v include tests/smalltestfile include tests/__init__.py -include release_notes.txt +include release_notes.md include README.md include bitstring/py.typed prune doc diff --git a/README.md b/README.md index d1426c74..71964c04 100644 --- a/README.md +++ b/README.md @@ -16,26 +16,13 @@ It has been actively maintained since 2006. [![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) - -News ---- -**May 2024**: bitstring 4.2.2 released. - -New in version 4.2: - -* Dropped support for Python 3.7. Minimum version is now 3.8. -* A new `Dtype` class can be optionally used to specify types. -* The `bitstring.options` object is now the preferred method for changing module options. -* New `fromstring` method as another way to create bitstrings from formatted strings. -* More types can now be pretty printed. -* A range of 8-bit, 6-bit and even 4-bit floating point formats added (beta): -* Performance improvements. -See the [release notes](https://github.com/scott-griffiths/bitstring/blob/main/release_notes.txt) for details. Please let me know if you encounter any problems. +> [!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 --------- +# 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. @@ -46,8 +33,8 @@ Overview * 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 -------------- +# Documentation + Extensive documentation for the bitstring module is available. Some starting points are given below: @@ -57,67 +44,66 @@ Some starting points are given below: There is also an introductory walkthrough notebook on [binder](https://mybinder.org/v2/gh/scott-griffiths/bitstring/main?labpath=doc%2Fwalkthrough.ipynb). -Release Notes -------------- - -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.txt). - -Examples --------- +# Examples ### Installation - - $ pip install bitstring +``` +$ pip install bitstring +``` ### Creation - - >>> 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)) +```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 - - >>> 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') +```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 - - >>> b = BitStream('0x160120f') - >>> b.read(12).hex - '160' - >>> b.pos = 0 - >>> b.read('uint12') - 352 - >>> b.readlist('uint12, bin3') - [288, '111'] +```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 - - >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' - >>> c.find('0x48') - (8,) - >>> c.replace('0b001', '0xabc') - >>> c.insert('0b0000', pos=3) - >>> del c[12:16] +```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 - - >>> 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]) - +```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 diff --git a/bitstring/__init__.py b/bitstring/__init__.py index f6102be7..87cd54bd 100644 --- a/bitstring/__init__.py +++ b/bitstring/__init__.py @@ -55,7 +55,7 @@ THE SOFTWARE. """ -__version__ = "4.2.2" +__version__ = "4.2.3" __author__ = "Scott Griffiths" @@ -111,31 +111,38 @@ def lsb0(self, value: bool) -> None: sys.modules[__name__].__class__ = _MyModuleType -"""These methods convert a bit length to the number of characters needed to print it for different interpretations.""" + +# 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]: @@ -143,53 +150,65 @@ def float_bits2chars(bitlength: Literal[16, 32, 64]): else: return 24 # Empirical value -def p3binary_bits2chars(bitlength: Literal[8]): + +def p3binary_bits2chars(_: Literal[8]): return 19 # Empirical value -def p4binary_bits2chars(bitlength: Literal[8]): + +def p4binary_bits2chars(_: Literal[8]): # Found by looking at all the possible values return 13 # Empirical value -def e4m3mxfp_bits2chars(bitlength: Literal[8]): + +def e4m3mxfp_bits2chars(_: Literal[8]): return 13 -def e5m2mxfp_bits2chars(bitlength: Literal[8]): + +def e5m2mxfp_bits2chars(_: Literal[8]): return 19 -def e3m2mxfp_bits2chars(bitlength: Literal[6]): + +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(bitlength: Literal[6]): + +def e2m3mxfp_bits2chars(_: Literal[6]): # Not sure what the best value is here. return 7 -def e2m1mxfp_bits2chars(bitlength: Literal[4]): + +def e2m1mxfp_bits2chars(_: Literal[4]): # Not sure what the best value is here. return 7 -def e8m0mxfp_bits2chars(bitlength: Literal[8]): - # Can range same as float32 + +def e8m0mxfp_bits2chars(_: Literal[8]): + # Has same range as float32 return 23 -def mxint_bits2chars(bitlength: Literal[8]): + +def mxint_bits2chars(_: Literal[8]): # Not sure what the best value is here. return 10 -def bfloat_bits2chars(bitlength: Literal[16]): +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(bitlength: Literal[1]): + +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, diff --git a/bitstring/__main__.py b/bitstring/__main__.py index 6262af88..f5949ff0 100644 --- a/bitstring/__main__.py +++ b/bitstring/__main__.py @@ -47,4 +47,4 @@ def main() -> None: if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/bitstring/array_.py b/bitstring/array_.py index 1c91f7c6..5aaed8b7 100644 --- a/bitstring/array_.py +++ b/bitstring/array_.py @@ -93,6 +93,7 @@ def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Ar 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. @@ -112,12 +113,15 @@ def _calculate_auto_scale(initializer, name: str, length: Optional[int]) -> floa } 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 - log2 = int(math.log2(max_float_value)) - lp2 = int(math.log2(Array._largest_values[f'{name}{length}'])) + # 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: @@ -155,7 +159,7 @@ def _set_dtype(self, new_dtype: Union[str, Dtype]) -> None: except ValueError: name_length = utils.parse_single_struct_token(new_dtype) if name_length is not None: - dtype = Dtype(*name_length) + dtype = Dtype(name_length[0], name_length[1]) else: raise ValueError(f"Inappropriate Dtype for Array: '{new_dtype}'.") if dtype.length is None: @@ -270,7 +274,7 @@ def astype(self, dtype: Union[str, Dtype]) -> Array: return new_array def tolist(self) -> List[ElementType]: - return [self._dtype.read_fn(self.data, start=start) + 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: @@ -408,9 +412,11 @@ def pp(self, fmt: Optional[str] = None, width: int = 120, 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.") - dtype1 = Dtype(*utils.parse_name_length_token(token_list[0])) + name1, length1 = utils.parse_name_length_token(token_list[0]) + dtype1 = Dtype(name1, length1) if len(token_list) == 2: - dtype2 = Dtype(*utils.parse_name_length_token(token_list[1])) + name2, length2 = utils.parse_name_length_token(token_list[1]) + dtype2 = Dtype(name2, length2) token_length = dtype1.bitlength if dtype2 is not None: diff --git a/bitstring/bitarray_.py b/bitstring/bitarray_.py index 15fe0bc7..4e686844 100644 --- a/bitstring/bitarray_.py +++ b/bitstring/bitarray_.py @@ -11,6 +11,7 @@ import bitstring.dtypes + class BitArray(Bits): """A container holding a mutable sequence of bits. @@ -71,7 +72,6 @@ class BitArray(Bits): # 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: @@ -172,6 +172,7 @@ def _setitem_int(self, key: int, value: Union[BitsType, int]) -> None: 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)))) @@ -195,7 +196,7 @@ def _setitem_slice(self, key: slice, value: BitsType) -> None: def __setitem__(self, key: Union[slice, int], value: BitsType) -> None: if isinstance(key, numbers.Integral): - self._setitem_int(key, value) + self._setitem_int(int(key), value) else: self._setitem_slice(key, value) @@ -573,5 +574,3 @@ def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]] = None, start: def clear(self) -> None: """Remove all bits, reset to zero length.""" self._clear() - - diff --git a/bitstring/bits.py b/bitstring/bits.py index e53efa6f..6893709c 100644 --- a/bitstring/bits.py +++ b/bitstring/bits.py @@ -1699,34 +1699,37 @@ def _pp(self, dtype1: Dtype, dtype2: Optional[Dtype], bits_per_group: int, width @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 - if len(token_list) == 1: - dtype1 = Dtype(*utils.parse_name_length_token(token_list[0])) - dtype2 = None - bits_per_group = dtype1.bitlength + 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: - has_length_in_fmt = False + 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}'.") - elif len(token_list) == 2: - dtype1 = Dtype(*utils.parse_name_length_token(token_list[0])) - dtype2 = Dtype(*utils.parse_name_length_token(token_list[1])) - if dtype1.bitlength is not None and dtype2.bitlength is not None and dtype1.bitlength != dtype2.bitlength: - raise ValueError( - f"Differing bit lengths of {dtype1.bitlength} and {dtype2.bitlength} in format string '{fmt}'.") - bits_per_group = dtype1.bitlength if dtype1.bitlength is not None else dtype2.bitlength - if bits_per_group is None: - has_length_in_fmt = False + 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 - else: - raise ValueError( - f"Only one or two tokens can be used in an pp() format - '{fmt}' has {len(token_list)} tokens.") return dtype1, dtype2, bits_per_group, has_length_in_fmt def pp(self, fmt: Optional[str] = None, width: int = 120, sep: str = ' ', diff --git a/bitstring/bitstore.py b/bitstring/bitstore.py index 6c01f289..cdb199ff 100644 --- a/bitstring/bitstore.py +++ b/bitstring/bitstore.py @@ -14,7 +14,8 @@ def offset_slice_indices_lsb0(key: slice, length: int) -> slice: # 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) -> slice: + +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) @@ -218,7 +219,6 @@ def getslice_lsb0(self, start: Optional[int], stop: Optional[int], /) -> BitStor def getindex_lsb0(self, index: int, /) -> bool: return bool(self._bitarray.__getitem__(-index - 1)) - @overload def setitem_lsb0(self, key: int, value: int, /) -> None: ... @@ -269,4 +269,4 @@ def setitem_msb0(self, key, value, /): self._bitarray.__setitem__(key, value) def delitem_msb0(self, key, /): - self._bitarray.__delitem__(key) \ No newline at end of file + self._bitarray.__delitem__(key) diff --git a/bitstring/bitstore_helpers.py b/bitstring/bitstore_helpers.py index 07e41f9e..41d30d29 100644 --- a/bitstring/bitstore_helpers.py +++ b/bitstring/bitstore_helpers.py @@ -8,7 +8,8 @@ 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 +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 @@ -121,11 +122,13 @@ def p4binary2bitstore(f: Union[str, float]) -> BitStore: 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': @@ -134,6 +137,7 @@ def e4m3mxfp2bitstore(f: Union[str, float]) -> BitStore: 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': @@ -142,6 +146,7 @@ def e5m2mxfp2bitstore(f: Union[str, float]) -> BitStore: 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): @@ -149,6 +154,7 @@ def e3m2mxfp2bitstore(f: Union[str, float]) -> BitStore: 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): @@ -156,6 +162,7 @@ def e2m3mxfp2bitstore(f: Union[str, float]) -> BitStore: 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): @@ -165,6 +172,8 @@ def e2m1mxfp2bitstore(f: Union[str, float]) -> BitStore: 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): @@ -199,6 +208,7 @@ def mxint2bitstore(f: Union[str, float]) -> BitStore: i += 1 return int2bitstore(i, 8, True) + def int2bitstore(i: int, length: int, signed: bool) -> BitStore: i = int(i) try: diff --git a/bitstring/bitstream.py b/bitstring/bitstream.py index 26941e51..8e7c6a14 100644 --- a/bitstring/bitstream.py +++ b/bitstring/bitstream.py @@ -57,7 +57,7 @@ class ConstBitStream(Bits): pos -- The current bit position in the bitstring. """ - __slots__ = ('_pos') + __slots__ = ('_pos',) def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None, offset: Optional[int] = None, pos: int = 0, **kwargs) -> None: diff --git a/bitstring/bitstring_options.py b/bitstring/bitstring_options.py index 619cdbe8..de84ae57 100644 --- a/bitstring/bitstring_options.py +++ b/bitstring/bitstring_options.py @@ -3,6 +3,7 @@ import bitstring import os + class Options: """Internal class to create singleton module options instance.""" @@ -91,4 +92,4 @@ def __new__(cls, use_colour: bool) -> Colour: cls.off = '\033[0m' else: cls.blue = cls.purple = cls.green = cls.off = '' - return x \ No newline at end of file + return x diff --git a/bitstring/dtypes.py b/bitstring/dtypes.py index e1a93e91..86c4ad1d 100644 --- a/bitstring/dtypes.py +++ b/bitstring/dtypes.py @@ -55,7 +55,6 @@ class Dtype: _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 @@ -253,7 +252,7 @@ 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, + 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 @@ -277,7 +276,7 @@ def __init__(self, name: str, set_fn, get_fn, return_type: Any = Any, is_signed: 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: + 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(): @@ -401,4 +400,4 @@ def __repr__(self) -> str: # Create the Register singleton -dtype_register = Register() \ No newline at end of file +dtype_register = Register() diff --git a/bitstring/exceptions.py b/bitstring/exceptions.py index 5a5e0b85..374d3936 100644 --- a/bitstring/exceptions.py +++ b/bitstring/exceptions.py @@ -21,4 +21,3 @@ class ByteAlignError(Error): CreationError = ValueError """Inappropriate argument during bitstring creation.""" - diff --git a/bitstring/luts.py b/bitstring/luts.py index f8667ece..f4ca2986 100644 --- a/bitstring/luts.py +++ b/bitstring/luts.py @@ -5,56 +5,57 @@ 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\xc0\x00\x08\x04A\xfa\xa7\xbf\x7f\xb5\xad\r\x92\x9bU\x00#\x00\xaaD\x80\x00' - b'\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\x04\x08\x10 @' - b'\x80@[\x81A\x04\x08\x10 \x90(0\x8a@\xb2\xc0\xa4d\x81Y\xd9\x02\x8b\x92\x05Ve\x0bl"@\x80' + 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\x11 \x90*\xd0\xf6.\x99\xc1\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 @\x80\x00\x01\x02\x04\x08\x10\xa8\xdaE\x80\x00\x01\x02' - b'\x89\x02\x87\x08$\x0b\x9cJ\x16\xb8\x94-p+Y\xe0Q\xb6\xc0+\x02\x04\x08\x10 @\x80\x00' - b'\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\x04' - b'\x08\x10 \xd0P \xf5\xf1\xb9\xbd\t\x10\xf8~uJ\x98\xcc'), + b'\x08\x14\x14H=>\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\x85q\x03\x01\x0cE\xc1\x0b8\xcc\xcc\x9e0sb;\xdc\x7fUN\x15\x9e?7\xdaW\x81\xb4' - b'\x05HM#\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\x04Z#0&\x02' - b'\x04\x08\x10\xa8(0.\x02\x95\x05&TY`RY\x81N\xed\xa6\xaa7]\xbb\x99\xb67\x9bm.\xdd|' - b'\xb6\x85t\x8b\xd9\x96\xd2-g[I\xb7\x9am-\xddz\xb6\x8dt\x9b\xd9\xb6\xd2mg\xdbI\xb7\x9bm/' - b'\xdd~\xb6\x83t\x87\xd9\x8e\xd2uE\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\x10 @' - b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x81\x11\t\x0cE\x80@U\x81\xd6\xdc)5(\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\x10 @\x80\x00\x01\x02Ms,\x02\x04\x08\x10\xa8' - b'(p"\x02\x95\x05NUY\xe0LY\x81\xf3\xda]T\xef\xb2vWm\xef:\xdbM\xba\xdblw\xe9\xee\xb3=\xa4{' - b'\xcc\xf6\x94\xee9\xdbK\xba\xd7lo\xe9\xde\xb3\xf5\xd2\xf5\xb3\r\xd2}d\xfbL\xf7\x95\xed;' - b'\xddO\xb6\xdft\x7f"@\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\x10 @\x80\x00\x01\x02\x04\x08\x10 ' - b'@\x80\x00\x01\x02\x04\x08\x10 @\x80\xc0\x88\x04\xaa>>\xb77\x01\x02\xc3\x7f3K\xcb\x81'), + 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\xd7Q\x15\x00\x10@\xd1'H\x16D\xa2H\x1a\t\x92\xa3\xe4\xa0\xfdW\xf5h\x81" - b'?\x86\xb9\xe7V\xb0{\n\xd8\x1d\x0cD\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'\x81/"@\x80\x00\x81\xa2\xc0\x88\x08\x94\x05FU\x16\xf8\xaa\x8f\x15\x18k7^o\xa2\xddd' - b'\xbd\xa9v\xd3\xf5f\xda}\xab7\xdbn\xae\xde\xf7v\xf3\xf5~\xb4[\xa8\xb7\xd8n\xa9\xder' - b'\xbb\x95z\xab\xed~\xd6[k\xf7\xab\xdez\xbb\x8dz\x9b\xed\xb6\xeam\x8b\x00\x01\x02\x04\x08\x10' + 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 @\x80\x00\x01\x02\x04\xde%0\x14\x01\x02U\x81Ow\xaf\xd8\xc0' - 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\xc0`\xf0[\x04\x08\x10 P\x14' - b'\xd8\x11\x81\xb2\xc0\xae\xca\x02{\xfaX\x81\xfdv\x7f\xea\x1d\xb4;\xacw\xd4\xee\xb8' - b'\xdeI\xbb\xd3zg\xed\xce\xeb]\xb4\xbb\xacw\xd5\xee\xba\xde\xdfv7\xf5n\xdb\xdd\xd5\xbbo' - b'\xf7P\xef\xb1\xddS\xbd\xe7v/\xf5^\xdb\xfd\xab\xf7_\x04\x08\x10 @\x80\x00\x01\x02\x04\x08' - b'\x10 @\x80\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 \xf0.\x81\xea\xe3s{\x13 0|\x03\xb3O;\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' @@ -65,33 +66,32 @@ 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\xdde\xb6\x96\x05\x00E\xe1\x0f\x01iD\xba\x11\xe9\x90n\x90\x12\xa4\xbb\xbb[\xbaA' - b'X\x8bF\xa5\x94\x94\xee\xee.iP:\x94\x96FA\xe9N\xbd\xfc`\x10{]\xde\xfd\xcc\xe0\xec\t\x9cP' - b'H\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\xc0\x07[' - b' \x82,`\x01\x0bX \x88\x05>\x92\x05\x82\\ \xa2\x82\\ \x92\xd8\x02\x91\x83\xed\xe3\xa0' - b'\x8b\x12lQ\xc3\xbbh\xac\xe8\xb4\x18\xac\x98\xb4X\xac\xd8\xb4OXqh\x9f\xb2\xe2\xd2\xe2\xb1' - b'\xe2\xd3\x12\xb0\x12\xd2\x12\xb1\x12\xd3\x92\xb0\x92\xd2\x92\xb1\x92\xd3R\xb0R\xd2R\xb1' - b'>\xa3\xa5f}NK\xc3JKK\xc7JO\xcb\xc0\xcaH\xcb\xc4\xcaL\xcb\xc2\xcaJ\xfb\x82\x95\x8d\x96\x9d' - b'\x95\x83\x96\x93\x95\x8b\x96\x9b\x95\x87\x96\x97\x95\x8f\x96\x9fU\x80V\x90U\x88V\x98' - b'U\x84\xf6%\xab(\xad\x18\xab8\xad\x04\xab$\xed+V)Zi\xd6\xd7\xb42\xac\xb2\xb4r\xac\xf2\xb4\n' - b'\xac\x8a\xb4J\xac\xca\xb4*\xac\xaa\xb4j\xac\xea\xb4\x1a\xac\x9a\xb4Z\xac\xda\xb4:' - b'\xac\xba\xb4z\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\xacoh\x1dY\x9dh\x9dY]h]Y\xddh\xdd' - b'Y=h=Y\xbdh\xbdY}h}Y\xfdh\xfdY\xdf\xd2\x06\xb0\x06\xca\x02\x16\xb0\x80\x05,`\x01\x0b' - b'X\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80' - b'\x05\xde\x17\x08\x93\x05,\x10\xd4\x02\x1f\xec\xaf\x99\xc3,`\x01\x0bX\xc0\x02\x16\xb0' - b'\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x81Ph\x90,`\x01\x0bX \x88\x05\x06\xcb\x02A.' - b'0DA.0Tl\x81a\xc16<\xe8F\x04\xdb\xc8\xf0\xee;\xd6\xf7\xb4\x1fX\xa3h\xa3YchcY\xe3h?' - b'\xb2~\xa2\x8dgM\xa0MdM\xa2MfM\xa1\xfd\xcc\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b' - b'\xc3\x9aK\x9b\xc7\x9aO[\xc0ZH[\xc4ZL[\xc2ZJ[\xc6ZN[\xc1ZI[\xc5ZM[\xc3ZK[\xc7ZO\xdb' - b'\xc0\xdaH\xdb\xc4\xdaL\xdb\xc2\xdaJ\xdb\xc6\xdaN\xfb\x85\xb5\x83\xb6\x93\xb5\x8b\xb6' - b'\x9b\xb5\x87\xb6\x97\xb5\x8f\xb6\x9fu\x80\xf6+\xeb7\xdaA\xd6!\xdaa\xd6\x11\xdaQ\xd61\xda' - b'q\xd6\t\xdaI\xd6)\xda\xef\xac?h\xa7YghgY\xe7h\xe7Y\x17h\x17Y\x7f\xd2.\xb1.\xd3' - b'\xae\xb0\xae\xd2\xae\xb1\xae\xd3n\xb0n\xd2\xfeb\xfdM\xbb\xc5\xbaM\xfb\x87\xf5/\xed\x0e\xeb.' - b'\xed\x1e\xeb>\xed\x01\xeb!\xed\x11\xeb1\xed\t\xeb)\xed\x19\xeb9\xed\x05\xeb%\xed\x15\xeb5' - b'\xed\r\xeb-\xed?\xd6\xff\xb2\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\x80\x05,`\x81\xf7\x05\x82z|\xeen\x0bX' - b' \xec\x1d\xdd\xb0\xc7z'), + 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" @@ -102,27 +102,22 @@ 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\x82\x1d\x00\x00D\xc1\x89m\xdb\xb6m\xdb\xb6m'\xab\xd8\xb6m\xdb\xb6m\xdb" - b'v\xb2H\x8e1\x8b\xfau\x83~\x17\xe8\xa0\xa0`\x16\xc2\x16R\x17\xca\x16Z\x17\xc6\x16V\x17' - b'\xce\x16^\x17\xc1\x16Q\x17\xc9\x16Y\x17\xc5\x16U\x17\xcd\x16]\x17\xc3\x16S\x17\xcb\x16[\x17' - b'\xc7\x16W\x17\xcf\x16_\x97\xc0\x96P\x97\xc8\x96X\x97\xc4\x96T\x97\xcc\x96\\\x97' - b'\xc2\x96R\x97\xca\x96Z\x97\xc6\x96V\x97\xce\x96^\x97\xc1\x96Q\x97\xc9\x96Y\x97\xc5\x96U\x97' - b'\xcd\x96]\x97\xc3\x96S\x97\xcb\x96[\x97\xc7\x96W\x97\xcf\x96_W\xc0VPW\xc8VXW\xc4VTW\xccV\\W' - b'\xc2VRW\xcaVZW\xc6VVW\xceV^W\xc1VQW\xc9VYW\xc5VUW\xcdV]W\xc3VSW\xcbV[W\xc7VWW\xcfV_\xd7' - b'\xc0\xd6P\xd7\xc8\xd6X\xd7\xc4\xd6T\xd7\xcc\xd6\\\xd7\xc2\xd6R\xd7\xca\xd6Z\xd7' - b'\xc6\xd6V\xd7\xce\xd6^\xd7\xc1\xd6Q\xd7\xc9\xd6Y\xd7\xc5\xd6U\xd7\xcd\xd6]\xd7\xc3\xd6S\xd7' - b'\xcb\xd6[\xd7\xc7\xd6W\xd7\xcf\xd6_7\xc06P7( X\x0b\xfc\x0b\x08\x14\x08\x14P\x0b' - b'\x04\x05\xb7\xc1\xb6!\xba\xa1\xb6a\xba\xe1\xb6\x11\xba\x91\xb6Q\xba\xd1\xb61\xba\xb1' - b'\xb6q\xba\xf1\xb6\t\xba\x89\xb6I\xba\xc9\xb6)\xba\xa9\xb6i\xba\xe9\xb6\x19\xba\x99' - b'\xb6Y\xba\xd9\xb69\xba\xb9\xb6y\xba\xf9\xb6\x05\xba\x85\xb6E\xba\xc5\xb6%\xba\xa5' - b'\xb6e\xba\xe5\xb6\x15\xba\x95\xb6U\xba\xd5\xb65\xba\xb5\xb6u\xba\xf5\xb6\r\xba\x8d' - b'\xb6M\xba\xcd\xb6-\xba\xad\xb6m\xba\xed\xb6\x1d\xba\x9d\xb6]\xba\xdd\xb6=\xba\xbd' - b'\xb6}\xba\xfd\xb6\x03\xba\x83\xb6C\xba\xc3\xb6#\xba\xa3\xb6c\xba\xe3\xb6\x13\xba\x93' - b'\xb6S\xba\xd3\xb63\xba\xb3\xb6s\xba\xf3\xb6\x0b\xba\x8b\xb6K\xba\xcb\xb6+\xba\xab' - b'\xb6k\xba\xeb\xb6\x1b\xba\x9b\xb6[\xba\xdb\xb6;\xba\xbb\xb6{\xba\xfb\xb6\x07\xba\x87' - b"\xb6G\xba\xc7\xb6'\xba\xa7\xb6g\xba\xe7\xb6\x17\xba\x97\xb6W\xba\xd7\xb67\xba\xb7" - b'\xb6w\xba\xf7\xb6\x0f\xba\x8f\xb6O\xba\xcf\xb6/\xba\xaf\xb6o\xba\xef\xb6\x1f\xba\x9f' - b'\xb6_\xba\xdf\xb6?\xba\xbf\x01\xc1Z@=>\x0f\xec\x0e\x14\x08\x14\xf8\xf7\x1f$\x1d\xcd\x0c'), + 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' @@ -133,32 +128,31 @@ 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\x85\xb2\x90\x05\x10@\xe1K\x87t\x83\x94tI\x83\xd2\x1d\xd2\xd2 \xdd\x8dt3C\xa74*' - b'\xdd\x12\x06\x9d"JwHwww\xc3\xe51\xce\\\xfe\xf3\xbd\xc0\xce\x9e\x07\xd8\r\t\x91\x05,`\x01\x0b' - b"X\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,\xf0\xd9\x16\x08'\x0bX\xc0\x02" - b'\x16\x08b\x81\xf0\xb2@\x90\x0bDP\x90\x0bD\x14[ R\xb0E\x0e\xba(\xc1\x165\xac\x8b\xc6\x8aN\xfb' - b'\x82\x15\x83\x16\x93\x15\x8b\x16\x9b\x15\x87\x16\x97\x15\x8f\x16\x9f\x95\x80\x96' - b'\x90\x95\x88\x96\x98\x95\x84\x96\x94\x95\x8c\xf6%+9-\x05+%-\x15+5\xed+V\x1aZZV:ZzV\x06ZFV&Z' - b'fV\x16ZVV6\xda\xd7\xac\xec\xb4\x1c\xac\x9c\xb4\\\xac\xdc\xb4<\xac\xbc\xb4|\xac\xfc\xb4' - b'oX\xdf\xd2\n\xb0\n\xd2\n\xb1\n\xd3\x8a\xb0\x8a\xd2\x8a\xb1\x8a\xd3J\xb0J\xd2J\xb1J\xd3' - b'\xca\xb0\xca\xd2\xca\xb1\xca\xd3\xbecU\xa0UdU\xa2UfU\xa1UeU\xa3}\xcf\xaaN\xab\xc1\xaaI' - b'\xab\xc5\xaaM\xab\xc3\xaaK\xab\xc7\xaaO\xfb\x81\xd5\x80\xd6\x90\xd5\x88\xd6\x98\xd5\x84' - b'\xd6\x94\xd5\x8c\xd6\x9c\xd5\x82\xd6\x92\xd5\x8a\xd6\x9a\xd5\x86\xd6\x96\xd5\x8e' - b"\xd6\x9e\xd5\x81\xd6\x91\xd5\x89\xd6\x99\xf5#\xad\x0b\xab+\xad\x1b\xab;\xad\x07\xab'" - b'\xad\x17\xab7\xad\x0f\xab/\xad\x1f\xab?m\x00k -T\x16\xb0\x80\x05,`\x01\x0bX\xc0' - b'\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,' - b'`\x01\x0bX\x80-\xf0\xd9\xfe5s1\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16' - b'\xb0\x80\x05,`\x01\x0bX $d\x90,`\x01\x0bX \x88\x05\x06\xcb\x02A.0DA.0Tl\x81a\xc16<\xe8F\x04' - b"\xdb\xc8\xb0n\x14k4m\x0ck,\xed'\xd68\xdax\xd6\x04\xdaD\xd6$\xdad\xd6\x14\xdaT\xd6\xcf\xb4" - b'_X\xbf\xd2\xa6\xb1\xa6\xd3f\xb0f\xd2f\xb1f\xd3\xe6\xb0\xe6\xd2\xe6\xb1\xe6\xd3' - b"\x16\xb0\x16\xd2\x16\xb1~\xa3-f-\xa1-e-\xa3\xfd\xce\xfa\x83\xf6'\xeb/\xdar\xd6\n\xdaJ\xd6*" - b'\xdaj\xd6\x1a\xdaZ\xd6:\xdaz\xd6\x06\xdaF\xd6&\xda\xdf\xac\xcd\xb4\x7fX[h\xff\xb2\xfe\xa3mem' - b'\xa3mg\xed\xa0\xedd\xed\xa2\xedf\xed\xa1\xede\xed\xa3\xedg\x1d\xa0\x1dd\x1d\xa2\x1df\x1d' - b'\xa1\xfd\xcf:J;\xc6:N;\xc1:I;\xc5:M;\xc3:K;\xc7:O\xbb\xc0\xbaH\xbb\xc4\xbaL\xbb\xc2\xba' - b'J\xbb\xc6\xbaN\xbb\xc1\xbaI\xbb\xc5\xbaM\xbb\xc3\xbaK\xbb\xc7\xbaO{\xc0zH{\xc4zL{\xc2z' - b'J{\xc6zN{\xc1zI{\xc5zM{\xc3zK{\xc7zO\xfb\xc0\xfaHc\xcf\xf7;\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' - b'\x16\xb0\x80\x05,`\x01\x0b\x84~\x02\x87va\xdb'), + 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" @@ -169,24 +163,27 @@ 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\xd6\x1d\x06\x00\x85\xd1?\xb6m[\r\x1a\xdb\xb6\xd94nl\xbe\xc4\xb6m\xdb' - b'v\xd2\xd8n\xd0\xd8\xb6\xd6J\x86q\x1f\xf6\xdd38\xdf\x04NHH\x80\x85\xb2\x85\xd6\x85\xb1' - b'\x85\xd5\x85\xb3\x85\xd7E\xb0E\xd4E\xb2E\xd6E\xb1E\xd5E\xb3E\xd7\xc5\xb0\xc5\xd4\xc5\xb2' - b'\xc5\xd6\xc5\xb1\xc5\xd5\xc5\xb3\xc5\xd7%\xb0%\xd4%\xb2%\xd6%\xb1%\xd5%\xb3%\xd7\xa5\xb0' - b'\xa5\xd4\xa5\xb2\xa5\xd6\xa5\xb1\xa5\xd5\xa5\xb3\xa5\xd7e\xb0e\xd4e\xb2e\xd6e\xb1e\xd5e\xb3' - b'e\xd7\xe5\xb0\xfd\xa1\xcbi\xcb\xa5\xcbm\xcb\xa3\xfb\xd3\x96W\x97\xcf\x96_W\xc0VPW\xc8' - b'VXW\xc4VTW\xccV\\W\xc2VRW\xcaVZW\xc6VVW\xceV^W\xc1VQW\xc9VYW\xc5VUW\xcdV]W\xc3VSW\xcb' - b'V[W\xc7VWW\xcfV_\xd7\xc0\xd6P\xd7\xc8\xd6X\xd7\xc4\xd6T\xd7\xcc\xf6\x97\xae\xb9\xedo]\x0b' - b'[K]+[k]\x1b[[];\xdb?\xba\xf6\xb6\x0e\xba\x8e\xb6N\xba\xce\xb6.\xba\xae\xb6n\xba\xee' - b'\xb6\x1e\xba\x9e\xb6^\xba\xde\xb6>\xba\xbe\xb6~\xba\xfe\xb6\x01\x81\xf6+(X X@-\x10' - b'\x12h\x03m\x83t\x83mCtCm\xc3t\xc3m#t#m\xa3t\xa3mctcm\xe3t\xe3m\x13t\x13m\x93t\x93mStSm' - b'\xd3t\xd3m3t3m\xb3t\xb3mstsm\xf3t\xf3m\x0bt\x0bm\x8bt\x8bmKtKm\xcbt\xcbm+t+m\xabt\xabmktkm' - b'\xebt\xebm\x1bt\x1bm\x9bt\x9bm[t[m\xdbt\xdbm;t;m\xbbt\xbbm{t{m\xfbt\xfbm\x07t\x07m' - b'\x87t\x87mGtGm\xff\xea\x8e\xd9\x8e\xebN\xd8N\xeaN\xd9N\xeb\xce\xd8\xce\xea\xce\xd9' - b'\xce\xeb.\xd8.\xea.\xd9.\xeb\xae\xd8\xae\xea\xae\xd9\xae\xebn\xd8\xfe\xd3\xdd\xb4' - b'\xdd\xd2\xdd\xb6\xdd\xd1\xfdo\xbb\xab\xbbg\xbb\xaf{`{\xa8{d{\xac{b{\xaa{f{\xae{a{\xa9{e' - b'{\xad{c{\xab{g{\xaf\xfb`\xfb\xa8\xfbd\xfb\xac\xfbb\xfb\xaa\xfbf\xfb\xae\xfba\xfb\x19h\xea' - b'\xf1ypw\xb0@\xb0\xc0\xaf\xdf\xf0\x99\xce\x0e')} + 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 = \ diff --git a/bitstring/mxfp.py b/bitstring/mxfp.py index bcbae947..d675c359 100644 --- a/bitstring/mxfp.py +++ b/bitstring/mxfp.py @@ -6,6 +6,7 @@ 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. @@ -56,7 +57,7 @@ def __init__(self, exp_bits: int, mantissa_bits: int, bias: int, mxfp_overflow: self.pos_clamp_value = 0b01111100 # +inf self.neg_clamp_value = 0b11111100 # -inf - # If we calculate these LUTs now now it creates a bootstrap problem in generate_luts.py. + # 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 @@ -91,7 +92,15 @@ def slow_float_to_int(self, f: float) -> int: # The output int has the binary sequence needed for the float. length = 1 + self.exp_bits + self.mantissa_bits values = 1 << length - if f >= 0: + # 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] @@ -101,7 +110,7 @@ def slow_float_to_int(self, f: float) -> int: if x is not None: return x return self.pos_clamp_value - if f < 0: + else: # Negative, so top bit is set for i in range(values // 2, values - 1): lower = self.lut_int_to_float[i + 1] @@ -112,11 +121,6 @@ def slow_float_to_int(self, f: float) -> int: return x # Clip to negative max return self.neg_clamp_value - assert 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 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""" @@ -154,7 +158,8 @@ def createLUT_for_float16_to_mxfp(self) -> bytes: length = 1 + self.exp_bits + self.mantissa_bits if length == 8: import gfloat - fi = gfloat.formats.format_info_ocp_e5m2 if self.exp_bits == 5 else gfloat.formats.format_info_ocp_e4m3 + 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): @@ -164,7 +169,11 @@ def createLUT_for_float16_to_mxfp(self) -> bytes: if math.isnan(fp): fp8_i = 0b11111111 else: - fp8_i = self.lut_int_to_float.index(fp) + # 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: @@ -174,9 +183,6 @@ def createLUT_for_float16_to_mxfp(self) -> bytes: b = struct.pack('>H', i) f, = struct.unpack('>e', b) fp8_i = self.slow_float_to_int(f) - if fp8_i == 1 << (self.exp_bits + self.mantissa_bits): - # Got back int representing binary digits for negative zero. Just convert to positive zero instead. - fp8_i = 0 fp16_to_fp8[i] = fp8_i return bytes(fp16_to_fp8) diff --git a/bitstring/utils.py b/bitstring/utils.py index c7081a47..4dae4d9b 100644 --- a/bitstring/utils.py +++ b/bitstring/utils.py @@ -21,11 +21,11 @@ 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[<>@=]){1}(?P(?:\d*[bBhHlLqQefd])+)$') +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[<>@=]){1}(?P[bBhHlLqQefd])$') +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]') @@ -74,6 +74,7 @@ def structparser(m: Match[str]) -> List[str]: 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 @@ -94,6 +95,7 @@ def parse_name_length_token(fmt: str, **kwargs) -> Tuple[str, Optional[int]]: 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): @@ -110,6 +112,7 @@ def parse_single_struct_token(fmt: str) -> Optional[Tuple[str, Optional[int]]]: 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: @@ -132,6 +135,7 @@ def parse_single_token(token: str) -> Tuple[str, str, Optional[str]]: length = token return name, length, value + @functools.lru_cache(CACHE_SIZE) def preprocess_tokens(fmt: str) -> List[str]: # Remove whitespace and expand brackets @@ -143,7 +147,7 @@ def preprocess_tokens(fmt: str) -> List[str]: final_tokens = [] for meta_token in meta_tokens: - if meta_token == '': + if meta_token == '': continue # Extract factor and actual token if a multiplicative factor exists factor = 1 @@ -204,6 +208,7 @@ def tokenparser(fmt: str, keys: Tuple[str, ...] = ()) -> \ BRACKET_RE = re.compile(r'(?P\d+)\*\(') + def expand_brackets(s: str) -> str: """Expand all brackets.""" while True: diff --git a/doc/array.rst b/doc/array.rst index 0e879fbc..2dfcb3e6 100644 --- a/doc/array.rst +++ b/doc/array.rst @@ -10,128 +10,136 @@ Array 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 ``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. :: + 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. :: - import array - import bitstring + >>> bitstring.Array('i4', 8) + Array('int4', [0, 0, 0, 0, 0, 0, 0, 0]) - x = array.array('f', [1.0, 2.0, 3.14]) - y = bitstring.Array('=f', [1.0, 2.0, 3.14]) + 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`. - 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. +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. :: - For example ``'uint4'``, ``'bfloat'`` or ``'hex12'`` can be used, and the endianness of multi-byte dtypes can be properly specified. + import array + import bitstring - Each element in the ``Array`` must then be something that makes sense for the ``dtype``. - Some examples will help illustrate:: + x = array.array('f', [1.0, 2.0, 3.14]) + y = bitstring.Array('=f', [1.0, 2.0, 3.14]) - from bitstring import Array + assert x.tobytes() == y.tobytes() - # Each unsigned int is stored in 4 bits - a = Array('uint4', [0, 5, 5, 3, 2]) +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. - # Convert and store floats in 8 bits each - b = Array('p3binary', [-56.0, 0.123, 99.6]) +For example ``'uint4'``, ``'bfloat'`` or ``'hex12'`` can be used, and the endianness of multi-byte dtypes can be properly specified. - # Each element is a 7 bit signed integer - c = Array('int7', [-3, 0, 120]) +Each element in the ``Array`` must then be something that makes sense for the ``dtype``. +Some examples will help illustrate:: - You can then access and modify the ``Array`` with the usual notation:: + from bitstring import Array - a[1:4] # Array('uint4', [5, 5, 3]) - b[0] # -56.0 - c[-1] # 120 + # Each unsigned int is stored in 4 bits + a = Array('uint4', [0, 5, 5, 3, 2]) - a[0] = 2 - b.extend([0.0, -1.5]) + # Convert and store floats in 8 bits each + b = Array('p3binary', [-56.0, 0.123, 99.6]) - 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. :: + # Each element is a 7 bit signed integer + c = Array('int7', [-3, 0, 120]) - >>> 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 then access and modify the ``Array`` with the usual notation:: - 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. :: + a[1:4] # Array('uint4', [5, 5, 3]) + b[0] # -56.0 + c[-1] # 120 - >>> x = Array('int16', [-5, 100, -4]) - >>> x - Array('int16', [-5, 100, -4]) - >>> x.dtype = 'int8' - >>> x - Array('int8', [-1, -5, 0, 100, -1, -4]) + 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. :: - 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. + >>> 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]. - 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. +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. :: - 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. + >>> 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 :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'``. +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. - 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. +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 correspondence between the big-endian type codes and bitstring dtype strings is given in the table below. +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'``. - ========= =================== - 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'`` - ========= =================== +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 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: +The correspondence between the big-endian type codes and bitstring dtype strings is given in the table below. - ============ ============================= - Type code bitstring dtype - ============ ============================= - ``'>H'`` ``'uint16'`` / ``'uintbe16'`` - ``'=H'`` ``'uintne16'`` - ``'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: - Note that: +============ ============================= +Type code bitstring dtype +============ ============================= +``'>H'`` ``'uint16'`` / ``'uintbe16'`` +``'=H'`` ``'uintne16'`` +``'>> b + 1 Array('i6', [31, -9, 2, 1]) >>> b + b - Array('i6', [30, -10, 1, 0, 30, -10, 1, 0]) + Array('i6', [60, -20, 2, 0]) Comparison operators will output an ``Array`` with a ``dtype`` of ``'bool'``. diff --git a/pyproject.toml b/pyproject.toml index 99673e95..b28695df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bitstring" -version = "4.2.2" +version = "4.2.3" authors = [ { name="Scott Griffiths", email="dr.scottgriffiths@gmail.com" }, ] diff --git a/release_notes.txt b/release_notes.md similarity index 55% rename from release_notes.txt rename to release_notes.md index a9abc2c9..ab1e0aba 100644 --- a/release_notes.txt +++ b/release_notes.md @@ -1,31 +1,31 @@ --------------------------------- -bitstring module version history --------------------------------- - ------------------------ -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. + +# 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 -------------------------- +## 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 @@ -34,99 +34,98 @@ 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: +#### 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. +* 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: +#### Other changes -* The Array class is no longer 'beta'. +* The `Array` class is no longer 'beta'. -* A new Dtype class can be optionally used to specify types. +* 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.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. +* 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. + * `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 ----------------------------- +### 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. +* `'bytes'` token can't be used without explicit length. Bug #303. + +### November 2023: version 4.1.3 ----------------------------- -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'. +* 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, +### 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'. +* 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. +* 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'. +### August 2023: version 4.1.1 released -* bitarray dependency now pinned to ">=2.8.0, <3.0.0" rather than a specific version. Bug #283. +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. +* 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 'nan' values for floating point types. +* `Array.count()` can now count `float('nan')` values for floating point types. + +## August 2023: version 4.1.0 released ------------------------------------ -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!) -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 +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. +#### 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 @@ -143,99 +142,100 @@ the same API. Huge kudos to Ilan Schnell for all his work on bitarray. -* New Array class for homogeneous data (beta) +#### New Array class for homogeneous data (beta) -If your data is all of the same 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 +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. - >>> 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 +```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: +#### Other changes -* Added two new floating point interpretations: float8_143 and float8_152. These are 8-bit +* 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 +* 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. +* 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 + 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. + this was not well-defined. -* Only empty bitstring are now considered False in a boolean sense. Previously s was - False is 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 +* 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. +* 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 '.' +* 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. +* 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. +### 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 +* 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. +* 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.1 released -------------------------------------- +## 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 @@ -244,38 +244,40 @@ with fixes, but no new features. Other breaking changes are minimal, and there are a few cool features added. -Breaking changes: +#### 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 +* 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 - default +* 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. + 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: +#### 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 `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 + 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 +* 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 @@ -287,44 +289,46 @@ New features and improvements: 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 +* 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. :: - - >>> 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 :: - - >>> s.floatle64 = 10.511 - + 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. + 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 signficant figures of + format that keeps its range but only has a couple of significant figures of accuracy. ---------------------------------------- -July 20th 2021: version 3.1.9 released ---------------------------------------- +### July 20th 2021: version 3.1.9 released + (version 3.1.8 was pulled due to serious issues) Another maintenance release. @@ -336,9 +340,8 @@ Another maintenance release. * 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 ---------------------------------------- +### 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. @@ -348,19 +351,20 @@ feature to allow bits to be indexed in the opposite direction. * Fixing a few stale links to documentation. (Issue 194) * Allowing initialisation with an io.BytesIO object. (Issue 189) -Experimental LSB0 mode ----------------------- +#### 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 +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 - - >>> bitstring.set_lsb0(True) +```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 @@ -369,12 +373,13 @@ finalising the interface. Slicing is still done with the start bit smaller than the end bit. For example: - - >>> s = Bits('0b000000111') - >>> s[0:5] - Bits('0b00111') - >>> s[0] - True +```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]`. @@ -382,9 +387,7 @@ 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 ---------------------------------------- +### July 9th 2019: version 3.1.6 released A long overdue maintenance release with some fixes. * Fixed immutability bug. Bug 176. @@ -394,74 +397,58 @@ A long overdue maintenance release with some fixes. * 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 ---------------------------------------- +### 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 ---------------------------------------- +### 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 ---------------------------------------- +### 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 ---------------------------------------- +### 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 ---------------------------------------- +### 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 ------------------------------------------- +## 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 ---------------- +#### 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. - >>> a, b = s.readlist('pad:5, uint:3, pad:1, uint:3') - +```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 --------------------------------------- +#### 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[:]. +`t = s.copy()` is equivalent to `t = s[:]`, and `s.clear()` is equivalent to `del s[:]`. -Other changes -------------- +#### Other changes * Some bug fixes. ------------------------------------------ -February 7th 2012: version 3.0.2 released ------------------------------------------ +### 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). @@ -469,181 +456,172 @@ This is a minor update that fixes a few bugs. * 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 +`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 ------------------------------------------- +### 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 ------------------------------------------- +## November 21st 2011: version 3.0.0 released This is a major release which breaks backward compatibility in a few places. -Backwardly incompatible changes -=============================== +### Backwardly incompatible changes -Hex, oct and bin properties don't have leading 0x, 0o and 0b ------------------------------------------------------------- +#### 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 +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. - >>> a = BitArray('0x123') - >>> a.hex, a.oct, a.bin - ('123', '0443', '000100100011') -Previously this would have returned ('0x123', '0o0443', '0b000100100011') +```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 ------------------------------ +#### 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. +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. +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. +The `ConstBitArray` name is kept as an alias for `Bits`. -Stepping in slices has conventional meaning -------------------------------------------- +#### Stepping in slices has conventional meaning -The step parameter in __getitem__, __setitem__ and __delitem__ used to act +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]. +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: - >>> s = BitArray('0x0000') - s[::4] = [1, 1, 1, 1] - >>> s.hex - '8888' - >>> del s[8::2] - >>> s.hex - '880' - +```pycon +>>> s = BitArray('0x0000') +s[::4] = [1, 1, 1, 1] +>>> s.hex +'8888' +>>> del s[8::2] +>>> s.hex +'880' +``` -New features -============ +### New features -New readto method ------------------ +#### 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: - >>> s = ConstBitStream('0x47000102034704050647') - >>> s.readto('0x47', bytealigned=True) - BitStream('0x47') - >>> s.readto('0x47', bytealigned=True) - BitStream('0x0001020347') - >>> s.readto('0x47', bytealigned=True) - BitStream('0x04050647') +```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 ------------------------------------------------ +#### 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. +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: - fmt = ['hex:8', 'bin:3'] - a = pack(fmt, '47', '001') - a.unpack(fmt) +```python +fmt = ['hex:8', 'bin:3'] +a = pack(fmt, '47', '001') +a.unpack(fmt) +``` --------------------------------------- -June 18th 2011: version 2.2.0 released --------------------------------------- +## 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 interleaved exponential-Golomb interpretations New bit interpretations for interleaved exponential-Golomb (as used in the -Dirac video codec) are supplied via 'uie' and 'sie': +Dirac video codec) are supplied via `uie` and `sie`: - >>> s = BitArray(uie=41) - >>> s.uie - 41 - >>> s.bin - '0b00010001001' +```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 --------------------------------------- +### 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 +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: - >>> 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,) +```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 -------------- +### 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 ------------------------------------------- +### 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. +* 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 ------------------------------------------ +## 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 +### 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 +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. +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 / \ / \ @@ -651,23 +629,22 @@ The classes hierarchy is: \ / \ / BitStream (formerly BitString) +``` - -Other changes -------------- +### 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 + 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 +* 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 @@ -678,16 +655,12 @@ most of which won't be noticed by the end user. Some things you might see are: 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 --------------------------------------- +### 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. + 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 25th 2010: version 2.0.2 released --------------------------------------- +## 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. @@ -696,15 +669,16 @@ 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. -The backwardly incompatible changes are: ------------------------------------------ -* Methods removed. +### 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 @@ -737,6 +711,7 @@ 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 @@ -744,236 +719,269 @@ 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. +#### 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. +#### 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 +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]'. +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. +#### Exception raised on reading off end of bitstring. -If a read or peek goes beyond the end of the bitstring then a ReadError +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. +#### BitStringError renamed to Error. -The base class for errors in the bitstring module is now just Error, so -it will likely appears in your code as bitstring.Error instead of -the rather repetitive bitstring.BitStringError. +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. +#### 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 +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 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 +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. +#### 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. +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: +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 +``` +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. +``` +#### 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'. +#### 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. +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. +#### Iterables instead of *format accepted for some methods. -This means that for the affected methods (unpack, readlist and peeklist) you +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 +Note that you could still use the single string `'uint:12, hex:4, bin:7'` if you preferred. -* Bool auto-initialisation removed. +#### 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). +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 +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 +``` +#### 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 +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 +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 +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. +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 +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. +#### 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 +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 +(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 +... 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 (). +position zero can still be `True` - it's the tuple `(0,)` - whereas not found can +be False - the empty tuple `()`. -The new features in this release are: -------------------------------------- -* New count method. +### New features -This method just counts the number of 1 or 0 bits in the bitstring. +#### 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. +#### read and peek methods accept integers. -The read, readlist, peek and peeklist methods now accept integers as parameters +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 +#### 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 +`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. +#### 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 +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. ->>> b = Bits(b'abcd\x23\x00') # Only Python 3! +```pycon +>>> b = Bits(b'abcd\x23\x00') # Only Python 3! +``` -* set, invert, all and any default to whole bitstring. +#### 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? @@ -981,37 +989,38 @@ True >>> a.any(0) # are any set to 0? False >>> a.invert() # invert every bit +``` -* New exception types. +#### New exception types. -As well as renaming BitStringError to just Error +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. +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. +ByteAlignError(Error) # whole byte position or length needed. -CreationError(Error, ValueError) - inappropriate argument during -bitstring creation. +ReadError(Error, IndexError) # reading or peeking off the end of the bitstring. -InterpretError(Error, ValueError) - inappropriate interpretation of -binary data. +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. +## 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 @@ -1025,134 +1034,141 @@ BitString('0x00112233445566') 1 >>> s BitString('0x11006655443322') +``` -* Multiplicative factors in bitstring creation and reading. +#### Multiplicative factors in bitstring creation and reading. For example: +```pycon >>> s = Bits('100*0x123') +``` -* Token grouping using parenthesis. +#### Token grouping using parenthesis. For example: +```pycon >>> s = Bits('3*(uint:6=3, 0b1)') +``` -* Negative slice indices allowed. +#### Negative slice indices allowed. -The start and end parameters of many methods may now be negative, with the +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. +#### Sequence ABCs used. -The Bits class now derives from collections.Sequence, while the BitString -class derives from collections.MutableSequence. +The `Bits` class now derives from `collections.Sequence`, while the `BitString` +class derives from `collections.MutableSequence`. -* Keywords allowed in readlist, peeklist and unpack. +#### 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. +#### start and end parameters added to rol and ror. -* join function accepts other iterables. +#### join function accepts other iterables. -Also its parameter has changed from 'bitstringlist' to 'sequence'. This is +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. +#### `__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 +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. +#### More optimisations. -* Bug fixed in replace method (it could fail if start != 0). +#### 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 ----------------------------------------------------------------- +### January 19th 2010: version 1.2.0 for Python 2.6 and 3.x released -* New 'Bits' class. +#### New `Bits` class. -Introducing a brand new class, Bits, representing an immutable sequence of +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: +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. +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. +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 +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 +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. +The relationship between `Bits` and `BitString` is supposed to loosely mirror that +between `bytes` and `bytearray` in Python 3. -* Deprecation messages turned on. +#### 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. +The deprecated methods are: `advancebit`, `advancebits`, `advancebyte`, `advancebytes`, +`retreatbit`, `retreatbits`, `retreatbyte`, `retreatbytes`, `tell`, `seek`, `slice`, `delete`, +`tellbyte`, `seekbyte`, `truncatestart` and `truncateend`. -* Initialise from bool. +#### 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. +#### Improved efficiency. -More methods have been speeded up, in particular some deletions and insertions. +More methods have been sped up, in particular some deletions and insertions. -* Bug fixes. +#### Bug fixes. A rare problem with truncating the start of bitstrings was fixed. -A possible problem outputting the final byte in tofile() 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 ------------------------------------------------------------------ +### 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 ------------------------------------------------------------------ +### 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. @@ -1165,50 +1181,57 @@ The speed of many typical operations has been increased, some substantially. 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 +* 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 ------------------------------------------------------------------ +### 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. +#### Improved efficiency. A fair number of functions have improved efficiency, some quite dramatically. -* New bit setting and checking functions. +#### 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. -* New rotate functions. - -ror / rol : Rotate bits to the right or left respectively. + `ror` / `rol` : Rotate bits to the right or left respectively. +```pycon >>> s = BitString('0b100000000') >>> s.ror(2) >>> s.bin @@ -1216,37 +1239,39 @@ ror / rol : Rotate bits to the right or left respectively. >>> s.rol(5) >>> s.bin '0b000000100' +``` +#### Floating point interpretations. -* Floating point interpretations. - -New float initialisations and interpretations are available. These only work -for BitStrings of length 32 or 64 bits. - + 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. +``` +#### '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. +``` +#### '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 --------------------------------------------------------- -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 @@ -1255,9 +1280,8 @@ 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 -------------------------------------------------------- +## 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 @@ -1270,113 +1294,120 @@ 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 ** +### API Changes -* New read / peek functions for returning multiple items. +#### New `read` / `peek` functions for returning multiple items. -The functions read, readbits, readbytes, peek, peekbits and peekbytes now only +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 +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. +``` +#### 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. -* 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 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. +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. +#### 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.prepend('0xff') >>> s.reverse() >>> t = s +``` +Affects `truncatestart`, `truncateend`, `insert`, `overwrite`, `delete`, `append`, +`prepend`, `reverse` and `reversebytes`. -Affects truncatestart, truncateend, insert, overwrite, delete, append, -prepend, reverse and reversebytes. - -* Properties renamed. +#### 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'. +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 +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. +#### Other changes. -The unpack function now always returns a list, never a single item. +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'). +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'). +Removed support for the `'bytes'` token in format strings. Instead of +`s.read('bytes:4')` use `s.read('bits:32')`. -** New features ** +### New features -* Added endswith and startswith functions. +#### Added endswith and startswith functions -These do much as you'd expect; they return True or False depending on whether +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 ----------------------------------------------------------- -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 @@ -1384,6 +1415,7 @@ 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 @@ -1392,26 +1424,27 @@ 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. -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. +* 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 +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 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 ------------------------------------------------------ +``` +### 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. +`bytealigned` in `find`, `replace`, `split`, etc. See the user manual for more details on each of these items. @@ -1477,109 +1514,122 @@ See the user manual for more details on each of these items. 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. +``` +* 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. - +``` +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. +* `hex()` and `oct()` functions removed. -The special functions for hex() and oct() have been removed. Please use the +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 +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 +``` +* 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 -* 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 +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 +* `readue` and `readse` functions have been removed -Instead you should use the new read function with a 'ue' or 'se' token: +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. +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 ------------------------------------------------------ +### 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. @@ -1590,50 +1640,53 @@ Changes in version 0.4.3 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 +``` +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__`. -* 'slice' function now equivalent to __getitem__. - -This means that a step can also be given to the slice function so that the +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. +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 ----------------------------------------------------- +### 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, +0.4.0, but with the slight exception of `findall()` returning a generator, detailed below. Changes in version 0.4.2 @@ -1646,75 +1699,78 @@ 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]. +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 +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] +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 `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]. +`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 +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 +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) +``` -* Less exceptions raised +* Fewer exceptions raised -Some changes have been made to slicing so that less exceptions are 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. +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 +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 -* Miscellany - -reversebits() now has optional startbit and endbit parameters. +* `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 +* 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(). +* Improved efficiency of rfind(). -A couple of minor bugs were fixed. See the issue tracker for details. +* A couple of minor bugs were fixed. See the issue tracker for details. ------------------------------------------------------ -April 23rd 2009: Python 3 only version 0.4.1 released ------------------------------------------------------ +### 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 @@ -1722,95 +1778,94 @@ 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 ---------------------------------------- +### 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 - +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__. +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() +* 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: +`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 +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 +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 +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 ---------------------------------------- +### 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 +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: +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 +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 +``` +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 ------------------------------------------- -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 @@ -1820,111 +1875,118 @@ break backwards compatibility. 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). +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 *. +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 +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. +`python setup.py install`. + +### February 15th 2009: version 0.3.0 released ------------------------------------------- -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'. +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 +``` +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 +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 +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='. +when initialising using `bin=`. -Also, if you just print a BitString with no interpretation it will pick +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. +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 ------------------------------------------ +### 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! + +## December 29th 2008: version 0.1.0 released + +First release! :tada: diff --git a/tests/requirements.txt b/tests/requirements.txt index 01bb7a76..669577b2 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,4 @@ pytest >= 7.4.2, < 8 hypothesis >= 6.98.13, < 7 -gfloat >= 0.1.0, < 0.2 \ No newline at end of file +gfloat >= 0.2.1, < 0.3 +pytest-benchmark >= 4.0.0, < 4.1 \ No newline at end of file diff --git a/tests/stress_test.py b/tests/stress_test.py deleted file mode 100644 index 30071c1d..00000000 --- a/tests/stress_test.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -import sys -sys.path.insert(0, '..') -from bitstring import Bits, BitStream -import bitstring -import time -import random -import cProfile -import pstats -import math -# Some performance tests. Each is a task - it is permissible to rewrite -# to do it in a different way. - - -# TEST 1: Create a bitstring, read every 3 bits and count how many equal '001'. -def perf1(): - s = bitstring.Bits('0xef1356a6200b3, 0b0') - s *= 6000 - c = 0 - for triplet in s.cut(3): - if triplet == '0b001': - c += 1 - assert c == 12000, c - - -def perf2(): - s = bitstring.BitArray(100000000) - s.set(1, [10, 100, 1000, 100000]) - count = s.count(1) - assert count == 4 - - -def perf3(): - 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) - - -def perf4(): - 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))) - assert x == 289 - - -def perf5(): - s = set() - s2 = set() - random.seed(12) - for _ in range(20000): - v = random.randint(0, 2**10) - s.add(bitstring.ConstBitStream(uint=v, length=1001, pos=v % 1000)) - s2.add(v) - assert len(s) == len(s2) - - -def perf6(): - 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') - -def perf7(): - 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'))) - assert twin_primes == 8169 - -def run(f): - start_time = time.perf_counter() - print("Running {0}".format(str(f))) - f() - print("Took {0} s".format(time.perf_counter() - start_time)) - - -def main(): - start_time = time.perf_counter() - run(perf1) - run(perf2) - run(perf3) - run(perf4) - run(perf5) - run(perf6) - run(perf7) - print("Total time {0} s".format(time.perf_counter() - start_time)) - - -if __name__ == '__main__': - print(f"bitstring version {bitstring.__version__}") - cProfile.run('main()', 'stats') - p = pstats.Stats('stats') - p.sort_stats('time').print_stats(10) diff --git a/tests/test_array.py b/tests/test_array.py index 54aeab77..f1e974c7 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -242,7 +242,7 @@ def test_setting(self): a = Array('bool') a.data += b'\x00' a[0] = 1 - assert a[0] == True + assert a[0] is True b = Array('h12') with pytest.raises(ValueError): diff --git a/tests/test_benchmarks.py b/tests/test_benchmarks.py new file mode 100644 index 00000000..73e9e53c --- /dev/null +++ b/tests/test_benchmarks.py @@ -0,0 +1,72 @@ +import 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 diff --git a/tests/test_bitarray.py b/tests/test_bitarray.py index 33f712fc..53a1b044 100644 --- a/tests/test_bitarray.py +++ b/tests/test_bitarray.py @@ -962,9 +962,9 @@ class TestNumpy: def test_getting(self): a = BitArray('0b110') p = np.int_(1) - assert a[p] == True + assert a[p] is True p = np.short(0) - assert a[p] == True + assert a[p] is True @pytest.mark.skipif(not numpy_installed, reason="numpy not installed.") def test_setting(self): @@ -989,4 +989,4 @@ def test_bytes_from_list(): s = BitArray(bytes=bytearray([1, 2])) assert s == '0x0102' s.bytes = [10, 20] - assert s == '0x0a14' \ No newline at end of file + assert s == '0x0a14' diff --git a/tests/test_bits.py b/tests/test_bits.py index 55161003..a79fef5e 100644 --- a/tests/test_bits.py +++ b/tests/test_bits.py @@ -53,15 +53,10 @@ def test_creation_from_hex_with_whitespace(self): s = Bits(hex=' \n0 X a 4e \r3 \n') assert s.hex == 'a4e3' - def test_creation_from_hex_errors(self): + @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='0xx0') - with pytest.raises(bitstring.CreationError): - Bits(hex='0xX0') - with pytest.raises(bitstring.CreationError): - Bits(hex='0Xx0') - with pytest.raises(bitstring.CreationError): - Bits(hex='-2e') + Bits(hex=bad_val) with pytest.raises(bitstring.CreationError): Bits('0x2', length=2) with pytest.raises(bitstring.CreationError): @@ -104,7 +99,6 @@ def test_creation_from_uint_errors(self): def test_creation_from_int(self): s = Bits(int=0, length=4) - temp = s.hex assert s.bin == '0000' s = Bits(int=1, length=2) assert s.bin == '01' @@ -120,15 +114,10 @@ def test_creation_from_int(self): assert (s.int, s.length) == (value, length) _ = Bits(int=10, length=8) - def test_creation_from_int_errors(self): - with pytest.raises(bitstring.CreationError): - _ = Bits(int=-1, length=0) - with pytest.raises(bitstring.CreationError): - _ = Bits(int=12) - with pytest.raises(bitstring.CreationError): - _ = Bits(int=4, length=3) + @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=-5, length=3) + _ = Bits(int=int_, length=length) def test_creation_from_se(self): for i in range(-100, 10): @@ -517,16 +506,16 @@ def teardown_class(cls): def test_get_single_bit(self): a = Bits('0b000001111') - assert a[0] == True - assert a[3] == True - assert a[4] == False - assert a[8] == False + 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] == False - assert a[-5] == False - assert a[-6] == True - assert a[-9] == True + 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] @@ -582,7 +571,7 @@ def test_uint(self): a = Bits('0x01') assert a == '0b00000001' assert a.uint == 1 - assert a[0] == True + assert a[0] is True def test_float(self): a = Bits(float=0.25, length=32) @@ -736,7 +725,7 @@ def test_multi_line_multi_format(self): b = Bits(bytes=a) s = io.StringIO() b.pp(stream=s, fmt='bytes') - assert remove_unprintable(s.getvalue()) == """ [ + 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 ǴǵǶǷ ǸǹǺǻ ǼǽǾÿ diff --git a/tests/test_mxfp.py b/tests/test_mxfp.py index 5d28434d..ce2d7053 100644 --- a/tests/test_mxfp.py +++ b/tests/test_mxfp.py @@ -233,6 +233,15 @@ def test_auto_scaling2(): 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]) @@ -250,6 +259,10 @@ 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) @@ -438,3 +451,16 @@ def test_mxint_rounding(): 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