This commit is contained in:
Jonas Zeunert
2024-08-16 21:57:55 +02:00
parent adeb5c5ec7
commit 4309a2d185
1696 changed files with 279655 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
Metadata-Version: 2.1
Name: PyBoard
Version: 1.1.4
Summary: A Python package for programming with Arduino.
Home-page: https://github.com/kaiyu-liu/PyBoard
Author: Kevin Liu
Author-email: winaes@126.com
License: GPL v3
Classifier: Environment :: Other Environment
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Natural Language :: English
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft
Classifier: Operating System :: POSIX
Classifier: Operating System :: Unix
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Education
Classifier: Programming Language :: Python :: 3.5
Requires-Dist: pymata-aio >=2.25
# PyBoard
A project that helps to use Python in STEM projects easily. The project is based on PyMata and makes the APIs as close to the functions in Arduino as possible.
== Installation
pip install PyBoard
== Usage
Please look at the examples folder to see how it works.
== License
GPL v3

View File

@@ -0,0 +1,18 @@
PyBoard-1.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
PyBoard-1.1.4.dist-info/METADATA,sha256=75bL6KHfusE99zg6qBm4C-zKf-yJK7-5ocJC3fLKetQ,1003
PyBoard-1.1.4.dist-info/RECORD,,
PyBoard-1.1.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
PyBoard-1.1.4.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
PyBoard-1.1.4.dist-info/top_level.txt,sha256=Lxn4VWX3v2-acNb1pnoNCabY7LslscnlLsGkKCd67sI,8
pyboard/__init__.py,sha256=MAiDuBlNc7SGg1NuQvafd1_MRIUO5zn1CryL_akIiCk,768
pyboard/__pycache__/__init__.cpython-312.pyc,,
pyboard/__pycache__/board.cpython-312.pyc,,
pyboard/__pycache__/main.cpython-312.pyc,,
pyboard/__pycache__/pyboard_constants.cpython-312.pyc,,
pyboard/__pycache__/pyboard_core.cpython-312.pyc,,
pyboard/__pycache__/pyboard_iot.cpython-312.pyc,,
pyboard/board.py,sha256=xni359QZD1eM_r5LEeaLJG4dC13n-e-pDVFBHP_zCl4,31731
pyboard/main.py,sha256=6vwDSMG7w9RQb66JONgyGwKjp3EHBzUkZbRVLjdoAwc,901
pyboard/pyboard_constants.py,sha256=vXr56UFRGTbuvABYiKbxUVsDzSl0sEXh3SI81ro2dDU,1131
pyboard/pyboard_core.py,sha256=K20f2JxM--809EBmToOUsH8WpbQpvrunNbfEJQDlaFs,7227
pyboard/pyboard_iot.py,sha256=D3MhMPKpGadnEFzSKydmqe6_UGxAj0Wulmn-qByS5XY,30600

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: setuptools (72.2.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1,20 @@
Copyright (c) 2017-2021 Ingy döt Net
Copyright (c) 2006-2016 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,46 @@
Metadata-Version: 2.1
Name: PyYAML
Version: 6.0.2
Summary: YAML parser and emitter for Python
Home-page: https://pyyaml.org/
Download-URL: https://pypi.org/project/PyYAML/
Author: Kirill Simonov
Author-email: xi@resolvent.net
License: MIT
Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
Project-URL: CI, https://github.com/yaml/pyyaml/actions
Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
Project-URL: Source Code, https://github.com/yaml/pyyaml
Platform: Any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup
Requires-Python: >=3.8
License-File: LICENSE
YAML is a data serialization format designed for human readability
and interaction with scripting languages. PyYAML is a YAML parser
and emitter for Python.
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
support, capable extension API, and sensible error messages. PyYAML
supports standard YAML tags and provides Python-specific tags that
allow to represent an arbitrary Python object.
PyYAML is applicable for a broad range of tasks from complex
configuration files to object serialization and persistence.

View File

@@ -0,0 +1,43 @@
PyYAML-6.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
PyYAML-6.0.2.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
PyYAML-6.0.2.dist-info/METADATA,sha256=9-odFB5seu4pGPcEv7E8iyxNF51_uKnaNGjLAhz2lto,2060
PyYAML-6.0.2.dist-info/RECORD,,
PyYAML-6.0.2.dist-info/WHEEL,sha256=1pP4yhrbipRtdbm4Rbg3aoTjzc7pDhpHKO0CEY24CNM,152
PyYAML-6.0.2.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
_yaml/__pycache__/__init__.cpython-312.pyc,,
yaml/__init__.py,sha256=N35S01HMesFTe0aRRMWkPj0Pa8IEbHpE9FK7cr5Bdtw,12311
yaml/__pycache__/__init__.cpython-312.pyc,,
yaml/__pycache__/composer.cpython-312.pyc,,
yaml/__pycache__/constructor.cpython-312.pyc,,
yaml/__pycache__/cyaml.cpython-312.pyc,,
yaml/__pycache__/dumper.cpython-312.pyc,,
yaml/__pycache__/emitter.cpython-312.pyc,,
yaml/__pycache__/error.cpython-312.pyc,,
yaml/__pycache__/events.cpython-312.pyc,,
yaml/__pycache__/loader.cpython-312.pyc,,
yaml/__pycache__/nodes.cpython-312.pyc,,
yaml/__pycache__/parser.cpython-312.pyc,,
yaml/__pycache__/reader.cpython-312.pyc,,
yaml/__pycache__/representer.cpython-312.pyc,,
yaml/__pycache__/resolver.cpython-312.pyc,,
yaml/__pycache__/scanner.cpython-312.pyc,,
yaml/__pycache__/serializer.cpython-312.pyc,,
yaml/__pycache__/tokens.cpython-312.pyc,,
yaml/_yaml.cpython-312-x86_64-linux-gnu.so,sha256=PJFgxnc0f5Dyde6WKmBm6fZWapawmWl7aBRruXjRA80,2481784
yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573

View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.44.0)
Root-Is-Purelib: false
Tag: cp312-cp312-manylinux_2_17_x86_64
Tag: cp312-cp312-manylinux2014_x86_64

View File

@@ -0,0 +1,2 @@
_yaml
yaml

View File

@@ -0,0 +1 @@
import _virtualenv

View File

@@ -0,0 +1,103 @@
"""Patches that are applied at runtime to the virtual environment."""
from __future__ import annotations
import os
import sys
VIRTUALENV_PATCH_FILE = os.path.join(__file__)
def patch_dist(dist):
"""
Distutils allows user to configure some arguments via a configuration file:
https://docs.python.org/3/install/index.html#distutils-configuration-files.
Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up.
""" # noqa: D205
# we cannot allow some install config as that would get packages installed outside of the virtual environment
old_parse_config_files = dist.Distribution.parse_config_files
def parse_config_files(self, *args, **kwargs):
result = old_parse_config_files(self, *args, **kwargs)
install = self.get_option_dict("install")
if "prefix" in install: # the prefix governs where to install the libraries
install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)
for base in ("purelib", "platlib", "headers", "scripts", "data"):
key = f"install_{base}"
if key in install: # do not allow global configs to hijack venv paths
install.pop(key, None)
return result
dist.Distribution.parse_config_files = parse_config_files
# Import hook that patches some modules to ignore configuration values that break package installation in case
# of virtual environments.
_DISTUTILS_PATCH = "distutils.dist", "setuptools.dist"
# https://docs.python.org/3/library/importlib.html#setting-up-an-importer
class _Finder:
"""A meta path finder that allows patching the imported distutils modules."""
fullname = None
# lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup,
# because there are gevent-based applications that need to be first to import threading by themselves.
# See https://github.com/pypa/virtualenv/issues/1895 for details.
lock = [] # noqa: RUF012
def find_spec(self, fullname, path, target=None): # noqa: ARG002
if fullname in _DISTUTILS_PATCH and self.fullname is None: # noqa: PLR1702
# initialize lock[0] lazily
if len(self.lock) == 0:
import threading # noqa: PLC0415
lock = threading.Lock()
# there is possibility that two threads T1 and T2 are simultaneously running into find_spec,
# observing .lock as empty, and further going into hereby initialization. However due to the GIL,
# list.append() operation is atomic and this way only one of the threads will "win" to put the lock
# - that every thread will use - into .lock[0].
# https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
self.lock.append(lock)
from functools import partial # noqa: PLC0415
from importlib.util import find_spec # noqa: PLC0415
with self.lock[0]:
self.fullname = fullname
try:
spec = find_spec(fullname, path)
if spec is not None:
# https://www.python.org/dev/peps/pep-0451/#how-loading-will-work
is_new_api = hasattr(spec.loader, "exec_module")
func_name = "exec_module" if is_new_api else "load_module"
old = getattr(spec.loader, func_name)
func = self.exec_module if is_new_api else self.load_module
if old is not func:
try: # noqa: SIM105
setattr(spec.loader, func_name, partial(func, old))
except AttributeError:
pass # C-Extension loaders are r/o such as zipimporter with <3.7
return spec
finally:
self.fullname = None
return None
@staticmethod
def exec_module(old, module):
old(module)
if module.__name__ in _DISTUTILS_PATCH:
patch_dist(module)
@staticmethod
def load_module(old, name):
module = old(name)
if module.__name__ in _DISTUTILS_PATCH:
patch_dist(module)
return module
sys.meta_path.insert(0, _Finder())

View File

@@ -0,0 +1,33 @@
# This is a stub package designed to roughly emulate the _yaml
# extension module, which previously existed as a standalone module
# and has been moved into the `yaml` package namespace.
# It does not perfectly mimic its old counterpart, but should get
# close enough for anyone who's relying on it even when they shouldn't.
import yaml
# in some circumstances, the yaml module we imoprted may be from a different version, so we need
# to tread carefully when poking at it here (it may not have the attributes we expect)
if not getattr(yaml, '__with_libyaml__', False):
from sys import version_info
exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
raise exc("No module named '_yaml'")
else:
from yaml._yaml import *
import warnings
warnings.warn(
'The _yaml extension module is now located at yaml._yaml'
' and its location is subject to change. To use the'
' LibYAML-based parser and emitter, import from `yaml`:'
' `from yaml import CLoader as Loader, CDumper as Dumper`.',
DeprecationWarning
)
del warnings
# Don't `del yaml` here because yaml is actually an existing
# namespace member of _yaml.
__name__ = '_yaml'
# If the module is top-level (i.e. not a part of any specific package)
# then the attribute should be set to ''.
# https://docs.python.org/3.8/library/types.html
__package__ = ''

View File

@@ -0,0 +1,46 @@
PYTHON SOFTWARE FOUNDATION LICENSE
----------------------------------
1. This LICENSE AGREEMENT is between Ilan Schnell, and the Individual or
Organization ("Licensee") accessing and otherwise using this software
("bitarray") in source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, Ilan Schnell
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use bitarray
alone or in any derivative version, provided, however, that Ilan Schnell's
License Agreement and Ilan Schnell's notice of copyright, i.e., "Copyright (c)
2008 - 2024 Ilan Schnell; All Rights Reserved" are retained in bitarray
alone or in any derivative version prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates bitarray or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to bitarray.
4. Ilan Schnell is making bitarray available to Licensee on an "AS IS"
basis. ILAN SCHNELL MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, ILAN SCHNELL MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF BITARRAY WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. ILAN SCHNELL SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF BITARRAY
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING BITARRAY,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between Ilan Schnell
and Licensee. This License Agreement does not grant permission to use Ilan
Schnell trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using bitarray, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
bitarray-2.9.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
bitarray-2.9.2.dist-info/LICENSE,sha256=a14E9uBGLhuXvoM_nWXmbNXs6zKD9Kb1MiYQh9CrEjM,2412
bitarray-2.9.2.dist-info/METADATA,sha256=AcCMyOnbnpLfWGRf8pb8AQwTtuIXrJ1tPV04U3RorNM,34424
bitarray-2.9.2.dist-info/RECORD,,
bitarray-2.9.2.dist-info/WHEEL,sha256=4ZiCdXIWMxJyEClivrQv1QAHZpQh8kVYU92_ZAVwaok,152
bitarray-2.9.2.dist-info/top_level.txt,sha256=cLdoeIHotTwh4eHSe8qy8umF7zzyxUM90I8zFb2_xhg,9
bitarray/__init__.py,sha256=LzhfIzq4GYmC7-nRwx4veFnPO4Cy5XA5b6qdjVhQYl0,2511
bitarray/__init__.pyi,sha256=aELtBsMYq66ob-8icr3COdYa0CrFJmsd88sdPE2sbto,5256
bitarray/__pycache__/__init__.cpython-312.pyc,,
bitarray/__pycache__/test_bitarray.cpython-312.pyc,,
bitarray/__pycache__/test_util.cpython-312.pyc,,
bitarray/__pycache__/util.cpython-312.pyc,,
bitarray/_bitarray.cpython-312-x86_64-linux-gnu.so,sha256=14HHa1TjQ1pmIePCLwjb3r_D7SkzcigQfQU6F0dK2_w,494720
bitarray/_util.cpython-312-x86_64-linux-gnu.so,sha256=ADHJVpvewtOhuK5bgeOiJn34VDTsPSm7y_HVusnv7wY,196136
bitarray/bitarray.h,sha256=f5CXTBVDfR9XF_YZMXqOB52zg9u_6dwaQYCEqsX_AIM,10447
bitarray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
bitarray/pythoncapi_compat.h,sha256=9ywdY-5gQSwyy61cQ-pEH2Wn4GyowXe93m3bGSlDN1E,16187
bitarray/test_150.pickle,sha256=v5oAS4jJwWDVDh8s0mPn4iCvwN3-Y5sZWpVGkpwyCqw,356
bitarray/test_281.pickle,sha256=GaA8vsZmcyZZmIzGDP6ppfaRlsthUxgJ9tMsyjpynoc,442
bitarray/test_bitarray.py,sha256=-o4hN4T1Dan34YIOTBqq0BXpiLIAGV8QtTSfjRW5ZhE,182731
bitarray/test_util.py,sha256=4zURWe3wT5ZOnaRZ_u8K4AfTDxCQmYspBOclZiMF2rc,88028
bitarray/util.py,sha256=8KY_zK5hWhAKAfe94LmYsojkLt8ienaDfZgcd1EBVIg,14015
bitarray/util.pyi,sha256=6Ol-9EQ3Ad6RzN8b-nmYp9ZGtRjz1knMSK-gL_8urmo,2532

View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.41.2)
Root-Is-Purelib: false
Tag: cp312-cp312-manylinux_2_17_x86_64
Tag: cp312-cp312-manylinux2014_x86_64

View File

@@ -0,0 +1,76 @@
# Copyright (c) 2008 - 2024, Ilan Schnell; All Rights Reserved
"""
This package defines an object type which can efficiently represent
a bitarray. Bitarrays are sequence types and behave very much like lists.
Please find a description of this package at:
https://github.com/ilanschnell/bitarray
Author: Ilan Schnell
"""
from __future__ import absolute_import
from bitarray._bitarray import (bitarray, decodetree, _sysinfo,
_bitarray_reconstructor,
get_default_endian, _set_default_endian,
__version__)
__all__ = ['bitarray', 'frozenbitarray', 'decodetree', 'bits2bytes']
class frozenbitarray(bitarray):
"""frozenbitarray(initializer=0, /, endian='big', buffer=None) -> \
frozenbitarray
Return a `frozenbitarray` object. Initialized the same way a `bitarray`
object is initialized. A `frozenbitarray` is immutable and hashable,
and may therefore be used as a dictionary key.
"""
def __init__(self, *args, **kwargs):
self._freeze()
def __repr__(self):
return 'frozen' + bitarray.__repr__(self)
def __hash__(self):
"Return hash(self)."
# ensure hash is independent of endianness
a = bitarray(self, 'big')
return hash((len(a), a.tobytes()))
# Technically the code below is not necessary, as all these methods will
# raise a TypeError on read-only memory. However, with a different error
# message.
def __delitem__(self, *args, **kwargs):
"" # no docstring
raise TypeError("frozenbitarray is immutable")
append = bytereverse = clear = extend = encode = fill = __delitem__
frombytes = fromfile = insert = invert = pack = pop = __delitem__
remove = reverse = setall = sort = __setitem__ = __delitem__
__iadd__ = __iand__ = __imul__ = __ior__ = __ixor__ = __delitem__
__ilshift__ = __irshift__ = __delitem__
def bits2bytes(__n):
"""bits2bytes(n, /) -> int
Return the number of bytes necessary to store n bits.
"""
import sys
if not isinstance(__n, (int, long) if sys.version_info[0] == 2 else int):
raise TypeError("integer expected")
if __n < 0:
raise ValueError("non-negative integer expected")
return (__n + 7) // 8
def test(verbosity=1):
"""test(verbosity=1) -> TextTestResult
Run self-test, and return unittest.runner.TextTestResult object.
"""
from bitarray import test_bitarray
return test_bitarray.run(verbosity=verbosity)

View File

@@ -0,0 +1,149 @@
# Copyright (c) 2021 - 2024, Ilan Schnell; All Rights Reserved
#
# This stub, as well as util.pyi, are tested with Python 3.9 and mypy 0.950
from collections.abc import Iterable, Iterator, Sequence
from unittest.runner import TextTestResult
from typing import Any, BinaryIO, Dict, Union, overload
CodeDict = Dict[Any, bitarray]
BytesLike = Union[bytes, Iterable[int]]
class decodetree:
def __init__(self, code: CodeDict) -> None: ...
def complete(self) -> bool: ...
def nodes(self) -> int: ...
def todict(self) -> CodeDict: ...
class bitarray:
def __init__(self,
initializer: Union[int, str, Iterable[int], None] = ...,
endian: Union[str, None] = ...,
buffer: Any = ...) -> None: ...
def all(self) -> bool: ...
def any(self) -> bool: ...
def append(self, value: int) -> None: ...
def buffer_info(self) -> tuple: ...
def bytereverse(self,
start: int = ...,
stop: int = ...) -> None: ...
def clear(self) -> None: ...
def copy(self) -> bitarray: ...
def count(self,
sub_bitarray: Union[bitarray, int] = ...,
start: int = ...,
stop: int = ...,
step: int = ...) -> int: ...
def decode(self, code: Union[CodeDict, decodetree]) -> list: ...
def encode(self, code: CodeDict, x: Iterable) -> None: ...
def endian(self) -> str: ...
def extend(self, x: Union[str, Iterable[int]]) -> None: ...
def fill(self) -> int: ...
def find(self,
sub_bitarray: Union[bitarray, int],
start: int = ...,
stop: int = ...,
right: int = ...) -> int: ...
def frombytes(self, a: BytesLike) -> None: ...
def fromfile(self, f: BinaryIO, n: int = ...) -> None: ...
def index(self,
sub_bitarray: Union[bitarray, int],
start: int = ...,
stop: int = ...,
right: int = ...) -> int: ...
def insert(self, i: int, value: int) -> None: ...
def invert(self, i: int = ...) -> None: ...
def iterdecode(self,
code: Union[CodeDict, decodetree]) -> Iterator: ...
def itersearch(self,
sub_bitarray: Union[bitarray, int],
start: int = ...,
stop: int = ...,
right: int = ...) -> Iterator[int]: ...
def pack(self, b: BytesLike) -> None: ...
def pop(self, i: int = ...) -> int: ...
def remove(self, value: int) -> None: ...
def reverse(self) -> None: ...
def search(self, sub_bitarray: Union[bitarray, int],
limit: int = ...) -> list[int]: ...
def setall(self, value: int) -> None: ...
def sort(self, reverse: int) -> None: ...
def to01(self) -> str: ...
def tobytes(self) -> bytes: ...
def tofile(self, f: BinaryIO) -> None: ...
def tolist(self) -> list[int]: ...
def unpack(self,
zero: bytes = ...,
one: bytes = ...) -> bytes: ...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[int]: ...
@overload
def __getitem__(self, i: int) -> int: ...
@overload
def __getitem__(self, s: Union[slice, Sequence]) -> bitarray: ...
@overload
def __setitem__(self, i: Union[int, slice, Sequence], o: int) -> None: ...
@overload
def __setitem__(self, s: Union[slice, Sequence] , o: bitarray) -> None: ...
def __delitem__(self, i: Union[int, slice, Sequence]) -> None: ...
def __add__(self, other: bitarray) -> bitarray: ...
def __iadd__(self, other: bitarray) -> bitarray: ...
def __mul__(self, n: int) -> bitarray: ...
def __imul__(self, n: int) -> bitarray: ...
def __rmul__(self, n: int) -> bitarray: ...
def __ge__(self, other: bitarray) -> bool: ...
def __gt__(self, other: bitarray) -> bool: ...
def __le__(self, other: bitarray) -> bool: ...
def __lt__(self, other: bitarray) -> bool: ...
def __and__(self, other: bitarray) -> bitarray: ...
def __or__(self, other: bitarray) -> bitarray: ...
def __xor__(self, other: bitarray) -> bitarray: ...
def __iand__(self, other: bitarray) -> bitarray: ...
def __ior__(self, other: bitarray) -> bitarray: ...
def __ixor__(self, other: bitarray) -> bitarray: ...
def __invert__(self) -> bitarray: ...
def __lshift__(self, n: int) -> bitarray: ...
def __rshift__(self, n: int) -> bitarray: ...
def __ilshift__(self, n: int) -> bitarray: ...
def __irshift__(self, n: int) -> bitarray: ...
# data descriptors
@property
def nbytes(self) -> int: ...
@property
def padbits(self) -> int: ...
@property
def readonly(self) -> bool: ...
class frozenbitarray(bitarray):
def __hash__(self) -> int: ...
__version__: str
def bits2bytes(n: int) -> int: ...
def get_default_endian() -> str: ...
def test(verbosity: int = ...) -> TextTestResult: ...
def _set_default_endian(endian: str) -> None: ...
def _sysinfo() -> tuple: ...
def _bitarray_reconstructor(cls: type,
buffer: bytes,
endian: str,
padbits: int,
readonly: int) -> bitarray: ...

View File

@@ -0,0 +1,336 @@
/*
Copyright (c) 2008 - 2024, Ilan Schnell; All Rights Reserved
bitarray is published under the PSF license.
Author: Ilan Schnell
*/
#define BITARRAY_VERSION "2.9.2"
#ifdef STDC_HEADERS
# include <stddef.h>
#else
# ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* For size_t */
# endif
#endif
/* Compatibility with Visual Studio 2013 and older which don't support
the inline keyword in C (only in C++): use __inline instead.
(copied from pythoncapi_compat.h) */
#if (defined(_MSC_VER) && _MSC_VER < 1900 \
&& !defined(__cplusplus) && !defined(inline))
#define inline __inline
#endif
#ifdef _MSC_VER
#include <intrin.h> /* For _byteswap_uint64() */
#endif
/* --- definitions specific to Python --- */
/* Py_UNREACHABLE was introduced in Python 3.7 */
#ifndef Py_UNREACHABLE
#define Py_UNREACHABLE() abort()
#endif
#if PY_MAJOR_VERSION >= 3
#define IS_PY3K 1
#define BYTES_SIZE_FMT "y#"
#else
#define IS_PY3K 0
/* the Py_MIN and Py_MAX macros were introduced in Python 3.3 */
#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y))
#define PySlice_GetIndicesEx(slice, len, start, stop, step, slicelength) \
PySlice_GetIndicesEx(((PySliceObject *) slice), \
(len), (start), (stop), (step), (slicelength))
#define PyLong_FromLong PyInt_FromLong
#define BYTES_SIZE_FMT "s#"
#endif
/* --- bitarrayobject --- */
/* .ob_size is buffer size (in bytes), not the number of elements.
The number of elements (bits) is .nbits. */
typedef struct {
PyObject_VAR_HEAD
char *ob_item; /* buffer */
Py_ssize_t allocated; /* allocated buffer size (in bytes) */
Py_ssize_t nbits; /* length of bitarray, i.e. elements */
int endian; /* bit-endianness of bitarray */
int ob_exports; /* how many buffer exports */
PyObject *weakreflist; /* list of weak references */
Py_buffer *buffer; /* used when importing a buffer */
int readonly; /* buffer is readonly */
} bitarrayobject;
/* --- bit-endianness --- */
#define ENDIAN_LITTLE 0
#define ENDIAN_BIG 1
#define IS_LE(self) ((self)->endian == ENDIAN_LITTLE)
#define IS_BE(self) ((self)->endian == ENDIAN_BIG)
/* endianness as string */
#define ENDIAN_STR(endian) ((endian) == ENDIAN_LITTLE ? "little" : "big")
/* number of pad bits */
#define PADBITS(self) (8 * Py_SIZE(self) - (self)->nbits)
/* number of bytes necessary to store given bits */
#define BYTES(bits) (((bits) + 7) >> 3)
/* we're not using bitmask_table here, as it is actually slower */
#define BITMASK(self, i) (((char) 1) << ((self)->endian == ENDIAN_LITTLE ? \
((i) % 8) : (7 - (i) % 8)))
/* buffer as uint64 array */
#define WBUFF(self) ((uint64_t *) (self)->ob_item)
/* assert that .nbits is in agreement with .ob_size */
#define assert_nbits(self) assert(BYTES((self)->nbits) == Py_SIZE(self))
/* assert byte index is in range */
#define assert_byte_in_range(self, j) \
assert(self->ob_item && 0 <= (j) && (j) < Py_SIZE(self))
/* ------------ low level access to bits in bitarrayobject ------------- */
static inline int
getbit(bitarrayobject *self, Py_ssize_t i)
{
assert_nbits(self);
assert(0 <= i && i < self->nbits);
return self->ob_item[i >> 3] & BITMASK(self, i) ? 1 : 0;
}
static inline void
setbit(bitarrayobject *self, Py_ssize_t i, int vi)
{
char *cp, mask;
assert_nbits(self);
assert(0 <= i && i < self->nbits);
assert(self->readonly == 0);
mask = BITMASK(self, i);
cp = self->ob_item + (i >> 3);
if (vi)
*cp |= mask;
else
*cp &= ~mask;
}
static const char bitmask_table[2][8] = {
{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, /* little endian */
{0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, /* big endian */
};
/* character with n leading ones is: ones_table[endian][n] */
static const char ones_table[2][8] = {
{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}, /* little endian */
{0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe}, /* big endian */
};
/* Return last byte in buffer with pad bits zeroed out.
If the length of the bitarray is a multiple of 8 (which includes an empty
bitarray), 0 is returned. */
static inline char
zlc(bitarrayobject *self) /* zlc = zeroed last char */
{
const int r = self->nbits % 8; /* index into mask table */
if (r == 0)
return 0;
return self->ob_item[Py_SIZE(self) - 1] & ones_table[IS_BE(self)][r];
}
/* Return a uint64_t word representing the last (up to 63) remaining bits
of the buffer. All missing bytes (to complete the word) and padbits are
treated as zeros.
If the length of the bitarray is a multiple of 64 (which includes an empty
bitarray), 0 is returned. */
static inline uint64_t
zlw(bitarrayobject *self) /* zlw = zeroed last word */
{
const Py_ssize_t nbits = self->nbits;
const Py_ssize_t nw = 8 * (nbits / 64); /* bytes in complete words */
const int nr = (nbits % 64) / 8; /* complete remaining bytes */
uint64_t res = 0;
assert(nw + nr == nbits / 8 && nw + nr <= Py_SIZE(self));
memcpy((char *) &res, self->ob_item + nw, (size_t) nr);
if (nbits % 8)
*(((char *) &res) + nr) = zlc(self);
assert(nbits % 64 || res == 0);
return res;
}
/* unless buffer is readonly, zero out pad bits - self->nbits is unchanged */
static inline void
set_padbits(bitarrayobject *self)
{
const int r = self->nbits % 8; /* index into mask table */
if (self->readonly == 0 && r)
self->ob_item[Py_SIZE(self) - 1] &= ones_table[IS_BE(self)][r];
}
/* population count - number of 1's in uint64 */
static inline int
popcnt_64(uint64_t x)
{
#if (defined(__clang__) || defined(__GNUC__))
return __builtin_popcountll(x);
#else
/* https://en.wikipedia.org/wiki/Hamming_weight popcount64c */
const uint64_t m1 = 0x5555555555555555;
const uint64_t m2 = 0x3333333333333333;
const uint64_t m4 = 0x0f0f0f0f0f0f0f0f;
const uint64_t h01 = 0x0101010101010101;
x -= (x >> 1) & m1;
x = (x & m2) + ((x >> 2) & m2);
x = (x + (x >> 4)) & m4;
return (x * h01) >> 56;
#endif
}
static inline uint64_t
builtin_bswap64(uint64_t word)
{
#if (defined(__clang__) || \
(defined(__GNUC__) \
&& ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))))
/* __builtin_bswap64() is available since GCC 4.3. */
# define HAVE_BUILTIN_BSWAP64 1
return __builtin_bswap64(word);
#elif defined(_MSC_VER)
# define HAVE_BUILTIN_BSWAP64 1
return _byteswap_uint64(word);
#else
# define HAVE_BUILTIN_BSWAP64 0
Py_UNREACHABLE();
#endif
}
/* Return distance [0..3] to next aligned pointer.
While on modern compilers uint64_t pointers may be misaligned, it may
cause problems on older ones. Moreover, it may lead to slowdown (even
on modern compilers). */
static inline int
to_aligned(void *p)
{
int r = ((uintptr_t) p) % 4;
return r ? 4 - r : 0;
}
/* population count of n words starting from at uint64_t pointer w */
static inline Py_ssize_t
popcnt_words(uint64_t *w, Py_ssize_t n)
{
Py_ssize_t cnt = 0;
assert(n >= 0 && to_aligned((void *) w) == 0);
while (n--)
cnt += popcnt_64(*w++);
return cnt;
}
/* adjust index a manner consistent with the handling of normal slices */
static inline void
adjust_index(Py_ssize_t length, Py_ssize_t *i, Py_ssize_t step)
{
if (*i < 0) {
*i += length;
if (*i < 0)
*i = (step < 0) ? -1 : 0;
}
else if (*i >= length) {
*i = (step < 0) ? length - 1 : length;
}
}
/* same as PySlice_AdjustIndices() which was introduced in Python 3.6.1 */
static inline Py_ssize_t
adjust_indices(Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop,
Py_ssize_t step)
{
#if PY_VERSION_HEX > 0x03060100
return PySlice_AdjustIndices(length, start, stop, step);
#else
assert(step != 0);
adjust_index(length, start, step);
adjust_index(length, stop, step);
/*
a / b does integer division. If either a or b is negative, the result
depends on the compiler (rounding can go toward 0 or negative infinity).
Therefore, we are careful that both a and b are always positive.
*/
if (step < 0) {
if (*stop < *start)
return (*start - *stop - 1) / (-step) + 1;
}
else {
if (*start < *stop)
return (*stop - *start - 1) / step + 1;
}
return 0;
#endif
}
/* adjust slice parameters such that step is always positive; produces
simpler loops over elements when their order is irrelevant */
static inline void
adjust_step_positive(Py_ssize_t slicelength,
Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step)
{
if (*step < 0) {
*stop = *start + 1;
*start = *stop + *step * (slicelength - 1) - 1;
*step = -(*step);
}
assert(*start >= 0 && *stop >= 0 && *step > 0 && slicelength >= 0);
/* slicelength == 0 implies stop <= start */
assert(slicelength != 0 || *stop <= *start);
/* step == 1 and slicelength != 0 implies stop - start == slicelength */
assert(*step != 1 || slicelength == 0 || *stop - *start == slicelength);
}
/* convert Python object to C int and set value at address -
return 1 on success, 0 on failure (and set exception) */
static inline int
conv_pybit(PyObject *value, int *vi)
{
Py_ssize_t n;
n = PyNumber_AsSsize_t(value, NULL);
if (n == -1 && PyErr_Occurred())
return 0;
if (n < 0 || n > 1) {
PyErr_Format(PyExc_ValueError, "bit must be 0 or 1, got %zd", n);
return 0;
}
*vi = (int) n;
return 1;
}
/* Return 0 if bitarrays have equal length and bit-endianness.
Otherwise, set exception and return -1. */
static inline int
ensure_eq_size_endian(bitarrayobject *a, bitarrayobject *b)
{
if (a->nbits != b->nbits) {
PyErr_SetString(PyExc_ValueError,
"bitarrays of equal length expected");
return -1;
}
if (a->endian != b->endian) {
PyErr_SetString(PyExc_ValueError,
"bitarrays of equal bit-endianness expected");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,577 @@
// Header file providing new C API functions to old Python versions.
//
// File distributed under the Zero Clause BSD (0BSD) license.
// Copyright Contributors to the pythoncapi_compat project.
//
// Homepage:
// https://github.com/python/pythoncapi_compat
//
// Latest version:
// https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h
//
// SPDX-License-Identifier: 0BSD
#ifndef PYTHONCAPI_COMPAT
#define PYTHONCAPI_COMPAT
#ifdef __cplusplus
extern "C" {
#endif
#include <Python.h>
#include "frameobject.h" // PyFrameObject, PyFrame_GetBack()
// Compatibility with Visual Studio 2013 and older which don't support
// the inline keyword in C (only in C++): use __inline instead.
#if (defined(_MSC_VER) && _MSC_VER < 1900 \
&& !defined(__cplusplus) && !defined(inline))
# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static __inline TYPE
#else
# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static inline TYPE
#endif
#ifndef _Py_CAST
# define _Py_CAST(type, expr) ((type)(expr))
#endif
// On C++11 and newer, _Py_NULL is defined as nullptr on C++11,
// otherwise it is defined as NULL.
#ifndef _Py_NULL
# if defined(__cplusplus) && __cplusplus >= 201103
# define _Py_NULL nullptr
# else
# define _Py_NULL NULL
# endif
#endif
// Cast argument to PyObject* type.
#ifndef _PyObject_CAST
# define _PyObject_CAST(op) _Py_CAST(PyObject*, op)
#endif
// bpo-42262 added Py_NewRef() to Python 3.10.0a3
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
_Py_NewRef(PyObject *obj)
{
Py_INCREF(obj);
return obj;
}
#define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj))
#endif
// bpo-42262 added Py_XNewRef() to Python 3.10.0a3
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
_Py_XNewRef(PyObject *obj)
{
Py_XINCREF(obj);
return obj;
}
#define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj))
#endif
// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT)
PYCAPI_COMPAT_STATIC_INLINE(void)
_Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
{
ob->ob_refcnt = refcnt;
}
#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)
#endif
// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2.
// It is excluded from the limited C API.
#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API)
#define Py_SETREF(dst, src) \
do { \
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
PyObject *_tmp_dst = (*_tmp_dst_ptr); \
*_tmp_dst_ptr = _PyObject_CAST(src); \
Py_DECREF(_tmp_dst); \
} while (0)
#define Py_XSETREF(dst, src) \
do { \
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
PyObject *_tmp_dst = (*_tmp_dst_ptr); \
*_tmp_dst_ptr = _PyObject_CAST(src); \
Py_XDECREF(_tmp_dst); \
} while (0)
#endif
// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse()
// to Python 3.10.0b1.
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is)
# define Py_Is(x, y) ((x) == (y))
#endif
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone)
# define Py_IsNone(x) Py_Is(x, Py_None)
#endif
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsTrue)
# define Py_IsTrue(x) Py_Is(x, Py_True)
#endif
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsFalse)
# define Py_IsFalse(x) Py_Is(x, Py_False)
#endif
// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
PYCAPI_COMPAT_STATIC_INLINE(void)
_Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
{
ob->ob_type = type;
}
#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type)
#endif
// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE)
PYCAPI_COMPAT_STATIC_INLINE(void)
_Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size)
{
ob->ob_size = size;
}
#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size)
#endif
// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1
#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*)
PyFrame_GetCode(PyFrameObject *frame)
{
assert(frame != _Py_NULL);
assert(frame->f_code != _Py_NULL);
return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code));
}
#endif
PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*)
_PyFrame_GetCodeBorrow(PyFrameObject *frame)
{
PyCodeObject *code = PyFrame_GetCode(frame);
Py_DECREF(code);
return code;
}
// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1
#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
PyFrame_GetBack(PyFrameObject *frame)
{
assert(frame != _Py_NULL);
return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back));
}
#endif
#if !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
_PyFrame_GetBackBorrow(PyFrameObject *frame)
{
PyFrameObject *back = PyFrame_GetBack(frame);
Py_XDECREF(back);
return back;
}
#endif
// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7
#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyFrame_GetLocals(PyFrameObject *frame)
{
#if PY_VERSION_HEX >= 0x030400B1
if (PyFrame_FastToLocalsWithError(frame) < 0) {
return NULL;
}
#else
PyFrame_FastToLocals(frame);
#endif
return Py_NewRef(frame->f_locals);
}
#endif
// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7
#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyFrame_GetGlobals(PyFrameObject *frame)
{
return Py_NewRef(frame->f_globals);
}
#endif
// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7
#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyFrame_GetBuiltins(PyFrameObject *frame)
{
return Py_NewRef(frame->f_builtins);
}
#endif
// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1
#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(int)
PyFrame_GetLasti(PyFrameObject *frame)
{
#if PY_VERSION_HEX >= 0x030A00A7
// bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset,
// not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes)
// instructions.
if (frame->f_lasti < 0) {
return -1;
}
return frame->f_lasti * 2;
#else
return frame->f_lasti;
#endif
}
#endif
// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2
#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyFrame_GetVar(PyFrameObject *frame, PyObject *name)
{
PyObject *locals, *value;
locals = PyFrame_GetLocals(frame);
if (locals == NULL) {
return NULL;
}
#if PY_VERSION_HEX >= 0x03000000
value = PyDict_GetItemWithError(locals, name);
#else
value = PyDict_GetItem(locals, name);
#endif
Py_DECREF(locals);
if (value == NULL) {
if (PyErr_Occurred()) {
return NULL;
}
#if PY_VERSION_HEX >= 0x03000000
PyErr_Format(PyExc_NameError, "variable %R does not exist", name);
#else
PyErr_SetString(PyExc_NameError, "variable does not exist");
#endif
return NULL;
}
return Py_NewRef(value);
}
#endif
// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2
#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyFrame_GetVarString(PyFrameObject *frame, const char *name)
{
PyObject *name_obj, *value;
name_obj = PyUnicode_FromString(name);
if (name_obj == NULL) {
return NULL;
}
value = PyFrame_GetVar(frame, name_obj);
Py_DECREF(name_obj);
return value;
}
#endif
// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5
#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState *)
PyThreadState_GetInterpreter(PyThreadState *tstate)
{
assert(tstate != _Py_NULL);
return tstate->interp;
}
#endif
// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1
#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
PyThreadState_GetFrame(PyThreadState *tstate)
{
assert(tstate != _Py_NULL);
return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame));
}
#endif
#if !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
_PyThreadState_GetFrameBorrow(PyThreadState *tstate)
{
PyFrameObject *frame = PyThreadState_GetFrame(tstate);
Py_XDECREF(frame);
return frame;
}
#endif
// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5
#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState*)
PyInterpreterState_Get(void)
{
PyThreadState *tstate;
PyInterpreterState *interp;
tstate = PyThreadState_GET();
if (tstate == _Py_NULL) {
Py_FatalError("GIL released (tstate is NULL)");
}
interp = tstate->interp;
if (interp == _Py_NULL) {
Py_FatalError("no current interpreter");
}
return interp;
}
#endif
// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6
#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(uint64_t)
PyThreadState_GetID(PyThreadState *tstate)
{
assert(tstate != _Py_NULL);
return tstate->id;
}
#endif
// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(void)
PyThreadState_EnterTracing(PyThreadState *tstate)
{
tstate->tracing++;
#if PY_VERSION_HEX >= 0x030A00A1
tstate->cframe->use_tracing = 0;
#else
tstate->use_tracing = 0;
#endif
}
#endif
// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(void)
PyThreadState_LeaveTracing(PyThreadState *tstate)
{
int use_tracing = (tstate->c_tracefunc != _Py_NULL
|| tstate->c_profilefunc != _Py_NULL);
tstate->tracing--;
#if PY_VERSION_HEX >= 0x030A00A1
tstate->cframe->use_tracing = use_tracing;
#else
tstate->use_tracing = use_tracing;
#endif
}
#endif
// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1
// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11
#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyObject_CallNoArgs(PyObject *func)
{
return PyObject_CallFunctionObjArgs(func, NULL);
}
#endif
// bpo-39245 made PyObject_CallOneArg() public (previously called
// _PyObject_CallOneArg) in Python 3.9.0a4
// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11
#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyObject_CallOneArg(PyObject *func, PyObject *arg)
{
return PyObject_CallFunctionObjArgs(func, arg, NULL);
}
#endif
// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3
#if PY_VERSION_HEX < 0x030A00A3
PYCAPI_COMPAT_STATIC_INLINE(int)
PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)
{
int res;
Py_XINCREF(value);
res = PyModule_AddObject(module, name, value);
if (res < 0) {
Py_XDECREF(value);
}
return res;
}
#endif
// bpo-40024 added PyModule_AddType() to Python 3.9.0a5
#if PY_VERSION_HEX < 0x030900A5
PYCAPI_COMPAT_STATIC_INLINE(int)
PyModule_AddType(PyObject *module, PyTypeObject *type)
{
const char *name, *dot;
if (PyType_Ready(type) < 0) {
return -1;
}
// inline _PyType_Name()
name = type->tp_name;
assert(name != _Py_NULL);
dot = strrchr(name, '.');
if (dot != _Py_NULL) {
name = dot + 1;
}
return PyModule_AddObjectRef(module, name, _PyObject_CAST(type));
}
#endif
// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6.
// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2.
#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(int)
PyObject_GC_IsTracked(PyObject* obj)
{
return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj));
}
#endif
// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6.
// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final.
#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(int)
PyObject_GC_IsFinalized(PyObject *obj)
{
PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1;
return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc));
}
#endif
// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)
PYCAPI_COMPAT_STATIC_INLINE(int)
_Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
return Py_TYPE(ob) == type;
}
#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type)
#endif
// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7.
// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1.
// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal
// C API: Python 3.11a2-3.11a6 versions are not supported.
#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(int)
PyFloat_Pack2(double x, char *p, int le)
{ return _PyFloat_Pack2(x, (unsigned char*)p, le); }
PYCAPI_COMPAT_STATIC_INLINE(double)
PyFloat_Unpack2(const char *p, int le)
{ return _PyFloat_Unpack2((const unsigned char *)p, le); }
#endif
// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and
// PyFloat_Unpack8() to Python 3.11a7.
// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4()
// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions
// are not supported.
#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(int)
PyFloat_Pack4(double x, char *p, int le)
{ return _PyFloat_Pack4(x, (unsigned char*)p, le); }
PYCAPI_COMPAT_STATIC_INLINE(int)
PyFloat_Pack8(double x, char *p, int le)
{ return _PyFloat_Pack8(x, (unsigned char*)p, le); }
PYCAPI_COMPAT_STATIC_INLINE(double)
PyFloat_Unpack4(const char *p, int le)
{ return _PyFloat_Unpack4((const unsigned char *)p, le); }
PYCAPI_COMPAT_STATIC_INLINE(double)
PyFloat_Unpack8(const char *p, int le)
{ return _PyFloat_Unpack8((const unsigned char *)p, le); }
#endif
// gh-92154 added PyCode_GetCode() to Python 3.11.0b1
#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyCode_GetCode(PyCodeObject *code)
{
return Py_NewRef(code->co_code);
}
#endif
// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1
#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyCode_GetVarnames(PyCodeObject *code)
{
return Py_NewRef(code->co_varnames);
}
#endif
// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1
#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyCode_GetFreevars(PyCodeObject *code)
{
return Py_NewRef(code->co_freevars);
}
#endif
// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1
#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION)
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
PyCode_GetCellvars(PyCodeObject *code)
{
return Py_NewRef(code->co_cellvars);
}
#endif
// Py_UNUSED() was added to Python 3.4.0b2.
#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED)
# if defined(__GNUC__) || defined(__clang__)
# define Py_UNUSED(name) _unused_ ## name __attribute__((unused))
# else
# define Py_UNUSED(name) _unused_ ## name
# endif
#endif
#ifdef __cplusplus
}
#endif
#endif // PYTHONCAPI_COMPAT

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,423 @@
# Copyright (c) 2019 - 2024, Ilan Schnell; All Rights Reserved
# bitarray is published under the PSF license.
#
# Author: Ilan Schnell
"""
Useful utilities for working with bitarrays.
"""
from __future__ import absolute_import
import os
import sys
from bitarray import bitarray, bits2bytes
from bitarray._util import (
zeros, ones, count_n, parity,
count_and, count_or, count_xor, any_and, subset,
_correspond_all,
serialize, deserialize,
ba2hex, hex2ba,
ba2base, base2ba,
sc_encode, sc_decode,
vl_encode, vl_decode,
canonical_decode,
)
__all__ = [
'zeros', 'ones', 'urandom',
'pprint', 'make_endian', 'rindex', 'strip', 'count_n',
'parity', 'count_and', 'count_or', 'count_xor', 'any_and', 'subset',
'intervals',
'ba2hex', 'hex2ba',
'ba2base', 'base2ba',
'ba2int', 'int2ba',
'serialize', 'deserialize',
'sc_encode', 'sc_decode',
'vl_encode', 'vl_decode',
'huffman_code', 'canonical_huffman', 'canonical_decode',
]
_is_py2 = bool(sys.version_info[0] == 2)
def urandom(__length, endian=None):
"""urandom(length, /, endian=None) -> bitarray
Return a bitarray of `length` random bits (uses `os.urandom`).
"""
a = bitarray(0, endian)
a.frombytes(os.urandom(bits2bytes(__length)))
del a[__length:]
return a
def rindex(__a, __sub_bitarray=1, __start=0, __stop=sys.maxsize):
"""rindex(bitarray, sub_bitarray=1, start=0, stop=<end>, /) -> int
Return rightmost (highest) index where sub_bitarray (or item - defaults
to 1) is found in bitarray (`a`), such that sub_bitarray is contained
within `a[start:stop]`.
Raises `ValueError` when the sub_bitarray is not present.
"""
from warnings import warn
warn("rindex() is deprecated and will be removed in bitarray 3.0 - "
"use .index(..., right=True) method instead.",
DeprecationWarning, stacklevel=1)
if not isinstance(__a, bitarray):
raise TypeError("bitarray expected, got '%s'" % type(__a).__name__)
return __a.index(__sub_bitarray, __start, __stop, right=True)
def pprint(__a, stream=None, group=8, indent=4, width=80):
"""pprint(bitarray, /, stream=None, group=8, indent=4, width=80)
Prints the formatted representation of object on `stream` (which defaults
to `sys.stdout`). By default, elements are grouped in bytes (8 elements),
and 8 bytes (64 elements) per line.
Non-bitarray objects are printed by the standard library
function `pprint.pprint()`.
"""
if stream is None:
stream = sys.stdout
if not isinstance(__a, bitarray):
import pprint as _pprint
_pprint.pprint(__a, stream=stream, indent=indent, width=width)
return
group = int(group)
if group < 1:
raise ValueError('group must be >= 1')
indent = int(indent)
if indent < 0:
raise ValueError('indent must be >= 0')
width = int(width)
if width <= indent:
raise ValueError('width must be > %d (indent)' % indent)
gpl = (width - indent) // (group + 1) # groups per line
epl = group * gpl # elements per line
if epl == 0:
epl = width - indent - 2
type_name = type(__a).__name__
# here 4 is len("'()'")
multiline = len(type_name) + 4 + len(__a) + len(__a) // group >= width
if multiline:
quotes = "'''"
elif __a:
quotes = "'"
else:
quotes = ""
stream.write("%s(%s" % (type_name, quotes))
for i, b in enumerate(__a):
if multiline and i % epl == 0:
stream.write('\n%s' % (indent * ' '))
if i % group == 0 and i % epl != 0:
stream.write(' ')
stream.write(str(b))
if multiline:
stream.write('\n')
stream.write("%s)\n" % quotes)
stream.flush()
def make_endian(__a, endian):
"""make_endian(bitarray, /, endian) -> bitarray
When the endianness of the given bitarray is different from `endian`,
return a new bitarray, with endianness `endian` and the same elements
as the original bitarray.
Otherwise (endianness is already `endian`) the original bitarray is returned
unchanged.
"""
from warnings import warn
warn("make_endian() is deprecated and will be removed in bitarray 3.0 - "
"use bitarray(..., endian=...) instead",
DeprecationWarning, stacklevel=1)
if not isinstance(__a, bitarray):
raise TypeError("bitarray expected, got '%s'" % type(__a).__name__)
if __a.endian() == endian:
return __a
return bitarray(__a, endian)
def strip(__a, mode='right'):
"""strip(bitarray, /, mode='right') -> bitarray
Return a new bitarray with zeros stripped from left, right or both ends.
Allowed values for mode are the strings: `left`, `right`, `both`
"""
if not isinstance(mode, str):
raise TypeError("str expected for mode, got '%s'" % type(__a).__name__)
if mode not in ('left', 'right', 'both'):
raise ValueError("mode must be 'left', 'right' or 'both', got %r" %
mode)
start = None if mode == 'right' else __a.find(1)
if start == -1:
return __a[:0]
stop = None if mode == 'left' else __a.find(1, right=1) + 1
return __a[start:stop]
def intervals(__a):
"""intervals(bitarray, /) -> iterator
Compute all uninterrupted intervals of 1s and 0s, and return an
iterator over tuples `(value, start, stop)`. The intervals are guaranteed
to be in order, and their size is always non-zero (`stop - start > 0`).
"""
try:
value = __a[0] # value of current interval
except IndexError:
return
n = len(__a)
stop = 0 # "previous" stop - becomes next start
while stop < n:
start = stop
# assert __a[start] == value
try: # find next occurrence of opposite value
stop = __a.index(not value, start)
except ValueError:
stop = n
yield int(value), start, stop
value = not value # next interval has opposite value
def ba2int(__a, signed=False):
"""ba2int(bitarray, /, signed=False) -> int
Convert the given bitarray to an integer.
The bit-endianness of the bitarray is respected.
`signed` indicates whether two's complement is used to represent the integer.
"""
if not isinstance(__a, bitarray):
raise TypeError("bitarray expected, got '%s'" % type(__a).__name__)
length = len(__a)
if length == 0:
raise ValueError("non-empty bitarray expected")
le = bool(__a.endian() == 'little')
if __a.padbits:
pad = zeros(__a.padbits, __a.endian())
__a = __a + pad if le else pad + __a
if _is_py2:
a = bitarray(__a, 'big')
if le:
a.reverse()
res = int(ba2hex(a), 16)
else: # py3
res = int.from_bytes(__a.tobytes(), byteorder=__a.endian())
if signed and res >= 1 << (length - 1):
res -= 1 << length
return res
def int2ba(__i, length=None, endian=None, signed=False):
"""int2ba(int, /, length=None, endian=None, signed=False) -> bitarray
Convert the given integer to a bitarray (with given endianness,
and no leading (big-endian) / trailing (little-endian) zeros), unless
the `length` of the bitarray is provided. An `OverflowError` is raised
if the integer is not representable with the given number of bits.
`signed` determines whether two's complement is used to represent the integer,
and requires `length` to be provided.
"""
if not isinstance(__i, (int, long) if _is_py2 else int):
raise TypeError("int expected, got '%s'" % type(__i).__name__)
if length is not None:
if not isinstance(length, int):
raise TypeError("int expected for length")
if length <= 0:
raise ValueError("length must be > 0")
if signed and length is None:
raise TypeError("signed requires length")
if __i == 0:
# there are special cases for 0 which we'd rather not deal with below
return zeros(length or 1, endian)
if signed:
m = 1 << (length - 1)
if not (-m <= __i < m):
raise OverflowError("signed integer not in range(%d, %d), "
"got %d" % (-m, m, __i))
if __i < 0:
__i += 1 << length
else: # unsigned
if __i < 0:
raise OverflowError("unsigned integer not positive, got %d" % __i)
if length and __i >= (1 << length):
raise OverflowError("unsigned integer not in range(0, %d), "
"got %d" % (1 << length, __i))
a = bitarray(0, endian)
le = bool(a.endian() == 'little')
if _is_py2:
s = hex(__i)[2:].rstrip('L')
a.extend(hex2ba(s, 'big'))
if le:
a.reverse()
else: # py3
b = __i.to_bytes(bits2bytes(__i.bit_length()), byteorder=a.endian())
a.frombytes(b)
if length is None:
return strip(a, 'right' if le else 'left')
la = len(a)
if la > length:
a = a[:length] if le else a[-length:]
if la < length:
pad = zeros(length - la, a.endian())
a = a + pad if le else pad + a
assert len(a) == length
return a
# ------------------------------ Huffman coding -----------------------------
def _huffman_tree(__freq_map):
"""_huffman_tree(dict, /) -> Node
Given a dict mapping symbols to their frequency, construct a Huffman tree
and return its root node.
"""
from heapq import heappush, heappop
class Node(object):
"""
A Node instance will either have a 'symbol' (leaf node) or
a 'child' (a tuple with both children) attribute.
The 'freq' attribute will always be present.
"""
def __lt__(self, other):
# heapq needs to be able to compare the nodes
return self.freq < other.freq
minheap = []
# create all leaf nodes and push them onto the queue
for sym, f in __freq_map.items():
leaf = Node()
leaf.symbol = sym
leaf.freq = f
heappush(minheap, leaf)
# repeat the process until only one node remains
while len(minheap) > 1:
# take the two nodes with lowest frequencies from the queue
# to construct a new node and push it onto the queue
parent = Node()
parent.child = heappop(minheap), heappop(minheap)
parent.freq = parent.child[0].freq + parent.child[1].freq
heappush(minheap, parent)
# the single remaining node is the root of the Huffman tree
return minheap[0]
def huffman_code(__freq_map, endian=None):
"""huffman_code(dict, /, endian=None) -> dict
Given a frequency map, a dictionary mapping symbols to their frequency,
calculate the Huffman code, i.e. a dict mapping those symbols to
bitarrays (with given endianness). Note that the symbols are not limited
to being strings. Symbols may may be any hashable object (such as `None`).
"""
if not isinstance(__freq_map, dict):
raise TypeError("dict expected, got '%s'" % type(__freq_map).__name__)
b0 = bitarray('0', endian)
b1 = bitarray('1', endian)
if len(__freq_map) < 2:
if len(__freq_map) == 0:
raise ValueError("cannot create Huffman code with no symbols")
# Only one symbol: Normally if only one symbol is given, the code
# could be represented with zero bits. However here, the code should
# be at least one bit for the .encode() and .decode() methods to work.
# So we represent the symbol by a single code of length one, in
# particular one 0 bit. This is an incomplete code, since if a 1 bit
# is received, it has no meaning and will result in an error.
return {list(__freq_map)[0]: b0}
result = {}
def traverse(nd, prefix=bitarray(0, endian)):
try: # leaf
result[nd.symbol] = prefix
except AttributeError: # parent, so traverse each of the children
traverse(nd.child[0], prefix + b0)
traverse(nd.child[1], prefix + b1)
traverse(_huffman_tree(__freq_map))
return result
def canonical_huffman(__freq_map):
"""canonical_huffman(dict, /) -> tuple
Given a frequency map, a dictionary mapping symbols to their frequency,
calculate the canonical Huffman code. Returns a tuple containing:
0. the canonical Huffman code as a dict mapping symbols to bitarrays
1. a list containing the number of symbols of each code length
2. a list of symbols in canonical order
Note: the two lists may be used as input for `canonical_decode()`.
"""
if not isinstance(__freq_map, dict):
raise TypeError("dict expected, got '%s'" % type(__freq_map).__name__)
if len(__freq_map) < 2:
if len(__freq_map) == 0:
raise ValueError("cannot create Huffman code with no symbols")
# Only one symbol: see note above in huffman_code()
sym = list(__freq_map)[0]
return {sym: bitarray('0', 'big')}, [0, 1], [sym]
code_length = {} # map symbols to their code length
def traverse(nd, length=0):
# traverse the Huffman tree, but (unlike in huffman_code() above) we
# now just simply record the length for reaching each symbol
try: # leaf
code_length[nd.symbol] = length
except AttributeError: # parent, so traverse each of the children
traverse(nd.child[0], length + 1)
traverse(nd.child[1], length + 1)
traverse(_huffman_tree(__freq_map))
# we now have a mapping of symbols to their code length,
# which is all we need
table = sorted(code_length.items(), key=lambda item: (item[1], item[0]))
maxbits = max(item[1] for item in table)
codedict = {}
count = (maxbits + 1) * [0]
code = 0
for i, (sym, length) in enumerate(table):
codedict[sym] = int2ba(code, length, 'big')
count[length] += 1
if i + 1 < len(table):
code += 1
code <<= table[i + 1][1] - length
return codedict, count, [item[0] for item in table]

View File

@@ -0,0 +1,71 @@
# Copyright (c) 2021 - 2024, Ilan Schnell; All Rights Reserved
from collections import Counter
from collections.abc import Iterable, Iterator, Sequence
from typing import Any, AnyStr, BinaryIO, Optional, Union
from bitarray import bitarray, BytesLike, CodeDict
FreqMap = Union[Counter[int], dict[Any, Union[int, float]]]
def zeros(length: int, endian: Optional[str] = ...) -> bitarray: ...
def ones(length: int, endian: Optional[str] = ...) -> bitarray: ...
def urandom(length: int, endian: Optional[str] = ...) -> bitarray: ...
def pprint(a: Any, stream: BinaryIO = ...,
group: int = ...,
indent: int = ...,
width: int = ...) -> None: ...
def make_endian(a: bitarray, endian: str) -> bitarray: ...
def rindex(a: bitarray,
sub_bitarray: Union[bitarray, int] = ...,
start: int = ...,
stop: int = ...) -> int: ...
def strip(a: bitarray, mode: str = ...) -> bitarray: ...
def count_n(a: bitarray,
n: int,
value: int = ...) -> int: ...
def parity(a: bitarray) -> int: ...
def count_and(a: bitarray, b: bitarray) -> int: ...
def count_or(a: bitarray, b: bitarray) -> int: ...
def count_xor(a: bitarray, b: bitarray) -> int: ...
def any_and(a: bitarray, b: bitarray) -> bool: ...
def subset(a: bitarray, b: bitarray) -> bool: ...
def _correspond_all(a: bitarray, b: bitarray) -> tuple: ...
def intervals(a: bitarray) -> Iterator: ...
def ba2hex(a: bitarray) -> str: ...
def hex2ba(s: AnyStr, endian: Optional[str] = ...) -> bitarray: ...
def ba2base(n: int, a: bitarray) -> str: ...
def base2ba(n: int,
s: AnyStr,
endian: Optional[str] = ...) -> bitarray: ...
def ba2int(a: bitarray, signed: int = ...) -> int: ...
def int2ba(i: int,
length: int = ...,
endian: str = ...,
signed: int = ...) -> bitarray: ...
def serialize(a: bitarray) -> bytes: ...
def deserialize(b: BytesLike) -> bitarray: ...
def sc_encode(a: bitarray) -> bytes: ...
def sc_decode(stream: BytesLike) -> bitarray: ...
def vl_encode(a: bitarray) -> bytes: ...
def vl_decode(stream: BytesLike,
endian: Optional[str] = ...) -> bitarray: ...
def _huffman_tree(freq_map: FreqMap) -> Any: ...
def huffman_code(freq_map: FreqMap,
endian: Optional[str] = ...) -> CodeDict: ...
def canonical_huffman(Freq_Map) -> tuple[CodeDict, list, list]: ...
def canonical_decode(a: bitarray,
count: Sequence[int],
symbol: Iterable[Any]) -> Iterator: ...

View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,133 @@
Metadata-Version: 2.1
Name: bitstring
Version: 4.2.3
Summary: Simple construction, analysis and modification of binary data.
Author-email: Scott Griffiths <dr.scottgriffiths@gmail.com>
Project-URL: homepage, https://github.com/scott-griffiths/bitstring
Project-URL: documentation, https://bitstring.readthedocs.io/
Keywords: binary,bitarray,bitvector,bitfield
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: bitarray <3.0.0,>=2.9.0
![bitstring](https://raw.githubusercontent.com/scott-griffiths/bitstring/main/doc/bitstring_logo_small.png "bitstring")
**bitstring** is a Python module to help make the creation and analysis of all types of bit-level binary data as simple and efficient as possible.
It has been actively maintained since 2006.
[![CI badge](https://github.com/scott-griffiths/bitstring/actions/workflows/.github/workflows/ci.yml/badge.svg)](https://github.com/scott-griffiths/bitstring/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/readthedocs/bitstring?logo=readthedocs&logoColor=white)](https://bitstring.readthedocs.io/en/latest/)
[![Codacy Badge](https://img.shields.io/codacy/grade/8869499b2eed44548fa1a5149dd451f4?logo=codacy)](https://app.codacy.com/gh/scott-griffiths/bitstring/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/pypi/bitstring?logo=libraries.io&logoColor=white)](https://libraries.io/pypi/bitstring)
&nbsp; &nbsp;
[![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://www.pepy.tech/projects/bitstring)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/bitstring?label=%40&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring)
----
> [!NOTE]
> To see what been added, improved or fixed, and also to see what's coming in the next version, see the [release notes](https://github.com/scott-griffiths/bitstring/blob/main/release_notes.md).
# Overview
* Efficiently store and manipulate binary data in idiomatic Python.
* Create bitstrings from hex, octal, binary, files, formatted strings, bytes, integers and floats of different endiannesses.
* Powerful binary packing and unpacking functions.
* Bit-level slicing, joining, searching, replacing and more.
* Create and manipulate arrays of fixed-length bitstrings.
* Read from and interpret bitstrings as streams of binary data.
* Rich API - chances are that whatever you want to do there's a simple and elegant way of doing it.
* Open source software, released under the MIT licence.
# Documentation
Extensive documentation for the bitstring module is available.
Some starting points are given below:
* [Overview](https://bitstring.readthedocs.io/en/stable/index.html)
* [Quick Reference](https://bitstring.readthedocs.io/en/stable/quick_reference.html)
* [Full Reference](https://bitstring.readthedocs.io/en/stable/reference.html)
There is also an introductory walkthrough notebook on [binder](https://mybinder.org/v2/gh/scott-griffiths/bitstring/main?labpath=doc%2Fwalkthrough.ipynb).
# Examples
### Installation
```
$ pip install bitstring
```
### Creation
```pycon
>>> from bitstring import Bits, BitArray, BitStream, pack
>>> a = BitArray(bin='00101')
>>> b = Bits(a_file_object)
>>> c = BitArray('0xff, 0b101, 0o65, uint6=22')
>>> d = pack('intle16, hex=a, 0b1', 100, a='0x34f')
>>> e = pack('<16h', *range(16))
```
### Different interpretations, slicing and concatenation
```pycon
>>> a = BitArray('0x3348')
>>> a.hex, a.bin, a.uint, a.float, a.bytes
('3348', '0011001101001000', 13128, 0.2275390625, b'3H')
>>> a[10:3:-1].bin
'0101100'
>>> '0b100' + 3*a
BitArray('0x866906690669, 0b000')
```
### Reading data sequentially
```pycon
>>> b = BitStream('0x160120f')
>>> b.read(12).hex
'160'
>>> b.pos = 0
>>> b.read('uint12')
352
>>> b.readlist('uint12, bin3')
[288, '111']
```
### Searching, inserting and deleting
```pycon
>>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f'
>>> c.find('0x48')
(8,)
>>> c.replace('0b001', '0xabc')
>>> c.insert('0b0000', pos=3)
>>> del c[12:16]
```
### Arrays of fixed-length formats
```pycon
>>> from bitstring import Array
>>> a = Array('uint7', [9, 100, 3, 1])
>>> a.data
BitArray('0x1390181')
>>> a[::2] *= 5
>>> a
Array('uint7', [45, 100, 15, 1])
```
<sub>Copyright (c) 2006 - 2024 Scott Griffiths</sub>

View File

@@ -0,0 +1,39 @@
bitstring-4.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
bitstring-4.2.3.dist-info/LICENSE,sha256=NwXu1akj812b-sofEOkTbMhNbldlcK7GYb2mmZHxKeo,1106
bitstring-4.2.3.dist-info/METADATA,sha256=aUUR8ljmKXchDLSPxTGzHot_7Wi659yUYUWS5Zi-3Ek,5017
bitstring-4.2.3.dist-info/RECORD,,
bitstring-4.2.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
bitstring-4.2.3.dist-info/top_level.txt,sha256=9Xh4qfKH0fMhwxzzkSBk3uzTRGiYPPM1FuYFAgCB-ks,10
bitstring/__init__.py,sha256=GjT0V01IeR3DBanzVa5utd4csONZx-PUSr6H0fuXnNU,13899
bitstring/__main__.py,sha256=Lg6ARiAqKssp_uj1msYpiTGDybhJRshQmeftQ-yTpc0,1658
bitstring/__pycache__/__init__.cpython-312.pyc,,
bitstring/__pycache__/__main__.cpython-312.pyc,,
bitstring/__pycache__/array_.cpython-312.pyc,,
bitstring/__pycache__/bitarray_.cpython-312.pyc,,
bitstring/__pycache__/bits.cpython-312.pyc,,
bitstring/__pycache__/bitstore.cpython-312.pyc,,
bitstring/__pycache__/bitstore_helpers.cpython-312.pyc,,
bitstring/__pycache__/bitstream.cpython-312.pyc,,
bitstring/__pycache__/bitstring_options.cpython-312.pyc,,
bitstring/__pycache__/dtypes.cpython-312.pyc,,
bitstring/__pycache__/exceptions.cpython-312.pyc,,
bitstring/__pycache__/fp8.cpython-312.pyc,,
bitstring/__pycache__/luts.cpython-312.pyc,,
bitstring/__pycache__/methods.cpython-312.pyc,,
bitstring/__pycache__/mxfp.cpython-312.pyc,,
bitstring/__pycache__/utils.cpython-312.pyc,,
bitstring/array_.py,sha256=d4vuZAc0ecJC95RD5_WPApehyH-ZGms4pcxl2I0R90I,36201
bitstring/bitarray_.py,sha256=kZTVYBo32vQuhQ4TZQldQkqRE5OMPVoKCoxEfKOTg8U,22319
bitstring/bits.py,sha256=N2bszuYM_WrTfqx-5-m56Vu_XgPZLeTta4xysBqCSYs,76754
bitstring/bitstore.py,sha256=jHk1cwBPqCJoYq2TEgIE37u_7K8HZq5K0L__X8edMgc,10337
bitstring/bitstore_helpers.py,sha256=6UHBomeOZlfQmY_Wa17U1zi7FBo73n2D9GPPmPztIrk,9107
bitstring/bitstream.py,sha256=Hu1ZpcCeP6lfpJiKAwKoJI22A-vsKATN1F2RvnS3rFY,29017
bitstring/bitstring_options.py,sha256=54Io4xLDylwXdRQpb12eNkSpSXQhRTW9Aidx7G42h7w,3560
bitstring/dtypes.py,sha256=oBrcavnz15vaBZ_GOVm0tOaW6Ml0oXoz8RyZSyqG6Zo,16897
bitstring/exceptions.py,sha256=7O3oVJJgvUOadqIzg6U-8fUGyOTu_lmjbMtX3yPc18o,553
bitstring/fp8.py,sha256=8CIiGlijn9OmBwdpJOl-1oVPUY6qVL2jGl1N8u8LjAg,3768
bitstring/luts.py,sha256=KPuXC9MvTOQSOlb0bFBKbnuxXuVBYlhaFEdIgSrwtzs,27072
bitstring/methods.py,sha256=3JM4jo8B6r3xQYY2nvbibmf7byQYTHTZye0F8D2lF10,4351
bitstring/mxfp.py,sha256=IxXK0SZy5RTD883YwATg6tlqVfl1uCFQplp5BVBwx9Q,9058
bitstring/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
bitstring/utils.py,sha256=tuWHppQUcCVLbtrZoO7eUkpNk6pJLtsOZT7QdWADW78,9221

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.43.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1,337 @@
#!/usr/bin/env python
r"""
This package defines classes that simplify bit-wise creation, manipulation and
interpretation of data.
Classes:
Bits -- An immutable container for binary data.
BitArray -- A mutable container for binary data.
ConstBitStream -- An immutable container with streaming methods.
BitStream -- A mutable container with streaming methods.
Array -- An efficient list-like container where each item has a fixed-length binary format.
Dtype -- Encapsulate the data types used in the other classes.
Functions:
pack -- Create a BitStream from a format string.
Data:
options -- Module-wide options.
Exceptions:
Error -- Module exception base class.
CreationError -- Error during creation.
InterpretError -- Inappropriate interpretation of binary data.
ByteAlignError -- Whole byte position or length needed.
ReadError -- Reading or peeking past the end of a bitstring.
https://github.com/scott-griffiths/bitstring
"""
__licence__ = """
The MIT License
Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
__version__ = "4.2.3"
__author__ = "Scott Griffiths"
import sys
from .bits import Bits
from .bitstring_options import Options
from .bitarray_ import BitArray
from .bitstream import ConstBitStream, BitStream
from .methods import pack
from .array_ import Array
from .exceptions import Error, ReadError, InterpretError, ByteAlignError, CreationError
from .dtypes import DtypeDefinition, dtype_register, Dtype
import types
from typing import List, Tuple, Literal
from .mxfp import decompress_luts as mxfp_decompress_luts
from .fp8 import decompress_luts as binary8_decompress_luts
# Decompress the LUTs for the exotic floating point formats
mxfp_decompress_luts()
binary8_decompress_luts()
# The Options class returns a singleton.
options = Options()
# These get defined properly by the module magic below. This just stops mypy complaining about them.
bytealigned = lsb0 = None
# An opaque way of adding module level properties. Taken from https://peps.python.org/pep-0549/
# This is now deprecated. Use the options object directly instead.
class _MyModuleType(types.ModuleType):
@property
def bytealigned(self) -> bool:
"""Determines whether a number of methods default to working only on byte boundaries."""
return options.bytealigned
@bytealigned.setter
def bytealigned(self, value: bool) -> None:
"""Determines whether a number of methods default to working only on byte boundaries."""
options.bytealigned = value
@property
def lsb0(self) -> bool:
"""If True, the least significant bit (the final bit) is indexed as bit zero."""
return options.lsb0
@lsb0.setter
def lsb0(self, value: bool) -> None:
"""If True, the least significant bit (the final bit) is indexed as bit zero."""
options.lsb0 = value
sys.modules[__name__].__class__ = _MyModuleType
# These methods convert a bit length to the number of characters needed to print it for different interpretations.
def hex_bits2chars(bitlength: int):
# One character for every 4 bits
return bitlength // 4
def oct_bits2chars(bitlength: int):
# One character for every 3 bits
return bitlength // 3
def bin_bits2chars(bitlength: int):
# One character for each bit
return bitlength
def bytes_bits2chars(bitlength: int):
# One character for every 8 bits
return bitlength // 8
def uint_bits2chars(bitlength: int):
# How many characters is largest possible int of this length?
return len(str((1 << bitlength) - 1))
def int_bits2chars(bitlength: int):
# How many characters is largest negative int of this length? (To include minus sign).
return len(str((-1 << (bitlength - 1))))
def float_bits2chars(bitlength: Literal[16, 32, 64]):
# These bit lengths were found by looking at lots of possible values
if bitlength in [16, 32]:
return 23 # Empirical value
else:
return 24 # Empirical value
def p3binary_bits2chars(_: Literal[8]):
return 19 # Empirical value
def p4binary_bits2chars(_: Literal[8]):
# Found by looking at all the possible values
return 13 # Empirical value
def e4m3mxfp_bits2chars(_: Literal[8]):
return 13
def e5m2mxfp_bits2chars(_: Literal[8]):
return 19
def e3m2mxfp_bits2chars(_: Literal[6]):
# Not sure what the best value is here. It's 7 without considering the scale that could be applied.
return 7
def e2m3mxfp_bits2chars(_: Literal[6]):
# Not sure what the best value is here.
return 7
def e2m1mxfp_bits2chars(_: Literal[4]):
# Not sure what the best value is here.
return 7
def e8m0mxfp_bits2chars(_: Literal[8]):
# Has same range as float32
return 23
def mxint_bits2chars(_: Literal[8]):
# Not sure what the best value is here.
return 10
def bfloat_bits2chars(_: Literal[16]):
# Found by looking at all the possible values
return 23 # Empirical value
def bits_bits2chars(bitlength: int):
# For bits type we can see how long it needs to be printed by trying any value
temp = Bits(bitlength)
return len(str(temp))
def bool_bits2chars(_: Literal[1]):
# Bools are printed as 1 or 0, not True or False, so are one character each
return 1
dtype_definitions = [
# Integer types
DtypeDefinition('uint', Bits._setuint, Bits._getuint, int, False, uint_bits2chars,
description="a two's complement unsigned int"),
DtypeDefinition('uintle', Bits._setuintle, Bits._getuintle, int, False, uint_bits2chars,
allowed_lengths=(8, 16, 24, ...), description="a two's complement little-endian unsigned int"),
DtypeDefinition('uintbe', Bits._setuintbe, Bits._getuintbe, int, False, uint_bits2chars,
allowed_lengths=(8, 16, 24, ...), description="a two's complement big-endian unsigned int"),
DtypeDefinition('int', Bits._setint, Bits._getint, int, True, int_bits2chars,
description="a two's complement signed int"),
DtypeDefinition('intle', Bits._setintle, Bits._getintle, int, True, int_bits2chars,
allowed_lengths=(8, 16, 24, ...), description="a two's complement little-endian signed int"),
DtypeDefinition('intbe', Bits._setintbe, Bits._getintbe, int, True, int_bits2chars,
allowed_lengths=(8, 16, 24, ...), description="a two's complement big-endian signed int"),
# String types
DtypeDefinition('hex', Bits._sethex, Bits._gethex, str, False, hex_bits2chars,
allowed_lengths=(0, 4, 8, ...), description="a hexadecimal string"),
DtypeDefinition('bin', Bits._setbin_safe, Bits._getbin, str, False, bin_bits2chars,
description="a binary string"),
DtypeDefinition('oct', Bits._setoct, Bits._getoct, str, False, oct_bits2chars,
allowed_lengths=(0, 3, 6, ...), description="an octal string"),
# Float types
DtypeDefinition('float', Bits._setfloatbe, Bits._getfloatbe, float, True, float_bits2chars,
allowed_lengths=(16, 32, 64), description="a big-endian floating point number"),
DtypeDefinition('floatle', Bits._setfloatle, Bits._getfloatle, float, True, float_bits2chars,
allowed_lengths=(16, 32, 64), description="a little-endian floating point number"),
DtypeDefinition('bfloat', Bits._setbfloatbe, Bits._getbfloatbe, float, True, bfloat_bits2chars,
allowed_lengths=(16,), description="a 16 bit big-endian bfloat floating point number"),
DtypeDefinition('bfloatle', Bits._setbfloatle, Bits._getbfloatle, float, True, bfloat_bits2chars,
allowed_lengths=(16,), description="a 16 bit little-endian bfloat floating point number"),
# Other known length types
DtypeDefinition('bits', Bits._setbits, Bits._getbits, Bits, False, bits_bits2chars,
description="a bitstring object"),
DtypeDefinition('bool', Bits._setbool, Bits._getbool, bool, False, bool_bits2chars,
allowed_lengths=(1,), description="a bool (True or False)"),
DtypeDefinition('bytes', Bits._setbytes, Bits._getbytes, bytes, False, bytes_bits2chars,
multiplier=8, description="a bytes object"),
# Unknown length types
DtypeDefinition('se', Bits._setse, Bits._getse, int, True, None,
variable_length=True, description="a signed exponential-Golomb code"),
DtypeDefinition('ue', Bits._setue, Bits._getue, int, False, None,
variable_length=True, description="an unsigned exponential-Golomb code"),
DtypeDefinition('sie', Bits._setsie, Bits._getsie, int, True, None,
variable_length=True, description="a signed interleaved exponential-Golomb code"),
DtypeDefinition('uie', Bits._setuie, Bits._getuie, int, False, None,
variable_length=True, description="an unsigned interleaved exponential-Golomb code"),
# Special case pad type
DtypeDefinition('pad', Bits._setpad, Bits._getpad, None, False, None,
description="a skipped section of padding"),
# MXFP and IEEE 8-bit float types
DtypeDefinition('p3binary', Bits._setp3binary, Bits._getp3binary, float, True, p3binary_bits2chars,
allowed_lengths=(8,), description="an 8 bit float with binary8p3 format"),
DtypeDefinition('p4binary', Bits._setp4binary, Bits._getp4binary, float, True, p4binary_bits2chars,
allowed_lengths=(8,), description="an 8 bit float with binary8p4 format"),
DtypeDefinition('e4m3mxfp', Bits._sete4m3mxfp, Bits._gete4m3mxfp, float, True, e4m3mxfp_bits2chars,
allowed_lengths=(8,), description="an 8 bit float with MXFP E4M3 format"),
DtypeDefinition('e5m2mxfp', Bits._sete5m2mxfp, Bits._gete5m2mxfp, float, True, e5m2mxfp_bits2chars,
allowed_lengths=(8,), description="an 8 bit float with MXFP E5M2 format"),
DtypeDefinition('e3m2mxfp', Bits._sete3m2mxfp, Bits._gete3m2mxfp, float, True, e3m2mxfp_bits2chars,
allowed_lengths=(6,), description="a 6 bit float with MXFP E3M2 format"),
DtypeDefinition('e2m3mxfp', Bits._sete2m3mxfp, Bits._gete2m3mxfp, float, True, e2m3mxfp_bits2chars,
allowed_lengths=(6,), description="a 6 bit float with MXFP E2M3 format"),
DtypeDefinition('e2m1mxfp', Bits._sete2m1mxfp, Bits._gete2m1mxfp, float, True, e2m1mxfp_bits2chars,
allowed_lengths=(4,), description="a 4 bit float with MXFP E2M1 format"),
DtypeDefinition('e8m0mxfp', Bits._sete8m0mxfp, Bits._gete8m0mxfp, float, False, e8m0mxfp_bits2chars,
allowed_lengths=(8,), description="an 8 bit float with MXFP E8M0 format"),
DtypeDefinition('mxint', Bits._setmxint, Bits._getmxint, float, True, mxint_bits2chars,
allowed_lengths=(8,), description="an 8 bit float with MXFP INT8 format"),
]
aliases: List[Tuple[str, str]] = [
# Floats default to big endian
('float', 'floatbe'),
('bfloat', 'bfloatbe'),
# Some single letter aliases for popular types
('int', 'i'),
('uint', 'u'),
('hex', 'h'),
('oct', 'o'),
('bin', 'b'),
('float', 'f'),
]
# Create native-endian aliases depending on the byteorder of the system
byteorder: str = sys.byteorder
if byteorder == 'little':
aliases.extend([
('uintle', 'uintne'),
('intle', 'intne'),
('floatle', 'floatne'),
('bfloatle', 'bfloatne'),
])
else:
aliases.extend([
('uintbe', 'uintne'),
('intbe', 'intne'),
('floatbe', 'floatne'),
('bfloatbe', 'bfloatne'),
])
for dt in dtype_definitions:
dtype_register.add_dtype(dt)
for alias in aliases:
dtype_register.add_dtype_alias(alias[0], alias[1])
property_docstrings = [f'{name} -- Interpret as {dtype_register[name].description}.' for name in dtype_register.names]
property_docstring = '\n '.join(property_docstrings)
# We can't be sure the docstrings are present, as it might be compiled without docstrings.
if Bits.__doc__ is not None:
Bits.__doc__ = Bits.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring)
if BitArray.__doc__ is not None:
BitArray.__doc__ = BitArray.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring)
if ConstBitStream.__doc__ is not None:
ConstBitStream.__doc__ = ConstBitStream.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring)
if BitStream.__doc__ is not None:
BitStream.__doc__ = BitStream.__doc__.replace('[GENERATED_PROPERTY_DESCRIPTIONS]', property_docstring)
__all__ = ['ConstBitStream', 'BitStream', 'BitArray', 'Array',
'Bits', 'pack', 'Error', 'ReadError', 'InterpretError',
'ByteAlignError', 'CreationError', 'bytealigned', 'lsb0', 'Dtype', 'options']

View File

@@ -0,0 +1,50 @@
import sys
from bitstring.bits import Bits
from bitstring.dtypes import Register
dtype_register = Register()
def main() -> None:
# check if final parameter is an interpretation string
fp = sys.argv[-1]
if fp in ['-h', '--help'] or len(sys.argv) == 1:
print("""Create and interpret a bitstring from command-line parameters.
Command-line parameters are concatenated and a bitstring created
from them. If the final parameter is either an interpretation string
or ends with a '.' followed by an interpretation string then that
interpretation of the bitstring will be used when printing it.
Typical usage might be invoking the Python module from a console
as a one-off calculation:
$ python -m bitstring int:16=-400
0xfe70
$ python -m bitstring float:32=0.2 bin
00111110010011001100110011001101
$ python -m bitstring 0xff 3*0b01,0b11 uint
65367
$ python -m bitstring hex=01, uint:12=352.hex
01160
""")
return
if fp in dtype_register.names:
# concatenate all other parameters and interpret using the final one
b1 = Bits(','.join(sys.argv[1: -1]))
print(b1._readtoken(fp, 0, b1.__len__())[0])
else:
# does final parameter end with a dot then an interpretation string?
interp = fp[fp.rfind('.') + 1:]
if interp in dtype_register.names:
sys.argv[-1] = fp[:fp.rfind('.')]
b1 = Bits(','.join(sys.argv[1:]))
print(b1._readtoken(interp, 0, b1.__len__())[0])
else:
# No interpretation - just use default print
b1 = Bits(','.join(sys.argv[1:]))
print(b1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,782 @@
from __future__ import annotations
import math
import numbers
from collections.abc import Sized
from bitstring.exceptions import CreationError
from typing import Union, List, Iterable, Any, Optional, BinaryIO, overload, TextIO
from bitstring.bits import Bits, BitsType
from bitstring.bitarray_ import BitArray
from bitstring.dtypes import Dtype, dtype_register
from bitstring import utils
from bitstring.bitstring_options import Options, Colour
import copy
import array
import operator
import io
import sys
# The possible types stored in each element of the Array
ElementType = Union[float, str, int, bytes, bool, Bits]
options = Options()
class Array:
"""Return an Array whose elements are initialised according to the fmt string.
The dtype string can be typecode as used in the struct module or any fixed-length bitstring
format.
a = Array('>H', [1, 15, 105])
b = Array('int5', [-9, 0, 4])
The Array data is stored compactly as a BitArray object and the Array behaves very like
a list of items of the given format. Both the Array data and fmt properties can be freely
modified after creation. If the data length is not a multiple of the fmt length then the
Array will have 'trailing_bits' which will prevent some methods from appending to the
Array.
Methods:
append() -- Append a single item to the end of the Array.
byteswap() -- Change byte endianness of all items.
count() -- Count the number of occurences of a value.
extend() -- Append new items to the end of the Array from an iterable.
fromfile() -- Append items read from a file object.
insert() -- Insert an item at a given position.
pop() -- Remove and return an item.
pp() -- Pretty print the Array.
reverse() -- Reverse the order of all items.
tobytes() -- Return Array data as bytes object, padding with zero bits at the end if needed.
tofile() -- Write Array data to a file, padding with zero bits at the end if needed.
tolist() -- Return Array items as a list.
Special methods:
Also available are the operators [], ==, !=, +, *, <<, >>, &, |, ^,
plus the mutating operators [], +=, *=, <<=, >>=, &=, |=, ^=.
Properties:
data -- The BitArray binary data of the Array. Can be freely modified.
dtype -- The format string or typecode. Can be freely modified.
itemsize -- The length *in bits* of a single item. Read only.
trailing_bits -- If the data length is not a multiple of the fmt length, this BitArray
gives the leftovers at the end of the data.
"""
def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Array, array.array, Iterable, Bits, bytes, bytearray, memoryview, BinaryIO]] = None,
trailing_bits: Optional[BitsType] = None) -> None:
self.data = BitArray()
if isinstance(dtype, Dtype) and dtype.scale == 'auto':
if isinstance(initializer, (int, Bits, bytes, bytearray, memoryview, BinaryIO)):
raise TypeError("An Array with an 'auto' scale factor can only be created from an iterable of values.")
auto_scale = self._calculate_auto_scale(initializer, dtype.name, dtype.length)
dtype = Dtype(dtype.name, dtype.length, scale=auto_scale)
try:
self._set_dtype(dtype)
except ValueError as e:
raise CreationError(e)
if isinstance(initializer, numbers.Integral):
self.data = BitArray(initializer * self._dtype.bitlength)
elif isinstance(initializer, (Bits, bytes, bytearray, memoryview)):
self.data += initializer
elif isinstance(initializer, io.BufferedReader):
self.fromfile(initializer)
elif initializer is not None:
self.extend(initializer)
if trailing_bits is not None:
self.data += BitArray._create_from_bitstype(trailing_bits)
_largest_values = None
@staticmethod
def _calculate_auto_scale(initializer, name: str, length: Optional[int]) -> float:
# Now need to find the largest power of 2 representable with this format.
if Array._largest_values is None:
Array._largest_values = {
'mxint8': Bits('0b01111111').mxint8, # 1.0 + 63.0/64.0,
'e2m1mxfp4': Bits('0b0111').e2m1mxfp4, # 6.0
'e2m3mxfp6': Bits('0b011111').e2m3mxfp6, # 7.5
'e3m2mxfp6': Bits('0b011111').e3m2mxfp6, # 28.0
'e4m3mxfp8': Bits('0b01111110').e4m3mxfp8, # 448.0
'e5m2mxfp8': Bits('0b01111011').e5m2mxfp8, # 57344.0
'p4binary8': Bits('0b01111110').p4binary8, # 224.0
'p3binary8': Bits('0b01111110').p3binary8, # 49152.0
'float16': Bits('0x7bff').float16, # 65504.0
# The bfloat range is so large the scaling algorithm doesn't work well, so I'm disallowing it.
# 'bfloat16': Bits('0x7f7f').bfloat16, # 3.38953139e38,
}
if f'{name}{length}' in Array._largest_values.keys():
float_values = Array('float64', initializer).tolist()
if not float_values:
raise ValueError("Can't calculate an 'auto' scale with an empty Array initializer.")
max_float_value = max(abs(x) for x in float_values)
if max_float_value == 0:
# This special case isn't covered in the standard. I'm choosing to return no scale.
return 1.0
# We need to find the largest power of 2 that is less than the max value
log2 = math.floor(math.log2(max_float_value))
lp2 = math.floor(math.log2(Array._largest_values[f'{name}{length}']))
lg_scale = log2 - lp2
# Saturate at values representable in E8M0 format.
if lg_scale > 127:
lg_scale = 127
elif lg_scale < -127:
lg_scale = -127
return 2 ** lg_scale
else:
raise ValueError(f"Can't calculate auto scale for format '{name}{length}'. "
f"This feature is only available for these formats: {list(Array._largest_values.keys())}.")
@property
def itemsize(self) -> int:
return self._dtype.length
@property
def trailing_bits(self) -> BitArray:
trailing_bit_length = len(self.data) % self._dtype.bitlength
return BitArray() if trailing_bit_length == 0 else self.data[-trailing_bit_length:]
@property
def dtype(self) -> Dtype:
return self._dtype
@dtype.setter
def dtype(self, new_dtype: Union[str, Dtype]) -> None:
self._set_dtype(new_dtype)
def _set_dtype(self, new_dtype: Union[str, Dtype]) -> None:
if isinstance(new_dtype, Dtype):
self._dtype = new_dtype
else:
try:
dtype = Dtype(new_dtype)
except ValueError:
name_length = utils.parse_single_struct_token(new_dtype)
if name_length is not None:
dtype = Dtype(name_length[0], name_length[1])
else:
raise ValueError(f"Inappropriate Dtype for Array: '{new_dtype}'.")
if dtype.length is None:
raise ValueError(f"A fixed length format is needed for an Array, received '{new_dtype}'.")
self._dtype = dtype
if self._dtype.scale == 'auto':
raise ValueError("A Dtype with an 'auto' scale factor can only be used when creating a new Array.")
def _create_element(self, value: ElementType) -> Bits:
"""Create Bits from value according to the token_name and token_length"""
b = self._dtype.build(value)
if len(b) != self._dtype.length:
raise ValueError(f"The value {value!r} has the wrong length for the format '{self._dtype}'.")
return b
def __len__(self) -> int:
return len(self.data) // self._dtype.length
@overload
def __getitem__(self, key: slice) -> Array:
...
@overload
def __getitem__(self, key: int) -> ElementType:
...
def __getitem__(self, key: Union[slice, int]) -> Union[Array, ElementType]:
if isinstance(key, slice):
start, stop, step = key.indices(len(self))
if step != 1:
d = BitArray()
for s in range(start * self._dtype.length, stop * self._dtype.length, step * self._dtype.length):
d.append(self.data[s: s + self._dtype.length])
a = self.__class__(self._dtype)
a.data = d
return a
else:
a = self.__class__(self._dtype)
a.data = self.data[start * self._dtype.length: stop * self._dtype.length]
return a
else:
if key < 0:
key += len(self)
if key < 0 or key >= len(self):
raise IndexError(f"Index {key} out of range for Array of length {len(self)}.")
return self._dtype.read_fn(self.data, start=self._dtype.length * key)
@overload
def __setitem__(self, key: slice, value: Iterable[ElementType]) -> None:
...
@overload
def __setitem__(self, key: int, value: ElementType) -> None:
...
def __setitem__(self, key: Union[slice, int], value: Union[Iterable[ElementType], ElementType]) -> None:
if isinstance(key, slice):
start, stop, step = key.indices(len(self))
if not isinstance(value, Iterable):
raise TypeError("Can only assign an iterable to a slice.")
if step == 1:
new_data = BitArray()
for x in value:
new_data += self._create_element(x)
self.data[start * self._dtype.length: stop * self._dtype.length] = new_data
return
items_in_slice = len(range(start, stop, step))
if not isinstance(value, Sized):
value = list(value)
if len(value) == items_in_slice:
for s, v in zip(range(start, stop, step), value):
self.data.overwrite(self._create_element(v), s * self._dtype.length)
else:
raise ValueError(f"Can't assign {len(value)} values to an extended slice of length {items_in_slice}.")
else:
if key < 0:
key += len(self)
if key < 0 or key >= len(self):
raise IndexError(f"Index {key} out of range for Array of length {len(self)}.")
start = self._dtype.length * key
self.data.overwrite(self._create_element(value), start)
return
def __delitem__(self, key: Union[slice, int]) -> None:
if isinstance(key, slice):
start, stop, step = key.indices(len(self))
if step == 1:
self.data.__delitem__(slice(start * self._dtype.length, stop * self._dtype.length))
return
# We need to delete from the end or the earlier positions will change
r = reversed(range(start, stop, step)) if step > 0 else range(start, stop, step)
for s in r:
self.data.__delitem__(slice(s * self._dtype.length, (s + 1) * self._dtype.length))
else:
if key < 0:
key += len(self)
if key < 0 or key >= len(self):
raise IndexError
start = self._dtype.length * key
del self.data[start: start + self._dtype.length]
def __repr__(self) -> str:
list_str = f"{self.tolist()}"
trailing_bit_length = len(self.data) % self._dtype.length
final_str = "" if trailing_bit_length == 0 else ", trailing_bits=" + repr(
self.data[-trailing_bit_length:])
return f"Array('{self._dtype}', {list_str}{final_str})"
def astype(self, dtype: Union[str, Dtype]) -> Array:
"""Return Array with elements of new dtype, initialised from current Array."""
new_array = self.__class__(dtype, self.tolist())
return new_array
def tolist(self) -> List[ElementType]:
return [self._dtype.read_fn(self.data, start=start)
for start in range(0, len(self.data) - self._dtype.length + 1, self._dtype.length)]
def append(self, x: ElementType) -> None:
if len(self.data) % self._dtype.length != 0:
raise ValueError("Cannot append to Array as its length is not a multiple of the format length.")
self.data += self._create_element(x)
def extend(self, iterable: Union[Array, array.array, Iterable[Any]]) -> None:
if len(self.data) % self._dtype.length != 0:
raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
if isinstance(iterable, Array):
if self._dtype.name != iterable._dtype.name or self._dtype.length != iterable._dtype.length:
raise TypeError(
f"Cannot extend an Array with format '{self._dtype}' from an Array of format '{iterable._dtype}'.")
# No need to iterate over the elements, we can just append the data
self.data.append(iterable.data)
elif isinstance(iterable, array.array):
# array.array types are always native-endian, hence the '='
name_value = utils.parse_single_struct_token('=' + iterable.typecode)
if name_value is None:
raise ValueError(f"Cannot extend from array with typecode {iterable.typecode}.")
other_dtype = dtype_register.get_dtype(*name_value, scale=None)
if self._dtype.name != other_dtype.name or self._dtype.length != other_dtype.length:
raise ValueError(
f"Cannot extend an Array with format '{self._dtype}' from an array with typecode '{iterable.typecode}'.")
self.data += iterable.tobytes()
else:
if isinstance(iterable, str):
raise TypeError("Can't extend an Array with a str.")
for item in iterable:
self.data += self._create_element(item)
def insert(self, i: int, x: ElementType) -> None:
"""Insert a new element into the Array at position i.
"""
i = min(i, len(self)) # Inserting beyond len of array inserts at the end (copying standard behaviour)
self.data.insert(self._create_element(x), i * self._dtype.length)
def pop(self, i: int = -1) -> ElementType:
"""Return and remove an element of the Array.
Default is to return and remove the final element.
"""
if len(self) == 0:
raise IndexError("Can't pop from an empty Array.")
x = self[i]
del self[i]
return x
def byteswap(self) -> None:
"""Change the endianness in-place of all items in the Array.
If the Array format is not a whole number of bytes a ValueError will be raised.
"""
if self._dtype.length % 8 != 0:
raise ValueError(
f"byteswap can only be used for whole-byte elements. The '{self._dtype}' format is {self._dtype.length} bits long.")
self.data.byteswap(self.itemsize // 8)
def count(self, value: ElementType) -> int:
"""Return count of Array items that equal value.
value -- The quantity to compare each Array element to. Type should be appropriate for the Array format.
For floating point types using a value of float('nan') will count the number of elements that are NaN.
"""
if math.isnan(value):
return sum(math.isnan(i) for i in self)
else:
return sum(i == value for i in self)
def tobytes(self) -> bytes:
"""Return the Array data as a bytes object, padding with zero bits if needed.
Up to seven zero bits will be added at the end to byte align.
"""
return self.data.tobytes()
def tofile(self, f: BinaryIO) -> None:
"""Write the Array data to a file object, padding with zero bits if needed.
Up to seven zero bits will be added at the end to byte align.
"""
self.data.tofile(f)
def fromfile(self, f: BinaryIO, n: Optional[int] = None) -> None:
trailing_bit_length = len(self.data) % self._dtype.bitlength
if trailing_bit_length != 0:
raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.bitlength} bits).")
new_data = Bits(f)
max_items = len(new_data) // self._dtype.length
items_to_append = max_items if n is None else min(n, max_items)
self.data += new_data[0: items_to_append * self._dtype.bitlength]
if n is not None and items_to_append < n:
raise EOFError(f"Only {items_to_append} were appended, not the {n} items requested.")
def reverse(self) -> None:
trailing_bit_length = len(self.data) % self._dtype.length
if trailing_bit_length != 0:
raise ValueError(f"Cannot reverse the items in the Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
for start_bit in range(0, len(self.data) // 2, self._dtype.length):
start_swap_bit = len(self.data) - start_bit - self._dtype.length
temp = self.data[start_bit: start_bit + self._dtype.length]
self.data[start_bit: start_bit + self._dtype.length] = self.data[
start_swap_bit: start_swap_bit + self._dtype.length]
self.data[start_swap_bit: start_swap_bit + self._dtype.length] = temp
def pp(self, fmt: Optional[str] = None, width: int = 120,
show_offset: bool = True, stream: TextIO = sys.stdout) -> None:
"""Pretty-print the Array contents.
fmt -- Data format string. Defaults to current Array dtype.
width -- Max width of printed lines in characters. Defaults to 120. A single group will always
be printed per line even if it exceeds the max width.
show_offset -- If True shows the element offset in the first column of each line.
stream -- A TextIO object with a write() method. Defaults to sys.stdout.
"""
colour = Colour(not options.no_color)
sep = ' '
dtype2 = None
tidy_fmt = None
if fmt is None:
fmt = self.dtype
dtype1 = self.dtype
tidy_fmt = "dtype='" + colour.purple + str(self.dtype) + "'" + colour.off
else:
token_list = utils.preprocess_tokens(fmt)
if len(token_list) not in [1, 2]:
raise ValueError(f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {len(token_list)} tokens.")
name1, length1 = utils.parse_name_length_token(token_list[0])
dtype1 = Dtype(name1, length1)
if len(token_list) == 2:
name2, length2 = utils.parse_name_length_token(token_list[1])
dtype2 = Dtype(name2, length2)
token_length = dtype1.bitlength
if dtype2 is not None:
# For two types we're OK as long as they don't have different lengths given.
if dtype1.bitlength is not None and dtype2.bitlength is not None and dtype1.bitlength != dtype2.bitlength:
raise ValueError(f"Two different format lengths specified ('{fmt}'). Either specify just one, or two the same length.")
if token_length is None:
token_length = dtype2.bitlength
if token_length is None:
token_length = self.itemsize
trailing_bit_length = len(self.data) % token_length
format_sep = " : " # String to insert on each line between multiple formats
if tidy_fmt is None:
tidy_fmt = colour.purple + str(dtype1) + colour.off
if dtype2 is not None:
tidy_fmt += ', ' + colour.blue + str(dtype2) + colour.off
tidy_fmt = "fmt='" + tidy_fmt + "'"
data = self.data if trailing_bit_length == 0 else self.data[0: -trailing_bit_length]
length = len(self.data) // token_length
len_str = colour.green + str(length) + colour.off
stream.write(f"<{self.__class__.__name__} {tidy_fmt}, length={len_str}, itemsize={token_length} bits, total data size={(len(self.data) + 7) // 8} bytes> [\n")
data._pp(dtype1, dtype2, token_length, width, sep, format_sep, show_offset, stream, False, token_length)
stream.write("]")
if trailing_bit_length != 0:
stream.write(" + trailing_bits = " + str(self.data[-trailing_bit_length:]))
stream.write("\n")
def equals(self, other: Any) -> bool:
"""Return True if format and all Array items are equal."""
if isinstance(other, Array):
if self._dtype.length != other._dtype.length:
return False
if self._dtype.name != other._dtype.name:
return False
if self.data != other.data:
return False
return True
elif isinstance(other, array.array):
# Assume we are comparing with an array type
if self.trailing_bits:
return False
# array's itemsize is in bytes, not bits.
if self.itemsize != other.itemsize * 8:
return False
if len(self) != len(other):
return False
if self.tolist() != other.tolist():
return False
return True
return False
def __iter__(self) -> Iterable[ElementType]:
start = 0
for _ in range(len(self)):
yield self._dtype.read_fn(self.data, start=start)
start += self._dtype.length
def __copy__(self) -> Array:
a_copy = self.__class__(self._dtype)
a_copy.data = copy.copy(self.data)
return a_copy
def _apply_op_to_all_elements(self, op, value: Union[int, float, None], is_comparison: bool = False) -> Array:
"""Apply op with value to each element of the Array and return a new Array"""
new_array = self.__class__('bool' if is_comparison else self._dtype)
new_data = BitArray()
failures = index = 0
msg = ''
if value is not None:
def partial_op(a):
return op(a, value)
else:
def partial_op(a):
return op(a)
for i in range(len(self)):
v = self._dtype.read_fn(self.data, start=self._dtype.length * i)
try:
new_data.append(new_array._create_element(partial_op(v)))
except (CreationError, ZeroDivisionError, ValueError) as e:
if failures == 0:
msg = str(e)
index = i
failures += 1
if failures != 0:
raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. "
f'First error at index {index} was: "{msg}"')
new_array.data = new_data
return new_array
def _apply_op_to_all_elements_inplace(self, op, value: Union[int, float]) -> Array:
"""Apply op with value to each element of the Array in place."""
# This isn't really being done in-place, but it's simpler and faster for now?
new_data = BitArray()
failures = index = 0
msg = ''
for i in range(len(self)):
v = self._dtype.read_fn(self.data, start=self._dtype.length * i)
try:
new_data.append(self._create_element(op(v, value)))
except (CreationError, ZeroDivisionError, ValueError) as e:
if failures == 0:
msg = str(e)
index = i
failures += 1
if failures != 0:
raise ValueError(f"Applying operator '{op.__name__}' to Array caused {failures} errors. "
f'First error at index {index} was: "{msg}"')
self.data = new_data
return self
def _apply_bitwise_op_to_all_elements(self, op, value: BitsType) -> Array:
"""Apply op with value to each element of the Array as an unsigned integer and return a new Array"""
a_copy = self[:]
a_copy._apply_bitwise_op_to_all_elements_inplace(op, value)
return a_copy
def _apply_bitwise_op_to_all_elements_inplace(self, op, value: BitsType) -> Array:
"""Apply op with value to each element of the Array as an unsigned integer in place."""
value = BitArray._create_from_bitstype(value)
if len(value) != self._dtype.length:
raise ValueError(f"Bitwise op needs a bitstring of length {self._dtype.length} to match format {self._dtype}.")
for start in range(0, len(self) * self._dtype.length, self._dtype.length):
self.data[start: start + self._dtype.length] = op(self.data[start: start + self._dtype.length], value)
return self
def _apply_op_between_arrays(self, op, other: Array, is_comparison: bool = False) -> Array:
if len(self) != len(other):
msg = f"Cannot operate element-wise on Arrays with different lengths ({len(self)} and {len(other)})."
if op in [operator.add, operator.iadd]:
msg += " Use extend() method to concatenate Arrays."
if op in [operator.eq, operator.ne]:
msg += " Use equals() method to compare Arrays for a single boolean result."
raise ValueError(msg)
if is_comparison:
new_type = dtype_register.get_dtype('bool', 1)
else:
new_type = self._promotetype(self._dtype, other._dtype)
new_array = self.__class__(new_type)
new_data = BitArray()
failures = index = 0
msg = ''
for i in range(len(self)):
a = self._dtype.read_fn(self.data, start=self._dtype.length * i)
b = other._dtype.read_fn(other.data, start=other._dtype.length * i)
try:
new_data.append(new_array._create_element(op(a, b)))
except (CreationError, ValueError, ZeroDivisionError) as e:
if failures == 0:
msg = str(e)
index = i
failures += 1
if failures != 0:
raise ValueError(f"Applying operator '{op.__name__}' between Arrays caused {failures} errors. "
f'First error at index {index} was: "{msg}"')
new_array.data = new_data
return new_array
@classmethod
def _promotetype(cls, type1: Dtype, type2: Dtype) -> Dtype:
"""When combining types which one wins?
1. We only deal with types representing floats or integers.
2. One of the two types gets returned. We never create a new one.
3. Floating point types always win against integer types.
4. Signed integer types always win against unsigned integer types.
5. Longer types win against shorter types.
6. In a tie the first type wins against the second type.
"""
def is_float(x): return x.return_type is float
def is_int(x): return x.return_type is int or x.return_type is bool
if is_float(type1) + is_int(type1) + is_float(type2) + is_int(type2) != 2:
raise ValueError(f"Only integer and floating point types can be combined - not '{type1}' and '{type2}'.")
# If same type choose the widest
if type1.name == type2.name:
return type1 if type1.length > type2.length else type2
# We choose floats above integers, irrespective of the widths
if is_float(type1) and is_int(type2):
return type1
if is_int(type1) and is_float(type2):
return type2
if is_float(type1) and is_float(type2):
return type2 if type2.length > type1.length else type1
assert is_int(type1) and is_int(type2)
if type1.is_signed and not type2.is_signed:
return type1
if type2.is_signed and not type1.is_signed:
return type2
return type2 if type2.length > type1.length else type1
# Operators between Arrays or an Array and scalar value
def __add__(self, other: Union[int, float, Array]) -> Array:
"""Add int or float to all elements."""
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.add, other)
return self._apply_op_to_all_elements(operator.add, other)
def __iadd__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.add, other)
return self._apply_op_to_all_elements_inplace(operator.add, other)
def __isub__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.sub, other)
return self._apply_op_to_all_elements_inplace(operator.sub, other)
def __sub__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.sub, other)
return self._apply_op_to_all_elements(operator.sub, other)
def __mul__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.mul, other)
return self._apply_op_to_all_elements(operator.mul, other)
def __imul__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.mul, other)
return self._apply_op_to_all_elements_inplace(operator.mul, other)
def __floordiv__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.floordiv, other)
return self._apply_op_to_all_elements(operator.floordiv, other)
def __ifloordiv__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.floordiv, other)
return self._apply_op_to_all_elements_inplace(operator.floordiv, other)
def __truediv__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.truediv, other)
return self._apply_op_to_all_elements(operator.truediv, other)
def __itruediv__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.truediv, other)
return self._apply_op_to_all_elements_inplace(operator.truediv, other)
def __rshift__(self, other: Union[int, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.rshift, other)
return self._apply_op_to_all_elements(operator.rshift, other)
def __lshift__(self, other: Union[int, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.lshift, other)
return self._apply_op_to_all_elements(operator.lshift, other)
def __irshift__(self, other: Union[int, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.rshift, other)
return self._apply_op_to_all_elements_inplace(operator.rshift, other)
def __ilshift__(self, other: Union[int, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.lshift, other)
return self._apply_op_to_all_elements_inplace(operator.lshift, other)
def __mod__(self, other: Union[int, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.mod, other)
return self._apply_op_to_all_elements(operator.mod, other)
def __imod__(self, other: Union[int, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.mod, other)
return self._apply_op_to_all_elements_inplace(operator.mod, other)
# Bitwise operators
def __and__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements(operator.iand, other)
def __iand__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements_inplace(operator.iand, other)
def __or__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements(operator.ior, other)
def __ior__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements_inplace(operator.ior, other)
def __xor__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements(operator.ixor, other)
def __ixor__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements_inplace(operator.ixor, other)
# Reverse operators between a scalar value and an Array
def __rmul__(self, other: Union[int, float]) -> Array:
return self._apply_op_to_all_elements(operator.mul, other)
def __radd__(self, other: Union[int, float]) -> Array:
return self._apply_op_to_all_elements(operator.add, other)
def __rsub__(self, other: Union[int, float]) -> Array:
# i - A == (-A) + i
neg = self._apply_op_to_all_elements(operator.neg, None)
return neg._apply_op_to_all_elements(operator.add, other)
# Reverse operators between a scalar and something that can be a BitArray.
def __rand__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements(operator.iand, other)
def __ror__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements(operator.ior, other)
def __rxor__(self, other: BitsType) -> Array:
return self._apply_bitwise_op_to_all_elements(operator.ixor, other)
# Comparison operators
def __lt__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.lt, other, is_comparison=True)
return self._apply_op_to_all_elements(operator.lt, other, is_comparison=True)
def __gt__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.gt, other, is_comparison=True)
return self._apply_op_to_all_elements(operator.gt, other, is_comparison=True)
def __ge__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.ge, other, is_comparison=True)
return self._apply_op_to_all_elements(operator.ge, other, is_comparison=True)
def __le__(self, other: Union[int, float, Array]) -> Array:
if isinstance(other, Array):
return self._apply_op_between_arrays(operator.le, other, is_comparison=True)
return self._apply_op_to_all_elements(operator.le, other, is_comparison=True)
def _eq_ne(self, op, other: Any) -> Array:
if isinstance(other, (int, float, str, Bits)):
return self._apply_op_to_all_elements(op, other, is_comparison=True)
try:
other = self.__class__(self.dtype, other)
except:
return NotImplemented
finally:
return self._apply_op_between_arrays(op, other, is_comparison=True)
def __eq__(self, other: Any) -> Array:
return self._eq_ne(operator.eq, other)
def __ne__(self, other: Any) -> Array:
return self._eq_ne(operator.ne, other)
# Unary operators
def __neg__(self):
return self._apply_op_to_all_elements(operator.neg, None)
def __abs__(self):
return self._apply_op_to_all_elements(operator.abs, None)

View File

@@ -0,0 +1,576 @@
from __future__ import annotations
import copy
import numbers
import re
from collections import abc
from typing import Union, List, Iterable, Any, Optional
from bitstring import utils
from bitstring.exceptions import CreationError, Error
from bitstring.bits import Bits, BitsType, TBits
import bitstring.dtypes
class BitArray(Bits):
"""A container holding a mutable sequence of bits.
Subclass of the immutable Bits class. Inherits all of its
methods (except __hash__) and adds mutating methods.
Mutating methods:
append() -- Append a bitstring.
byteswap() -- Change byte endianness in-place.
clear() -- Remove all bits from the bitstring.
insert() -- Insert a bitstring.
invert() -- Flip bit(s) between one and zero.
overwrite() -- Overwrite a section with a new bitstring.
prepend() -- Prepend a bitstring.
replace() -- Replace occurrences of one bitstring with another.
reverse() -- Reverse bits in-place.
rol() -- Rotate bits to the left.
ror() -- Rotate bits to the right.
set() -- Set bit(s) to 1 or 0.
Methods inherited from Bits:
all() -- Check if all specified bits are set to 1 or 0.
any() -- Check if any of specified bits are set to 1 or 0.
copy() -- Return a copy of the bitstring.
count() -- Count the number of bits set to 1 or 0.
cut() -- Create generator of constant sized chunks.
endswith() -- Return whether the bitstring ends with a sub-string.
find() -- Find a sub-bitstring in the current bitstring.
findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
fromstring() -- Create a bitstring from a formatted string.
join() -- Join bitstrings together using current bitstring.
pp() -- Pretty print the bitstring.
rfind() -- Seek backwards to find a sub-bitstring.
split() -- Create generator of chunks split by a delimiter.
startswith() -- Return whether the bitstring starts with a sub-bitstring.
tobitarray() -- Return bitstring as a bitarray from the bitarray package.
tobytes() -- Return bitstring as bytes, padding if needed.
tofile() -- Write bitstring to file, padding if needed.
unpack() -- Interpret bits using format string.
Special methods:
Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^=
in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^.
Properties:
[GENERATED_PROPERTY_DESCRIPTIONS]
len -- Length of the bitstring in bits.
"""
__slots__ = ()
# As BitArray objects are mutable, we shouldn't allow them to be hashed.
__hash__: None = None
def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None,
offset: Optional[int] = None, **kwargs) -> None:
"""Either specify an 'auto' initialiser:
A string of comma separated tokens, an integer, a file object,
a bytearray, a boolean iterable or another bitstring.
Or initialise via **kwargs with one (and only one) of:
bin -- binary string representation, e.g. '0b001010'.
hex -- hexadecimal string representation, e.g. '0x2ef'
oct -- octal string representation, e.g. '0o777'.
bytes -- raw data as a bytes object, for example read from a binary file.
int -- a signed integer.
uint -- an unsigned integer.
float / floatbe -- a big-endian floating point number.
bool -- a boolean (True or False).
se -- a signed exponential-Golomb code.
ue -- an unsigned exponential-Golomb code.
sie -- a signed interleaved exponential-Golomb code.
uie -- an unsigned interleaved exponential-Golomb code.
floatle -- a little-endian floating point number.
floatne -- a native-endian floating point number.
bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number.
bfloatle -- a little-endian bfloat format 16-bit floating point number.
bfloatne -- a native-endian bfloat format 16-bit floating point number.
intbe -- a signed big-endian whole byte integer.
intle -- a signed little-endian whole byte integer.
intne -- a signed native-endian whole byte integer.
uintbe -- an unsigned big-endian whole byte integer.
uintle -- an unsigned little-endian whole byte integer.
uintne -- an unsigned native-endian whole byte integer.
filename -- the path of a file which will be opened in binary read-only mode.
Other keyword arguments:
length -- length of the bitstring in bits, if needed and appropriate.
It must be supplied for all integer and float initialisers.
offset -- bit offset to the data. These offset bits are
ignored and this is intended for use when
initialising using 'bytes' or 'filename'.
"""
if self._bitstore.immutable:
self._bitstore = self._bitstore._copy()
self._bitstore.immutable = False
def copy(self: TBits) -> TBits:
"""Return a copy of the bitstring."""
return self.__copy__()
def __setattr__(self, attribute, value) -> None:
try:
# First try the ordinary attribute setter
super().__setattr__(attribute, value)
except AttributeError:
dtype = bitstring.dtypes.Dtype(attribute)
x = object.__new__(Bits)
if (set_fn := dtype.set_fn) is None:
raise AttributeError(f"Cannot set attribute '{attribute}' as it does not have a set_fn.")
set_fn(x, value)
if len(x) != dtype.bitlength:
raise CreationError(f"Can't initialise with value of length {len(x)} bits, "
f"as attribute has length of {dtype.bitlength} bits.")
self._bitstore = x._bitstore
return
def __iadd__(self, bs: BitsType) -> BitArray:
"""Append bs to current bitstring. Return self.
bs -- the bitstring to append.
"""
self._append(bs)
return self
def __copy__(self) -> BitArray:
"""Return a new copy of the BitArray."""
s_copy = BitArray()
s_copy._bitstore = self._bitstore._copy()
assert s_copy._bitstore.immutable is False
return s_copy
def _setitem_int(self, key: int, value: Union[BitsType, int]) -> None:
if isinstance(value, numbers.Integral):
if value == 0:
self._bitstore[key] = 0
return
if value in (1, -1):
self._bitstore[key] = 1
return
raise ValueError(f"Cannot set a single bit with integer {value}.")
try:
value = self._create_from_bitstype(value)
except TypeError:
raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.")
positive_key = key + len(self) if key < 0 else key
if positive_key < 0 or positive_key >= len(self._bitstore):
raise IndexError(f"Bit position {key} out of range.")
self._bitstore[positive_key: positive_key + 1] = value._bitstore
def _setitem_slice(self, key: slice, value: BitsType) -> None:
if isinstance(value, numbers.Integral):
value = int(value)
if key.step not in [None, -1, 1]:
if value in [0, 1]:
self.set(value, range(*key.indices(len(self))))
return
else:
raise ValueError("Can't assign an integer except 0 or 1 to a slice with a step value.")
# To find the length we first get the slice
s = self._bitstore.getslice(key.start, key.stop)
length = len(s)
# Now create an int of the correct length
if value >= 0:
value = self.__class__(uint=value, length=length)
else:
value = self.__class__(int=value, length=length)
else:
try:
value = self._create_from_bitstype(value)
except TypeError:
raise TypeError(f"Bitstring, integer or string expected. Got {type(value)}.")
self._bitstore.__setitem__(key, value._bitstore)
def __setitem__(self, key: Union[slice, int], value: BitsType) -> None:
if isinstance(key, numbers.Integral):
self._setitem_int(int(key), value)
else:
self._setitem_slice(key, value)
def __delitem__(self, key: Union[slice, int]) -> None:
"""Delete item or range.
>>> a = BitArray('0x001122')
>>> del a[8:16]
>>> print a
0x0022
"""
self._bitstore.__delitem__(key)
return
def __ilshift__(self: TBits, n: int) -> TBits:
"""Shift bits by n to the left in place. Return self.
n -- the number of bits to shift. Must be >= 0.
"""
if n < 0:
raise ValueError("Cannot shift by a negative amount.")
if not len(self):
raise ValueError("Cannot shift an empty bitstring.")
if not n:
return self
n = min(n, len(self))
return self._ilshift(n)
def __irshift__(self: TBits, n: int) -> TBits:
"""Shift bits by n to the right in place. Return self.
n -- the number of bits to shift. Must be >= 0.
"""
if n < 0:
raise ValueError("Cannot shift by a negative amount.")
if not len(self):
raise ValueError("Cannot shift an empty bitstring.")
if not n:
return self
n = min(n, len(self))
return self._irshift(n)
def __imul__(self: TBits, n: int) -> TBits:
"""Concatenate n copies of self in place. Return self.
Called for expressions of the form 'a *= 3'.
n -- The number of concatenations. Must be >= 0.
"""
if n < 0:
raise ValueError("Cannot multiply by a negative integer.")
return self._imul(n)
def __ior__(self: TBits, bs: BitsType) -> TBits:
bs = self._create_from_bitstype(bs)
self._bitstore |= bs._bitstore
return self
def __iand__(self: TBits, bs: BitsType) -> TBits:
bs = self._create_from_bitstype(bs)
self._bitstore &= bs._bitstore
return self
def __ixor__(self: TBits, bs: BitsType) -> TBits:
bs = self._create_from_bitstype(bs)
self._bitstore ^= bs._bitstore
return self
def _replace(self, old: Bits, new: Bits, start: int, end: int, count: int, bytealigned: Optional[bool]) -> int:
if bytealigned is None:
bytealigned = bitstring.options.bytealigned
# First find all the places where we want to do the replacements
starting_points: List[int] = []
for x in self.findall(old, start, end, bytealigned=bytealigned):
if not starting_points:
starting_points.append(x)
elif x >= starting_points[-1] + len(old):
# Can only replace here if it hasn't already been replaced!
starting_points.append(x)
if count != 0 and len(starting_points) == count:
break
if not starting_points:
return 0
replacement_list = [self._bitstore.getslice(0, starting_points[0])]
for i in range(len(starting_points) - 1):
replacement_list.append(new._bitstore)
replacement_list.append(
self._bitstore.getslice(starting_points[i] + len(old), starting_points[i + 1]))
# Final replacement
replacement_list.append(new._bitstore)
replacement_list.append(self._bitstore.getslice(starting_points[-1] + len(old), None))
if bitstring.options.lsb0:
# Addition of bitarray is always on the right, so assemble from other end
replacement_list.reverse()
self._bitstore.clear()
for r in replacement_list:
self._bitstore += r
return len(starting_points)
def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None,
count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int:
"""Replace all occurrences of old with new in place.
Returns number of replacements made.
old -- The bitstring to replace.
new -- The replacement bitstring.
start -- Any occurrences that start before this will not be replaced.
Defaults to 0.
end -- Any occurrences that finish after this will not be replaced.
Defaults to len(self).
count -- The maximum number of replacements to make. Defaults to
replace all occurrences.
bytealigned -- If True replacements will only be made on byte
boundaries.
Raises ValueError if old is empty or if start or end are
out of range.
"""
if count == 0:
return 0
old = self._create_from_bitstype(old)
new = self._create_from_bitstype(new)
if len(old) == 0:
raise ValueError("Empty bitstring cannot be replaced.")
start, end = self._validate_slice(start, end)
if new is self:
# Prevent self assignment woes
new = copy.copy(self)
return self._replace(old, new, start, end, 0 if count is None else count, bytealigned)
def insert(self, bs: BitsType, pos: int) -> None:
"""Insert bs at bit position pos.
bs -- The bitstring to insert.
pos -- The bit position to insert at.
Raises ValueError if pos < 0 or pos > len(self).
"""
bs = self._create_from_bitstype(bs)
if len(bs) == 0:
return
if bs is self:
bs = self._copy()
if pos < 0:
pos += len(self)
if not 0 <= pos <= len(self):
raise ValueError("Invalid insert position.")
self._insert(bs, pos)
def overwrite(self, bs: BitsType, pos: int) -> None:
"""Overwrite with bs at bit position pos.
bs -- The bitstring to overwrite with.
pos -- The bit position to begin overwriting from.
Raises ValueError if pos < 0 or pos > len(self).
"""
bs = self._create_from_bitstype(bs)
if len(bs) == 0:
return
if pos < 0:
pos += len(self)
if pos < 0 or pos > len(self):
raise ValueError("Overwrite starts outside boundary of bitstring.")
self._overwrite(bs, pos)
def append(self, bs: BitsType) -> None:
"""Append a bitstring to the current bitstring.
bs -- The bitstring to append.
"""
self._append(bs)
def prepend(self, bs: BitsType) -> None:
"""Prepend a bitstring to the current bitstring.
bs -- The bitstring to prepend.
"""
self._prepend(bs)
def _append_msb0(self, bs: BitsType) -> None:
self._addright(self._create_from_bitstype(bs))
def _append_lsb0(self, bs: BitsType) -> None:
bs = self._create_from_bitstype(bs)
self._addleft(bs)
def reverse(self, start: Optional[int] = None, end: Optional[int] = None) -> None:
"""Reverse bits in-place.
start -- Position of first bit to reverse. Defaults to 0.
end -- One past the position of the last bit to reverse.
Defaults to len(self).
Using on an empty bitstring will have no effect.
Raises ValueError if start < 0, end > len(self) or end < start.
"""
start, end = self._validate_slice(start, end)
if start == 0 and end == len(self):
self._bitstore.reverse()
return
s = self._slice(start, end)
s._bitstore.reverse()
self[start:end] = s
def set(self, value: Any, pos: Optional[Union[int, Iterable[int]]] = None) -> None:
"""Set one or many bits to 1 or 0.
value -- If bool(value) is True bits are set to 1, otherwise they are set to 0.
pos -- Either a single bit position or an iterable of bit positions.
Negative numbers are treated in the same way as slice indices.
Defaults to the entire bitstring.
Raises IndexError if pos < -len(self) or pos >= len(self).
"""
if pos is None:
# Set all bits to either 1 or 0
self._setint(-1 if value else 0)
return
if not isinstance(pos, abc.Iterable):
pos = (pos,)
v = 1 if value else 0
if isinstance(pos, range):
self._bitstore.__setitem__(slice(pos.start, pos.stop, pos.step), v)
return
for p in pos:
self._bitstore[p] = v
def invert(self, pos: Optional[Union[Iterable[int], int]] = None) -> None:
"""Invert one or many bits from 0 to 1 or vice versa.
pos -- Either a single bit position or an iterable of bit positions.
Negative numbers are treated in the same way as slice indices.
Raises IndexError if pos < -len(self) or pos >= len(self).
"""
if pos is None:
self._invert_all()
return
if not isinstance(pos, abc.Iterable):
pos = (pos,)
length = len(self)
for p in pos:
if p < 0:
p += length
if not 0 <= p < length:
raise IndexError(f"Bit position {p} out of range.")
self._invert(p)
def ror(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None:
"""Rotate bits to the right in-place.
bits -- The number of bits to rotate by.
start -- Start of slice to rotate. Defaults to 0.
end -- End of slice to rotate. Defaults to len(self).
Raises ValueError if bits < 0.
"""
if not len(self):
raise Error("Cannot rotate an empty bitstring.")
if bits < 0:
raise ValueError("Cannot rotate by negative amount.")
self._ror(bits, start, end)
def _ror_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None:
start, end = self._validate_slice(start, end) # the _slice deals with msb0/lsb0
bits %= (end - start)
if not bits:
return
rhs = self._slice(end - bits, end)
self._delete(bits, end - bits)
self._insert(rhs, start)
def rol(self, bits: int, start: Optional[int] = None, end: Optional[int] = None) -> None:
"""Rotate bits to the left in-place.
bits -- The number of bits to rotate by.
start -- Start of slice to rotate. Defaults to 0.
end -- End of slice to rotate. Defaults to len(self).
Raises ValueError if bits < 0.
"""
if not len(self):
raise Error("Cannot rotate an empty bitstring.")
if bits < 0:
raise ValueError("Cannot rotate by negative amount.")
self._rol(bits, start, end)
def _rol_msb0(self, bits: int, start: Optional[int] = None, end: Optional[int] = None):
start, end = self._validate_slice(start, end)
bits %= (end - start)
if bits == 0:
return
lhs = self._slice(start, start + bits)
self._delete(bits, start)
self._insert(lhs, end - bits)
def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]] = None, start: Optional[int] = None,
end: Optional[int] = None, repeat: bool = True) -> int:
"""Change the endianness in-place. Return number of repeats of fmt done.
fmt -- A compact structure string, an integer number of bytes or
an iterable of integers. Defaults to 0, which byte reverses the
whole bitstring.
start -- Start bit position, defaults to 0.
end -- End bit position, defaults to len(self).
repeat -- If True (the default) the byte swapping pattern is repeated
as much as possible.
"""
start_v, end_v = self._validate_slice(start, end)
if fmt is None or fmt == 0:
# reverse all of the whole bytes.
bytesizes = [(end_v - start_v) // 8]
elif isinstance(fmt, numbers.Integral):
if fmt < 0:
raise ValueError(f"Improper byte length {fmt}.")
bytesizes = [fmt]
elif isinstance(fmt, str):
if not (m := utils.BYTESWAP_STRUCT_PACK_RE.match(fmt)):
raise ValueError(f"Cannot parse format string {fmt}.")
# Split the format string into a list of 'q', '4h' etc.
formatlist = re.findall(utils.STRUCT_SPLIT_RE, m.group('fmt'))
# Now deal with multiplicative factors, 4h -> hhhh etc.
bytesizes = []
for f in formatlist:
if len(f) == 1:
bytesizes.append(utils.PACK_CODE_SIZE[f])
else:
bytesizes.extend([utils.PACK_CODE_SIZE[f[-1]]] * int(f[:-1]))
elif isinstance(fmt, abc.Iterable):
bytesizes = fmt
for bytesize in bytesizes:
if not isinstance(bytesize, numbers.Integral) or bytesize < 0:
raise ValueError(f"Improper byte length {bytesize}.")
else:
raise TypeError("Format must be an integer, string or iterable.")
repeats = 0
totalbitsize: int = 8 * sum(bytesizes)
if not totalbitsize:
return 0
if repeat:
# Try to repeat up to the end of the bitstring.
finalbit = end_v
else:
# Just try one (set of) byteswap(s).
finalbit = start_v + totalbitsize
for patternend in range(start_v + totalbitsize, finalbit + 1, totalbitsize):
bytestart = patternend - totalbitsize
for bytesize in bytesizes:
byteend = bytestart + bytesize * 8
self._reversebytes(bytestart, byteend)
bytestart += bytesize * 8
repeats += 1
return repeats
def clear(self) -> None:
"""Remove all bits, reset to zero length."""
self._clear()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,272 @@
from __future__ import annotations
import bitarray
from bitstring.exceptions import CreationError
from typing import Union, Iterable, Optional, overload, Iterator, Any
def offset_slice_indices_lsb0(key: slice, length: int) -> slice:
# First convert slice to all integers
# Length already should take account of the offset
start, stop, step = key.indices(length)
new_start = length - stop
new_stop = length - start
# For negative step we sometimes get a negative stop, which can't be used correctly in a new slice
return slice(new_start, None if new_stop < 0 else new_stop, step)
def offset_start_stop_lsb0(start: Optional[int], stop: Optional[int], length: int) -> tuple[int, int]:
# First convert slice to all integers
# Length already should take account of the offset
start, stop, _ = slice(start, stop, None).indices(length)
new_start = length - stop
new_stop = length - start
return new_start, new_stop
class BitStore:
"""A light wrapper around bitarray that does the LSB0 stuff"""
__slots__ = ('_bitarray', 'modified_length', 'immutable')
def __init__(self, initializer: Union[int, bitarray.bitarray, str, None] = None,
immutable: bool = False) -> None:
self._bitarray = bitarray.bitarray(initializer)
self.immutable = immutable
self.modified_length = None
@classmethod
def frombytes(cls, b: Union[bytes, bytearray, memoryview], /) -> BitStore:
x = super().__new__(cls)
x._bitarray = bitarray.bitarray()
x._bitarray.frombytes(b)
x.immutable = False
x.modified_length = None
return x
@classmethod
def frombuffer(cls, buffer, /, length: Optional[int] = None) -> BitStore:
x = super().__new__(cls)
x._bitarray = bitarray.bitarray(buffer=buffer)
x.immutable = True
x.modified_length = length
# Here 'modified' means it shouldn't be changed further, so setting, deleting etc. are disallowed.
if x.modified_length is not None:
if x.modified_length < 0:
raise CreationError("Can't create bitstring with a negative length.")
if x.modified_length > len(x._bitarray):
raise CreationError(
f"Can't create bitstring with a length of {x.modified_length} from {len(x._bitarray)} bits of data.")
return x
def setall(self, value: int, /) -> None:
self._bitarray.setall(value)
def tobytes(self) -> bytes:
if self.modified_length is not None:
return self._bitarray[:self.modified_length].tobytes()
return self._bitarray.tobytes()
def slice_to_uint(self, start: Optional[int] = None, end: Optional[int] = None) -> int:
return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=False)
def slice_to_int(self, start: Optional[int] = None, end: Optional[int] = None) -> int:
return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=True)
def slice_to_hex(self, start: Optional[int] = None, end: Optional[int] = None) -> str:
return bitarray.util.ba2hex(self.getslice(start, end)._bitarray)
def slice_to_bin(self, start: Optional[int] = None, end: Optional[int] = None) -> str:
return self.getslice(start, end)._bitarray.to01()
def slice_to_oct(self, start: Optional[int] = None, end: Optional[int] = None) -> str:
return bitarray.util.ba2base(8, self.getslice(start, end)._bitarray)
def __iadd__(self, other: BitStore, /) -> BitStore:
self._bitarray += other._bitarray
return self
def __add__(self, other: BitStore, /) -> BitStore:
bs = self._copy()
bs += other
return bs
def __eq__(self, other: Any, /) -> bool:
return self._bitarray == other._bitarray
def __and__(self, other: BitStore, /) -> BitStore:
return BitStore(self._bitarray & other._bitarray)
def __or__(self, other: BitStore, /) -> BitStore:
return BitStore(self._bitarray | other._bitarray)
def __xor__(self, other: BitStore, /) -> BitStore:
return BitStore(self._bitarray ^ other._bitarray)
def __iand__(self, other: BitStore, /) -> BitStore:
self._bitarray &= other._bitarray
return self
def __ior__(self, other: BitStore, /) -> BitStore:
self._bitarray |= other._bitarray
return self
def __ixor__(self, other: BitStore, /) -> BitStore:
self._bitarray ^= other._bitarray
return self
def find(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> int:
if not bytealigned:
return self._bitarray.find(bs._bitarray, start, end)
try:
return next(self.findall_msb0(bs, start, end, bytealigned))
except StopIteration:
return -1
def rfind(self, bs: BitStore, start: int, end: int, bytealigned: bool = False):
if not bytealigned:
return self._bitarray.find(bs._bitarray, start, end, right=True)
try:
return next(self.rfindall_msb0(bs, start, end, bytealigned))
except StopIteration:
return -1
def findall_msb0(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]:
if bytealigned is True and len(bs) % 8 == 0:
# Special case, looking for whole bytes on whole byte boundaries
bytes_ = bs.tobytes()
# Round up start byte to next byte, and round end byte down.
# We're only looking for whole bytes, so can ignore bits at either end.
start_byte = (start + 7) // 8
end_byte = end // 8
b = self._bitarray[start_byte * 8: end_byte * 8].tobytes()
byte_pos = 0
bytes_to_search = end_byte - start_byte
while byte_pos < bytes_to_search:
byte_pos = b.find(bytes_, byte_pos)
if byte_pos == -1:
break
yield (byte_pos + start_byte) * 8
byte_pos = byte_pos + 1
return
# General case
i = self._bitarray.itersearch(bs._bitarray, start, end)
if not bytealigned:
for p in i:
yield p
else:
for p in i:
if (p % 8) == 0:
yield p
def rfindall_msb0(self, bs: BitStore, start: int, end: int, bytealigned: bool = False) -> Iterator[int]:
i = self._bitarray.itersearch(bs._bitarray, start, end, right=True)
if not bytealigned:
for p in i:
yield p
else:
for p in i:
if (p % 8) == 0:
yield p
def count(self, value, /) -> int:
return self._bitarray.count(value)
def clear(self) -> None:
self._bitarray.clear()
def reverse(self) -> None:
self._bitarray.reverse()
def __iter__(self) -> Iterable[bool]:
for i in range(len(self)):
yield self.getindex(i)
def _copy(self) -> BitStore:
"""Always creates a copy, even if instance is immutable."""
return BitStore(self._bitarray)
def copy(self) -> BitStore:
return self if self.immutable else self._copy()
def __getitem__(self, item: Union[int, slice], /) -> Union[int, BitStore]:
# Use getindex or getslice instead
raise NotImplementedError
def getindex_msb0(self, index: int, /) -> bool:
return bool(self._bitarray.__getitem__(index))
def getslice_withstep_msb0(self, key: slice, /) -> BitStore:
if self.modified_length is not None:
key = slice(*key.indices(self.modified_length))
return BitStore(self._bitarray.__getitem__(key))
def getslice_withstep_lsb0(self, key: slice, /) -> BitStore:
key = offset_slice_indices_lsb0(key, len(self))
return BitStore(self._bitarray.__getitem__(key))
def getslice_msb0(self, start: Optional[int], stop: Optional[int], /) -> BitStore:
if self.modified_length is not None:
key = slice(*slice(start, stop, None).indices(self.modified_length))
start = key.start
stop = key.stop
return BitStore(self._bitarray[start:stop])
def getslice_lsb0(self, start: Optional[int], stop: Optional[int], /) -> BitStore:
start, stop = offset_start_stop_lsb0(start, stop, len(self))
return BitStore(self._bitarray[start:stop])
def getindex_lsb0(self, index: int, /) -> bool:
return bool(self._bitarray.__getitem__(-index - 1))
@overload
def setitem_lsb0(self, key: int, value: int, /) -> None:
...
@overload
def setitem_lsb0(self, key: slice, value: BitStore, /) -> None:
...
def setitem_lsb0(self, key: Union[int, slice], value: Union[int, BitStore], /) -> None:
if isinstance(key, slice):
new_slice = offset_slice_indices_lsb0(key, len(self))
self._bitarray.__setitem__(new_slice, value._bitarray)
else:
self._bitarray.__setitem__(-key - 1, value)
def delitem_lsb0(self, key: Union[int, slice], /) -> None:
if isinstance(key, slice):
new_slice = offset_slice_indices_lsb0(key, len(self))
self._bitarray.__delitem__(new_slice)
else:
self._bitarray.__delitem__(-key - 1)
def invert_msb0(self, index: Optional[int] = None, /) -> None:
if index is not None:
self._bitarray.invert(index)
else:
self._bitarray.invert()
def invert_lsb0(self, index: Optional[int] = None, /) -> None:
if index is not None:
self._bitarray.invert(-index - 1)
else:
self._bitarray.invert()
def any_set(self) -> bool:
return self._bitarray.any()
def all_set(self) -> bool:
return self._bitarray.all()
def __len__(self) -> int:
return self.modified_length if self.modified_length is not None else len(self._bitarray)
def setitem_msb0(self, key, value, /):
if isinstance(value, BitStore):
self._bitarray.__setitem__(key, value._bitarray)
else:
self._bitarray.__setitem__(key, value)
def delitem_msb0(self, key, /):
self._bitarray.__delitem__(key)

View File

@@ -0,0 +1,270 @@
from __future__ import annotations
import struct
import math
import functools
from typing import Union, Optional, Dict, Callable
import bitarray
from bitstring.bitstore import BitStore
import bitstring
from bitstring.fp8 import p4binary_fmt, p3binary_fmt
from bitstring.mxfp import (e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_saturate_fmt,
e5m2mxfp_saturate_fmt, e4m3mxfp_overflow_fmt, e5m2mxfp_overflow_fmt)
# The size of various caches used to improve performance
CACHE_SIZE = 256
def tidy_input_string(s: str) -> str:
"""Return string made lowercase and with all whitespace and underscores removed."""
try:
t = s.split()
except (AttributeError, TypeError):
raise ValueError(f"Expected str object but received a {type(s)} with value {s}.")
return ''.join(t).lower().replace('_', '')
@functools.lru_cache(CACHE_SIZE)
def str_to_bitstore(s: str) -> BitStore:
_, tokens = bitstring.utils.tokenparser(s)
bs = BitStore()
for token in tokens:
bs += bitstore_from_token(*token)
bs.immutable = True
return bs
def bin2bitstore(binstring: str) -> BitStore:
binstring = tidy_input_string(binstring)
binstring = binstring.replace('0b', '')
try:
return BitStore(binstring)
except ValueError:
raise bitstring.CreationError(f"Invalid character in bin initialiser {binstring}.")
def bin2bitstore_unsafe(binstring: str) -> BitStore:
return BitStore(binstring)
def hex2bitstore(hexstring: str) -> BitStore:
hexstring = tidy_input_string(hexstring)
hexstring = hexstring.replace('0x', '')
try:
ba = bitarray.util.hex2ba(hexstring)
except ValueError:
raise bitstring.CreationError("Invalid symbol in hex initialiser.")
return BitStore(ba)
def oct2bitstore(octstring: str) -> BitStore:
octstring = tidy_input_string(octstring)
octstring = octstring.replace('0o', '')
try:
ba = bitarray.util.base2ba(8, octstring)
except ValueError:
raise bitstring.CreationError("Invalid symbol in oct initialiser.")
return BitStore(ba)
def ue2bitstore(i: Union[str, int]) -> BitStore:
i = int(i)
if i < 0:
raise bitstring.CreationError("Cannot use negative initialiser for unsigned exponential-Golomb.")
if i == 0:
return BitStore('1')
tmp = i + 1
leadingzeros = -1
while tmp > 0:
tmp >>= 1
leadingzeros += 1
remainingpart = i + 1 - (1 << leadingzeros)
return BitStore('0' * leadingzeros + '1') + int2bitstore(remainingpart, leadingzeros, False)
def se2bitstore(i: Union[str, int]) -> BitStore:
i = int(i)
if i > 0:
u = (i * 2) - 1
else:
u = -2 * i
return ue2bitstore(u)
def uie2bitstore(i: Union[str, int]) -> BitStore:
i = int(i)
if i < 0:
raise bitstring.CreationError("Cannot use negative initialiser for unsigned interleaved exponential-Golomb.")
return BitStore('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1')
def sie2bitstore(i: Union[str, int]) -> BitStore:
i = int(i)
if i == 0:
return BitStore('1')
else:
return uie2bitstore(abs(i)) + (BitStore('1') if i < 0 else BitStore('0'))
def bfloat2bitstore(f: Union[str, float], big_endian: bool) -> BitStore:
f = float(f)
fmt = '>f' if big_endian else '<f'
try:
b = struct.pack(fmt, f)
except OverflowError:
# For consistency we overflow to 'inf'.
b = struct.pack(fmt, float('inf') if f > 0 else float('-inf'))
return BitStore.frombytes(b[0:2]) if big_endian else BitStore.frombytes(b[2:4])
def p4binary2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
u = p4binary_fmt.float_to_int8(f)
return int2bitstore(u, 8, False)
def p3binary2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
u = p3binary_fmt.float_to_int8(f)
return int2bitstore(u, 8, False)
def e4m3mxfp2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if bitstring.options.mxfp_overflow == 'saturate':
u = e4m3mxfp_saturate_fmt.float_to_int(f)
else:
u = e4m3mxfp_overflow_fmt.float_to_int(f)
return int2bitstore(u, 8, False)
def e5m2mxfp2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if bitstring.options.mxfp_overflow == 'saturate':
u = e5m2mxfp_saturate_fmt.float_to_int(f)
else:
u = e5m2mxfp_overflow_fmt.float_to_int(f)
return int2bitstore(u, 8, False)
def e3m2mxfp2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if math.isnan(f):
raise ValueError("Cannot convert float('nan') to e3m2mxfp format as it has no representation for it.")
u = e3m2mxfp_fmt.float_to_int(f)
return int2bitstore(u, 6, False)
def e2m3mxfp2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if math.isnan(f):
raise ValueError("Cannot convert float('nan') to e2m3mxfp format as it has no representation for it.")
u = e2m3mxfp_fmt.float_to_int(f)
return int2bitstore(u, 6, False)
def e2m1mxfp2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if math.isnan(f):
raise ValueError("Cannot convert float('nan') to e2m1mxfp format as it has no representation for it.")
u = e2m1mxfp_fmt.float_to_int(f)
return int2bitstore(u, 4, False)
e8m0mxfp_allowed_values = [float(2 ** x) for x in range(-127, 128)]
def e8m0mxfp2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if math.isnan(f):
return BitStore('11111111')
try:
i = e8m0mxfp_allowed_values.index(f)
except ValueError:
raise ValueError(f"{f} is not a valid e8m0mxfp value. It must be exactly 2 ** i, for -127 <= i <= 127 or float('nan') as no rounding will be done.")
return int2bitstore(i, 8, False)
def mxint2bitstore(f: Union[str, float]) -> BitStore:
f = float(f)
if math.isnan(f):
raise ValueError("Cannot convert float('nan') to mxint format as it has no representation for it.")
f *= 2 ** 6 # Remove the implicit scaling factor
if f > 127: # 1 + 63/64
return BitStore('01111111')
if f <= -128: # -2
return BitStore('10000000')
# Want to round to nearest, so move by 0.5 away from zero and round down by converting to int
if f >= 0.0:
f += 0.5
i = int(f)
# For ties-round-to-even
if f - i == 0.0 and i % 2:
i -= 1
else:
f -= 0.5
i = int(f)
if f - i == 0.0 and i % 2:
i += 1
return int2bitstore(i, 8, True)
def int2bitstore(i: int, length: int, signed: bool) -> BitStore:
i = int(i)
try:
x = BitStore(bitarray.util.int2ba(i, length=length, endian='big', signed=signed))
except OverflowError as e:
if signed:
if i >= (1 << (length - 1)) or i < -(1 << (length - 1)):
raise bitstring.CreationError(f"{i} is too large a signed integer for a bitstring of length {length}. "
f"The allowed range is [{-(1 << (length - 1))}, {(1 << (length - 1)) - 1}].")
else:
if i >= (1 << length):
raise bitstring.CreationError(f"{i} is too large an unsigned integer for a bitstring of length {length}. "
f"The allowed range is [0, {(1 << length) - 1}].")
if i < 0:
raise bitstring.CreationError("uint cannot be initialised with a negative number.")
raise e
return x
def intle2bitstore(i: int, length: int, signed: bool) -> BitStore:
x = int2bitstore(i, length, signed).tobytes()
return BitStore.frombytes(x[::-1])
def float2bitstore(f: Union[str, float], length: int, big_endian: bool) -> BitStore:
f = float(f)
fmt = {16: '>e', 32: '>f', 64: '>d'}[length] if big_endian else {16: '<e', 32: '<f', 64: '<d'}[length]
try:
b = struct.pack(fmt, f)
except OverflowError:
# If float64 doesn't fit it automatically goes to 'inf'. This reproduces that behaviour for other types.
b = struct.pack(fmt, float('inf') if f > 0 else float('-inf'))
return BitStore.frombytes(b)
literal_bit_funcs: Dict[str, Callable[..., BitStore]] = {
'0x': hex2bitstore,
'0X': hex2bitstore,
'0b': bin2bitstore,
'0B': bin2bitstore,
'0o': oct2bitstore,
'0O': oct2bitstore,
}
def bitstore_from_token(name: str, token_length: Optional[int], value: Optional[str]) -> BitStore:
if name in literal_bit_funcs:
return literal_bit_funcs[name](value)
try:
d = bitstring.dtypes.Dtype(name, token_length)
except ValueError as e:
raise bitstring.CreationError(f"Can't parse token: {e}")
if value is None and name != 'pad':
raise ValueError(f"Token {name} requires a value.")
bs = d.build(value)._bitstore
if token_length is not None and len(bs) != d.bitlength:
raise bitstring.CreationError(f"Token with length {token_length} packed with value of length {len(bs)} "
f"({name}:{token_length}={value}).")
return bs

View File

@@ -0,0 +1,714 @@
from __future__ import annotations
import bitstring
from bitstring.bits import Bits, BitsType
from bitstring.dtypes import Dtype
from typing import Union, List, Any, Optional, overload, TypeVar, Tuple
import copy
import numbers
TConstBitStream = TypeVar("TConstBitStream", bound='ConstBitStream')
class ConstBitStream(Bits):
"""A container or stream holding an immutable sequence of bits.
For a mutable container use the BitStream class instead.
Methods inherited from Bits:
all() -- Check if all specified bits are set to 1 or 0.
any() -- Check if any of specified bits are set to 1 or 0.
copy() -- Return a copy of the bitstring.
count() -- Count the number of bits set to 1 or 0.
cut() -- Create generator of constant sized chunks.
endswith() -- Return whether the bitstring ends with a sub-string.
find() -- Find a sub-bitstring in the current bitstring.
findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
fromstring() -- Create a bitstring from a formatted string.
join() -- Join bitstrings together using current bitstring.
pp() -- Pretty print the bitstring.
rfind() -- Seek backwards to find a sub-bitstring.
split() -- Create generator of chunks split by a delimiter.
startswith() -- Return whether the bitstring starts with a sub-bitstring.
tobitarray() -- Return bitstring as a bitarray from the bitarray package.
tobytes() -- Return bitstring as bytes, padding if needed.
tofile() -- Write bitstring to file, padding if needed.
unpack() -- Interpret bits using format string.
Other methods:
bytealign() -- Align to next byte boundary.
peek() -- Peek at and interpret next bits as a single item.
peeklist() -- Peek at and interpret next bits as a list of items.
read() -- Read and interpret next bits as a single item.
readlist() -- Read and interpret next bits as a list of items.
readto() -- Read up to and including next occurrence of a bitstring.
Special methods:
Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^.
Properties:
[GENERATED_PROPERTY_DESCRIPTIONS]
len -- Length of the bitstring in bits.
pos -- The current bit position in the bitstring.
"""
__slots__ = ('_pos',)
def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None,
offset: Optional[int] = None, pos: int = 0, **kwargs) -> None:
"""Either specify an 'auto' initialiser:
A string of comma separated tokens, an integer, a file object,
a bytearray, a boolean iterable or another bitstring.
Or initialise via **kwargs with one (and only one) of:
bin -- binary string representation, e.g. '0b001010'.
hex -- hexadecimal string representation, e.g. '0x2ef'
oct -- octal string representation, e.g. '0o777'.
bytes -- raw data as a bytes object, for example read from a binary file.
int -- a signed integer.
uint -- an unsigned integer.
float / floatbe -- a big-endian floating point number.
bool -- a boolean (True or False).
se -- a signed exponential-Golomb code.
ue -- an unsigned exponential-Golomb code.
sie -- a signed interleaved exponential-Golomb code.
uie -- an unsigned interleaved exponential-Golomb code.
floatle -- a little-endian floating point number.
floatne -- a native-endian floating point number.
bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number.
bfloatle -- a little-endian bfloat format 16-bit floating point number.
bfloatne -- a native-endian bfloat format 16-bit floating point number.
intbe -- a signed big-endian whole byte integer.
intle -- a signed little-endian whole byte integer.
intne -- a signed native-endian whole byte integer.
uintbe -- an unsigned big-endian whole byte integer.
uintle -- an unsigned little-endian whole byte integer.
uintne -- an unsigned native-endian whole byte integer.
filename -- the path of a file which will be opened in binary read-only mode.
Other keyword arguments:
length -- length of the bitstring in bits, if needed and appropriate.
It must be supplied for all integer and float initialisers.
offset -- bit offset to the data. These offset bits are
ignored and this is mainly intended for use when
initialising using 'bytes' or 'filename'.
pos -- Initial bit position, defaults to 0.
"""
if pos < 0:
pos += len(self._bitstore)
if pos < 0 or pos > len(self._bitstore):
raise bitstring.CreationError(f"Cannot set pos to {pos} when length is {len(self._bitstore)}.")
self._pos = pos
self._bitstore.immutable = True
def _setbytepos(self, bytepos: int) -> None:
"""Move to absolute byte-aligned position in stream."""
self._setbitpos(bytepos * 8)
def _getbytepos(self) -> int:
"""Return the current position in the stream in bytes. Must be byte aligned."""
if self._pos % 8:
raise bitstring.ByteAlignError("Not byte aligned when using bytepos property.")
return self._pos // 8
def _setbitpos(self, pos: int) -> None:
"""Move to absolute position bit in bitstream."""
if pos < 0:
raise ValueError("Bit position cannot be negative.")
if pos > len(self):
raise ValueError("Cannot seek past the end of the data.")
self._pos = pos
def _getbitpos(self) -> int:
"""Return the current position in the stream in bits."""
return self._pos
def _clear(self) -> None:
Bits._clear(self)
self._pos = 0
def __copy__(self: TConstBitStream) -> TConstBitStream:
"""Return a new copy of the ConstBitStream for the copy module."""
# Note that if you want a new copy (different ID), use _copy instead.
# The copy can use the same datastore as it's immutable.
s = self.__class__()
s._bitstore = self._bitstore
# Reset the bit position, don't copy it.
s._pos = 0
return s
def __and__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream:
"""Bit-wise 'and' between two bitstrings. Returns new bitstring.
bs -- The bitstring to '&' with.
Raises ValueError if the two bitstrings have differing lengths.
"""
s = Bits.__and__(self, bs)
s._pos = 0
return s
def __or__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream:
"""Bit-wise 'or' between two bitstrings. Returns new bitstring.
bs -- The bitstring to '|' with.
Raises ValueError if the two bitstrings have differing lengths.
"""
s = Bits.__or__(self, bs)
s._pos = 0
return s
def __xor__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream:
"""Bit-wise 'xor' between two bitstrings. Returns new bitstring.
bs -- The bitstring to '^' with.
Raises ValueError if the two bitstrings have differing lengths.
"""
s = Bits.__xor__(self, bs)
s._pos = 0
return s
def __add__(self: TConstBitStream, bs: BitsType, /) -> TConstBitStream:
"""Concatenate bitstrings and return new bitstring.
bs -- the bitstring to append.
"""
s = Bits.__add__(self, bs)
s._pos = 0
return s
def append(self, bs: BitsType, /) -> None:
"""Append a bitstring to the current bitstring.
bs -- The bitstring to append.
The current bit position will be moved to the end of the BitStream.
"""
self._append(bs)
self._pos = len(self)
def __repr__(self) -> str:
"""Return representation that could be used to recreate the bitstring.
If the returned string is too long it will be truncated. See __str__().
"""
return self._repr(self.__class__.__name__, len(self), self._pos)
def overwrite(self, bs: BitsType, /, pos: Optional[int] = None) -> None:
"""Overwrite with bitstring at bit position pos.
bs -- The bitstring to overwrite with.
pos -- The bit position to begin overwriting from.
The current bit position will be moved to the end of the overwritten section.
Raises ValueError if pos < 0 or pos > len(self).
"""
bs = Bits._create_from_bitstype(bs)
if len(bs) == 0:
return
if pos is None:
pos = self._pos
if pos < 0:
pos += len(self)
if pos < 0 or pos > len(self):
raise ValueError("Overwrite starts outside boundary of bitstring.")
self._overwrite(bs, pos)
self._pos = pos + len(bs)
def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None,
bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]:
"""Find first occurrence of substring bs.
Returns a single item tuple with the bit position if found, or an
empty tuple if not found. The bit position (pos property) will
also be set to the start of the substring if it is found.
bs -- The bitstring to find.
start -- The bit position to start the search. Defaults to 0.
end -- The bit position one past the last bit to search.
Defaults to len(self).
bytealigned -- If True the bitstring will only be
found on byte boundaries.
Raises ValueError if bs is empty, if start < 0, if end > len(self) or
if end < start.
>>> BitStream('0xc3e').find('0b1111')
(6,)
"""
p = super().find(bs, start, end, bytealigned)
if p:
self._pos = p[0]
return p
def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None,
bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]:
"""Find final occurrence of substring bs.
Returns a single item tuple with the bit position if found, or an
empty tuple if not found. The bit position (pos property) will
also be set to the start of the substring if it is found.
bs -- The bitstring to find.
start -- The bit position to end the reverse search. Defaults to 0.
end -- The bit position one past the first bit to reverse search.
Defaults to len(self).
bytealigned -- If True the bitstring will only be found on byte
boundaries.
Raises ValueError if bs is empty, if start < 0, if end > len(self) or
if end < start.
"""
p = super().rfind(bs, start, end, bytealigned)
if p:
self._pos = p[0]
return p
@overload
def read(self, fmt: int) -> Bits:
...
@overload
def read(self, fmt: str) -> Any:
...
def read(self, fmt: Union[int, str, Dtype]) -> Union[int, float, str, Bits, bool, bytes, None]:
"""Interpret next bits according to the format string and return result.
fmt -- Token string describing how to interpret the next bits.
Token examples: 'int:12' : 12 bits as a signed integer
'uint:8' : 8 bits as an unsigned integer
'float:64' : 8 bytes as a big-endian float
'intbe:16' : 2 bytes as a big-endian signed integer
'uintbe:16' : 2 bytes as a big-endian unsigned integer
'intle:32' : 4 bytes as a little-endian signed integer
'uintle:32' : 4 bytes as a little-endian unsigned integer
'floatle:64': 8 bytes as a little-endian float
'intne:24' : 3 bytes as a native-endian signed integer
'uintne:24' : 3 bytes as a native-endian unsigned integer
'floatne:32': 4 bytes as a native-endian float
'hex:80' : 80 bits as a hex string
'oct:9' : 9 bits as an octal string
'bin:1' : single bit binary string
'ue' : next bits as unsigned exp-Golomb code
'se' : next bits as signed exp-Golomb code
'uie' : next bits as unsigned interleaved exp-Golomb code
'sie' : next bits as signed interleaved exp-Golomb code
'bits:5' : 5 bits as a bitstring
'bytes:10' : 10 bytes as a bytes object
'bool' : 1 bit as a bool
'pad:3' : 3 bits of padding to ignore - returns None
fmt may also be an integer, which will be treated like the 'bits' token.
The position in the bitstring is advanced to after the read items.
Raises ReadError if not enough bits are available.
Raises ValueError if the format is not understood.
"""
p = self._pos
if isinstance(fmt, numbers.Integral):
if fmt < 0:
raise ValueError("Cannot read negative amount.")
if fmt > len(self) - self._pos:
raise bitstring.ReadError(f"Cannot read {fmt} bits, only {len(self) - self._pos} available.")
bs = self._slice(self._pos, self._pos + fmt)
self._pos += fmt
return bs
dtype = bitstring.dtypes.Dtype(fmt)
if dtype.bitlength is None and not dtype.variable_length:
# No length specified? Try again, but read to end.
bitlength = len(self) - self._pos
items, remainder = divmod(bitlength, dtype.bits_per_item)
if remainder != 0:
raise ValueError(
f"The '{dtype.name}' type must have a bit length that is a multiple of {dtype.bits_per_item}"
f" so cannot be read from the {bitlength} bits that are available.")
dtype = bitstring.dtypes.Dtype(fmt, items)
if dtype.bitlength is not None:
val = dtype.read_fn(self, self._pos)
self._pos += dtype.bitlength
else:
val, self._pos = dtype.read_fn(self, self._pos)
if self._pos > len(self):
self._pos = p
raise bitstring.ReadError(f"Reading off end of bitstring with fmt '{fmt}'. Only {len(self) - p} bits available.")
return val
def readlist(self, fmt: Union[str, List[Union[int, str, Dtype]]], **kwargs) \
-> List[Union[int, float, str, Bits, bool, bytes, None]]:
"""Interpret next bits according to format string(s) and return list.
fmt -- A single string or list of strings with comma separated tokens
describing how to interpret the next bits in the bitstring. Items
can also be integers, for reading new bitstring of the given length.
kwargs -- A dictionary or keyword-value pairs - the keywords used in the
format string will be replaced with their given value.
The position in the bitstring is advanced to after the read items.
Raises ReadError is not enough bits are available.
Raises ValueError if the format is not understood.
See the docstring for 'read' for token examples. 'pad' tokens are skipped
and not added to the returned list.
>>> h, b1, b2 = s.readlist('hex:20, bin:5, bin:3')
>>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10])
"""
value, self._pos = self._readlist(fmt, self._pos, **kwargs)
return value
def readto(self: TConstBitStream, bs: BitsType, /, bytealigned: Optional[bool] = None) -> TConstBitStream:
"""Read up to and including next occurrence of bs and return result.
bs -- The bitstring to find.
bytealigned -- If True the bitstring will only be
found on byte boundaries.
Raises ValueError if bs is empty.
Raises ReadError if bs is not found.
"""
if isinstance(bs, numbers.Integral):
raise ValueError("Integers cannot be searched for")
bs = Bits._create_from_bitstype(bs)
oldpos = self._pos
p = self.find(bs, self._pos, bytealigned=bytealigned)
if not p:
raise bitstring.ReadError("Substring not found")
self._pos += len(bs)
return self._slice(oldpos, self._pos)
@overload
def peek(self: TConstBitStream, fmt: int) -> TConstBitStream:
...
@overload
def peek(self, fmt: str) -> Union[int, float, str, TConstBitStream, bool, bytes, None]:
...
def peek(self: TConstBitStream, fmt: Union[int, str]) -> Union[int, float, str, TConstBitStream, bool, bytes, None]:
"""Interpret next bits according to format string and return result.
fmt -- Token string describing how to interpret the next bits.
The position in the bitstring is not changed. If not enough bits are
available then all bits to the end of the bitstring will be used.
Raises ReadError if not enough bits are available.
Raises ValueError if the format is not understood.
See the docstring for 'read' for token examples.
"""
pos_before = self._pos
value = self.read(fmt)
self._pos = pos_before
return value
def peeklist(self, fmt: Union[str, List[Union[int, str]]], **kwargs) \
-> List[Union[int, float, str, Bits, None]]:
"""Interpret next bits according to format string(s) and return list.
fmt -- One or more integers or strings with comma separated tokens describing
how to interpret the next bits in the bitstring.
kwargs -- A dictionary or keyword-value pairs - the keywords used in the
format string will be replaced with their given value.
The position in the bitstring is not changed. If not enough bits are
available then all bits to the end of the bitstring will be used.
Raises ReadError if not enough bits are available.
Raises ValueError if the format is not understood.
See the docstring for 'read' for token examples.
"""
pos = self._pos
return_values = self.readlist(fmt, **kwargs)
self._pos = pos
return return_values
def bytealign(self) -> int:
"""Align to next byte and return number of skipped bits.
Raises ValueError if the end of the bitstring is reached before
aligning to the next byte.
"""
skipped = (8 - (self._pos % 8)) % 8
self.pos += skipped
return skipped
@classmethod
def fromstring(cls: TBits, s: str, /) -> TBits:
x = super().fromstring(s)
x._pos = 0
x._bitstore.immutable = True
return x
@overload
def __getitem__(self: TBits, key: slice, /) -> TBits:
...
@overload
def __getitem__(self: TBits, key: int, /) -> bool:
...
def __getitem__(self: TBits, key: Union[slice, int], /) -> Union[TBits, bool]:
"""Return a new bitstring representing a slice of the current bitstring."""
if isinstance(key, numbers.Integral):
return bool(self._bitstore.getindex(key))
bs = super().__new__(self.__class__)
bs._bitstore = self._bitstore.getslice_withstep(key)
bs._pos = 0
return bs
pos = property(_getbitpos, _setbitpos,
doc="""The position in the bitstring in bits. Read and write.
""")
bitpos = property(_getbitpos, _setbitpos,
doc="""The position in the bitstring in bits. Read and write.
""")
bytepos = property(_getbytepos, _setbytepos,
doc="""The position in the bitstring in bytes. Read and write.
""")
class BitStream(ConstBitStream, bitstring.BitArray):
"""A container or stream holding a mutable sequence of bits
Subclass of the ConstBitStream and BitArray classes. Inherits all of
their methods.
Methods:
all() -- Check if all specified bits are set to 1 or 0.
any() -- Check if any of specified bits are set to 1 or 0.
append() -- Append a bitstring.
bytealign() -- Align to next byte boundary.
byteswap() -- Change byte endianness in-place.
clear() -- Remove all bits from the bitstring.
copy() -- Return a copy of the bitstring.
count() -- Count the number of bits set to 1 or 0.
cut() -- Create generator of constant sized chunks.
endswith() -- Return whether the bitstring ends with a sub-string.
find() -- Find a sub-bitstring in the current bitstring.
findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
fromstring() -- Create a bitstring from a formatted string.
insert() -- Insert a bitstring.
invert() -- Flip bit(s) between one and zero.
join() -- Join bitstrings together using current bitstring.
overwrite() -- Overwrite a section with a new bitstring.
peek() -- Peek at and interpret next bits as a single item.
peeklist() -- Peek at and interpret next bits as a list of items.
pp() -- Pretty print the bitstring.
prepend() -- Prepend a bitstring.
read() -- Read and interpret next bits as a single item.
readlist() -- Read and interpret next bits as a list of items.
readto() -- Read up to and including next occurrence of a bitstring.
replace() -- Replace occurrences of one bitstring with another.
reverse() -- Reverse bits in-place.
rfind() -- Seek backwards to find a sub-bitstring.
rol() -- Rotate bits to the left.
ror() -- Rotate bits to the right.
set() -- Set bit(s) to 1 or 0.
split() -- Create generator of chunks split by a delimiter.
startswith() -- Return whether the bitstring starts with a sub-bitstring.
tobitarray() -- Return bitstring as a bitarray from the bitarray package.
tobytes() -- Return bitstring as bytes, padding if needed.
tofile() -- Write bitstring to file, padding if needed.
unpack() -- Interpret bits using format string.
Special methods:
Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^=
in addition to [], ==, !=, +, *, ~, <<, >>, &, | and ^.
Properties:
[GENERATED_PROPERTY_DESCRIPTIONS]
len -- Length of the bitstring in bits.
pos -- The current bit position in the bitstring.
"""
__slots__ = ()
def __init__(self, auto: Optional[Union[BitsType, int]] = None, /, length: Optional[int] = None,
offset: Optional[int] = None, pos: int = 0, **kwargs) -> None:
"""Either specify an 'auto' initialiser:
A string of comma separated tokens, an integer, a file object,
a bytearray, a boolean iterable or another bitstring.
Or initialise via **kwargs with one (and only one) of:
bin -- binary string representation, e.g. '0b001010'.
hex -- hexadecimal string representation, e.g. '0x2ef'
oct -- octal string representation, e.g. '0o777'.
bytes -- raw data as a bytes object, for example read from a binary file.
int -- a signed integer.
uint -- an unsigned integer.
float / floatbe -- a big-endian floating point number.
bool -- a boolean (True or False).
se -- a signed exponential-Golomb code.
ue -- an unsigned exponential-Golomb code.
sie -- a signed interleaved exponential-Golomb code.
uie -- an unsigned interleaved exponential-Golomb code.
floatle -- a little-endian floating point number.
floatne -- a native-endian floating point number.
bfloat / bfloatbe - a big-endian bfloat format 16-bit floating point number.
bfloatle -- a little-endian bfloat format 16-bit floating point number.
bfloatne -- a native-endian bfloat format 16-bit floating point number.
intbe -- a signed big-endian whole byte integer.
intle -- a signed little-endian whole byte integer.
intne -- a signed native-endian whole byte integer.
uintbe -- an unsigned big-endian whole byte integer.
uintle -- an unsigned little-endian whole byte integer.
uintne -- an unsigned native-endian whole byte integer.
filename -- the path of a file which will be opened in binary read-only mode.
Other keyword arguments:
length -- length of the bitstring in bits, if needed and appropriate.
It must be supplied for all integer and float initialisers.
offset -- bit offset to the data. These offset bits are
ignored and this is intended for use when
initialising using 'bytes' or 'filename'.
pos -- Initial bit position, defaults to 0.
"""
ConstBitStream.__init__(self, auto, length, offset, pos, **kwargs)
if self._bitstore.immutable:
self._bitstore = self._bitstore._copy()
self._bitstore.immutable = False
def __copy__(self) -> BitStream:
"""Return a new copy of the BitStream."""
s_copy = object.__new__(BitStream)
s_copy._pos = 0
s_copy._bitstore = self._bitstore.copy()
return s_copy
def __iadd__(self, bs: BitsType, /) -> BitStream:
"""Append to current bitstring. Return self.
bs -- the bitstring to append.
The current bit position will be moved to the end of the BitStream.
"""
self._append(bs)
self._pos = len(self)
return self
def prepend(self, bs: BitsType, /) -> None:
"""Prepend a bitstring to the current bitstring.
bs -- The bitstring to prepend.
"""
bs = Bits._create_from_bitstype(bs)
super().prepend(bs)
self._pos = 0
def __setitem__(self, /, key: Union[slice, int], value: BitsType) -> None:
length_before = len(self)
super().__setitem__(key, value)
if len(self) != length_before:
self._pos = 0
return
def __delitem__(self, /, key: Union[slice, int]) -> None:
"""Delete item or range.
>>> a = BitStream('0x001122')
>>> del a[8:16]
>>> print a
0x0022
"""
length_before = len(self)
self._bitstore.__delitem__(key)
if len(self) != length_before:
self._pos = 0
def insert(self, bs: BitsType, /, pos: Optional[int] = None) -> None:
"""Insert bitstring at bit position pos.
bs -- The bitstring to insert.
pos -- The bit position to insert at.
The current bit position will be moved to the end of the inserted section.
Raises ValueError if pos < 0 or pos > len(self).
"""
bs = Bits._create_from_bitstype(bs)
if len(bs) == 0:
return
if bs is self:
bs = self._copy()
if pos is None:
pos = self._pos
if pos < 0:
pos += len(self)
if not 0 <= pos <= len(self):
raise ValueError("Invalid insert position.")
self._insert(bs, pos)
self._pos = pos + len(bs)
def replace(self, old: BitsType, new: BitsType, start: Optional[int] = None, end: Optional[int] = None,
count: Optional[int] = None, bytealigned: Optional[bool] = None) -> int:
"""Replace all occurrences of old with new in place.
Returns number of replacements made.
old -- The bitstring to replace.
new -- The replacement bitstring.
start -- Any occurrences that start before this will not be replaced.
Defaults to 0.
end -- Any occurrences that finish after this will not be replaced.
Defaults to len(self).
count -- The maximum number of replacements to make. Defaults to
replace all occurrences.
bytealigned -- If True replacements will only be made on byte
boundaries.
Raises ValueError if old is empty or if start or end are
out of range.
"""
if count == 0:
return 0
if len(old := Bits._create_from_bitstype(old)) == 0:
raise ValueError("Empty bitstring cannot be replaced.")
start, end = self._validate_slice(start, end)
new = Bits._create_from_bitstype(new)
if new is self:
# Prevent self assignment woes
new = copy.copy(self)
length_before = len(self)
replacement_count = self._replace(old, new, start, end, 0 if count is None else count, bytealigned)
if len(self) != length_before:
self._pos = 0
return replacement_count

View File

@@ -0,0 +1,95 @@
from __future__ import annotations
import bitstring
import os
class Options:
"""Internal class to create singleton module options instance."""
_instance = None
def __init__(self):
self.set_lsb0(False)
self._bytealigned = False
self.mxfp_overflow = 'saturate'
self.no_color = False
no_color = os.getenv('NO_COLOR')
self.no_color = True if no_color else False
@property
def mxfp_overflow(self) -> str:
return self._mxfp_overflow
@mxfp_overflow.setter
def mxfp_overflow(self, value: str) -> None:
allowed_values = ('saturate', 'overflow')
if value not in allowed_values:
raise ValueError(f"mxfp_overflow must be one of {allowed_values}, not {value}.")
self._mxfp_overflow = value
def __repr__(self) -> str:
attributes = {attr: getattr(self, attr) for attr in dir(self) if not attr.startswith('_') and not callable(getattr(self, attr))}
return '\n'.join(f"{attr}: {value!r}" for attr, value in attributes.items())
@property
def lsb0(self) -> bool:
return self._lsb0
@lsb0.setter
def lsb0(self, value: bool) -> None:
self.set_lsb0(value)
def set_lsb0(self, value: bool) -> None:
self._lsb0 = bool(value)
Bits = bitstring.bits.Bits
BitArray = bitstring.bitarray_.BitArray
BitStore = bitstring.bitstore.BitStore
lsb0_methods = {
Bits: {'_find': Bits._find_lsb0, '_rfind': Bits._rfind_lsb0, '_findall': Bits._findall_lsb0},
BitArray: {'_ror': BitArray._rol_msb0, '_rol': BitArray._ror_msb0, '_append': BitArray._append_lsb0,
'_prepend': BitArray._append_msb0},
BitStore: {'__setitem__': BitStore.setitem_lsb0, '__delitem__': BitStore.delitem_lsb0,
'getindex': BitStore.getindex_lsb0, 'getslice': BitStore.getslice_lsb0,
'getslice_withstep': BitStore.getslice_withstep_lsb0, 'invert': BitStore.invert_lsb0}
}
msb0_methods = {
Bits: {'_find': Bits._find_msb0, '_rfind': Bits._rfind_msb0, '_findall': Bits._findall_msb0},
BitArray: {'_ror': BitArray._ror_msb0, '_rol': BitArray._rol_msb0, '_append': BitArray._append_msb0,
'_prepend': BitArray._append_lsb0},
BitStore: {'__setitem__': BitStore.setitem_msb0, '__delitem__': BitStore.delitem_msb0,
'getindex': BitStore.getindex_msb0, 'getslice': BitStore.getslice_msb0,
'getslice_withstep': BitStore.getslice_withstep_msb0, 'invert': BitStore.invert_msb0}
}
methods = lsb0_methods if self._lsb0 else msb0_methods
for cls, method_dict in methods.items():
for attr, method in method_dict.items():
setattr(cls, attr, method)
@property
def bytealigned(self) -> bool:
return self._bytealigned
@bytealigned.setter
def bytealigned(self, value: bool) -> None:
self._bytealigned = bool(value)
def __new__(cls):
if cls._instance is None:
cls._instance = super(Options, cls).__new__(cls)
return cls._instance
class Colour:
def __new__(cls, use_colour: bool) -> Colour:
x = super().__new__(cls)
if use_colour:
cls.blue = '\033[34m'
cls.purple = '\033[35m'
cls.green = '\033[32m'
cls.off = '\033[0m'
else:
cls.blue = cls.purple = cls.green = cls.off = ''
return x

View File

@@ -0,0 +1,403 @@
from __future__ import annotations
import functools
from typing import Optional, Dict, Any, Union, Tuple, Callable
import inspect
import bitstring
from bitstring import utils
CACHE_SIZE = 256
def scaled_get_fn(get_fn, s: Union[int, float]):
def wrapper(*args, scale=s, **kwargs):
return get_fn(*args, **kwargs) * scale
return wrapper
def scaled_set_fn(set_fn, s: Union[int, float]):
def wrapper(bs, value, *args, scale=s, **kwargs):
return set_fn(bs, value / scale, *args, **kwargs)
return wrapper
def scaled_read_fn(read_fn, s: Union[int, float]):
def wrapper(*args, scale=s, **kwargs):
val = read_fn(*args, **kwargs)
if isinstance(val, tuple):
val, pos = val
return val * scale, pos
return val * scale
return wrapper
class Dtype:
"""A data type class, representing a concrete interpretation of binary data.
Dtype instances are immutable. They are often created implicitly elsewhere via a token string.
>>> u12 = Dtype('uint', 12) # length separate from token string.
>>> float16 = Dtype('float16') # length part of token string.
>>> mxfp = Dtype('e3m2mxfp', scale=2 ** 6) # dtype with scaling factor
"""
_name: str
_read_fn: Callable
_set_fn: Callable
_get_fn: Callable
_return_type: Any
_is_signed: bool
_set_fn_needs_length: bool
_variable_length: bool
_bitlength: Optional[int]
_bits_per_item: int
_length: Optional[int]
_scale: Union[None, float, int]
def __new__(cls, token: Union[str, Dtype], /, length: Optional[int] = None, scale: Union[None, float, int] = None) -> Dtype:
if isinstance(token, cls):
return token
if length is None:
x = cls._new_from_token(token, scale)
return x
else:
x = dtype_register.get_dtype(token, length, scale)
return x
@property
def scale(self) -> Union[int, float, None]:
"""The multiplicative scale applied when interpreting the data."""
return self._scale
@property
def name(self) -> str:
"""A string giving the name of the data type."""
return self._name
@property
def length(self) -> int:
"""The length of the data type in units of bits_per_item. Set to None for variable length dtypes."""
return self._length
@property
def bitlength(self) -> Optional[int]:
"""The number of bits needed to represent a single instance of the data type. Set to None for variable length dtypes."""
return self._bitlength
@property
def bits_per_item(self) -> int:
"""The number of bits for each unit of length. Usually 1, but equals 8 for bytes type."""
return self._bits_per_item
@property
def variable_length(self) -> bool:
"""If True then the length of the data type depends on the data being interpreted, and must not be specified."""
return self._variable_length
@property
def return_type(self) -> Any:
"""The type of the value returned by the parse method, such as int, float or str."""
return self._return_type
@property
def is_signed(self) -> bool:
"""If True then the data type represents a signed quantity."""
return self._is_signed
@property
def set_fn(self) -> Optional[Callable]:
"""A function to set the value of the data type."""
return self._set_fn
@property
def get_fn(self) -> Callable:
"""A function to get the value of the data type."""
return self._get_fn
@property
def read_fn(self) -> Callable:
"""A function to read the value of the data type."""
return self._read_fn
def _set_scale(self, value: Union[None, float, int]) -> None:
self._scale = value
if self._scale is None:
return
if self._scale == 0:
raise ValueError("A Dtype's scale factor must not be zero.")
if not hasattr(self, 'unscaled_get_fn'):
self.unscaled_get_fn = self._get_fn
self.unscaled_set_fn = self._set_fn
self.unscaled_read_fn = self._read_fn
self._get_fn = scaled_get_fn(self.unscaled_get_fn, self._scale)
self._set_fn = scaled_set_fn(self.unscaled_set_fn, self._scale)
self._read_fn = scaled_read_fn(self.unscaled_read_fn, self._scale)
@classmethod
@functools.lru_cache(CACHE_SIZE)
def _new_from_token(cls, token: str, scale: Union[None, float, int] = None) -> Dtype:
token = ''.join(token.split())
return dtype_register.get_dtype(*utils.parse_name_length_token(token), scale=scale)
def __hash__(self) -> int:
return hash((self._name, self._length))
@classmethod
@functools.lru_cache(CACHE_SIZE)
def _create(cls, definition: DtypeDefinition, length: Optional[int], scale: Union[None, float, int]) -> Dtype:
x = super().__new__(cls)
x._name = definition.name
x._bitlength = x._length = length
x._bits_per_item = definition.multiplier
if x._bitlength is not None:
x._bitlength *= x._bits_per_item
x._set_fn_needs_length = definition.set_fn_needs_length
x._variable_length = definition.variable_length
if x._variable_length or dtype_register.names[x._name].allowed_lengths.only_one_value():
x._read_fn = definition.read_fn
else:
x._read_fn = functools.partial(definition.read_fn, length=x._bitlength)
if definition.set_fn is None:
x._set_fn = None
else:
if x._set_fn_needs_length:
x._set_fn = functools.partial(definition.set_fn, length=x._bitlength)
else:
x._set_fn = definition.set_fn
x._get_fn = definition.get_fn
x._return_type = definition.return_type
x._is_signed = definition.is_signed
x._set_scale(scale)
return x
def build(self, value: Any, /) -> bitstring.Bits:
"""Create a bitstring from a value.
The value parameter should be of a type appropriate to the dtype.
"""
b = bitstring.Bits()
self._set_fn(b, value)
if self.bitlength is not None and len(b) != self.bitlength:
raise ValueError(f"Dtype has a length of {self.bitlength} bits, but value '{value}' has {len(b)} bits.")
return b
def parse(self, b: BitsType, /) -> Any:
"""Parse a bitstring to find its value.
The b parameter should be a bitstring of the appropriate length, or an object that can be converted to a bitstring."""
b = bitstring.Bits._create_from_bitstype(b)
return self._get_fn(bitstring.Bits(b))
def __str__(self) -> str:
if self._scale is not None:
return self.__repr__()
hide_length = self._variable_length or dtype_register.names[self._name].allowed_lengths.only_one_value() or self._length is None
length_str = '' if hide_length else str(self._length)
return f"{self._name}{length_str}"
def __repr__(self) -> str:
hide_length = self._variable_length or dtype_register.names[self._name].allowed_lengths.only_one_value() or self._length is None
length_str = '' if hide_length else ', ' + str(self._length)
if self._scale is None:
scale_str = ''
else:
try:
# This will only succeed for powers of two from -127 to 127.
e8m0 = bitstring.Bits(e8m0mxfp=self._scale)
except ValueError:
scale_str = f', scale={self._scale}'
else:
power_of_two = e8m0.uint - 127
if power_of_two in [0, 1]:
scale_str = f', scale={self._scale}'
else:
scale_str = f', scale=2 ** {power_of_two}'
return f"{self.__class__.__name__}('{self._name}'{length_str}{scale_str})"
def __eq__(self, other: Any) -> bool:
if isinstance(other, Dtype):
return self._name == other._name and self._length == other._length
return False
class AllowedLengths:
def __init__(self, value: Tuple[int, ...] = tuple()) -> None:
if len(value) >= 3 and value[-1] is Ellipsis:
step = value[1] - value[0]
for i in range(1, len(value) - 1):
if value[i] - value[i - 1] != step:
raise ValueError(f"Allowed length tuples must be equally spaced when final element is Ellipsis, but got {value}.")
self.values = (value[0], value[1], Ellipsis)
else:
self.values = value
def __str__(self) -> str:
if self.values and self.values[-1] is Ellipsis:
return f"({self.values[0]}, {self.values[1]}, ...)"
return str(self.values)
def __contains__(self, other: Any) -> bool:
if not self.values:
return True
if self.values[-1] is Ellipsis:
return (other - self.values[0]) % (self.values[1] - self.values[0]) == 0
return other in self.values
def only_one_value(self) -> bool:
return self.values and len(self.values) == 1
class DtypeDefinition:
"""Represents a class of dtypes, such as uint or float, rather than a concrete dtype such as uint8.
Not (yet) part of the public interface."""
def __init__(self, name: str, set_fn, get_fn, return_type: Any = Any, is_signed: bool = False, bitlength2chars_fn=None,
variable_length: bool = False, allowed_lengths: Tuple[int, ...] = tuple(), multiplier: int = 1, description: str = ''):
# Consistency checks
if int(multiplier) != multiplier or multiplier <= 0:
raise ValueError("multiplier must be an positive integer")
if variable_length and allowed_lengths:
raise ValueError("A variable length dtype can't have allowed lengths.")
if variable_length and set_fn is not None and 'length' in inspect.signature(set_fn).parameters:
raise ValueError("A variable length dtype can't have a set_fn which takes a length.")
self.name = name
self.description = description
self.return_type = return_type
self.is_signed = is_signed
self.variable_length = variable_length
self.allowed_lengths = AllowedLengths(allowed_lengths)
self.multiplier = multiplier
# Can work out if set_fn needs length based on its signature.
self.set_fn_needs_length = set_fn is not None and 'length' in inspect.signature(set_fn).parameters
self.set_fn = set_fn
if self.allowed_lengths.values:
def allowed_length_checked_get_fn(bs):
if len(bs) not in self.allowed_lengths:
if self.allowed_lengths.only_one_value():
raise bitstring.InterpretError(f"'{self.name}' dtypes must have a length of {self.allowed_lengths.values[0]}, but received a length of {len(bs)}.")
else:
raise bitstring.InterpretError(f"'{self.name}' dtypes must have a length in {self.allowed_lengths}, but received a length of {len(bs)}.")
return get_fn(bs)
self.get_fn = allowed_length_checked_get_fn # Interpret everything and check the length
else:
self.get_fn = get_fn # Interpret everything
# Create a reading function from the get_fn.
if not self.variable_length:
if self.allowed_lengths.only_one_value():
def read_fn(bs, start):
return self.get_fn(bs[start:start + self.allowed_lengths.values[0]])
else:
def read_fn(bs, start, length):
if len(bs) < start + length:
raise bitstring.ReadError(f"Needed a length of at least {length} bits, but only {len(bs) - start} bits were available.")
return self.get_fn(bs[start:start + length])
self.read_fn = read_fn
else:
# We only find out the length when we read/get.
def length_checked_get_fn(bs):
x, length = get_fn(bs)
if length != len(bs):
raise ValueError
return x
self.get_fn = length_checked_get_fn
def read_fn(bs, start):
try:
x, length = get_fn(bs[start:])
except bitstring.InterpretError:
raise bitstring.ReadError
return x, start + length
self.read_fn = read_fn
self.bitlength2chars_fn = bitlength2chars_fn
def get_dtype(self, length: Optional[int] = None, scale: Union[None, float, int] = None) -> Dtype:
if self.allowed_lengths:
if length is None:
if self.allowed_lengths.only_one_value():
length = self.allowed_lengths.values[0]
else:
if length not in self.allowed_lengths:
if self.allowed_lengths.only_one_value():
raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype, but its only allowed length is {self.allowed_lengths.values[0]}.")
else:
raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype which is not one of its possible lengths (must be one of {self.allowed_lengths}).")
if length is None:
d = Dtype._create(self, None, scale)
return d
if self.variable_length:
raise ValueError(f"A length ({length}) shouldn't be supplied for the variable length dtype '{self.name}'.")
d = Dtype._create(self, length, scale)
return d
def __repr__(self) -> str:
s = f"{self.__class__.__name__}(name='{self.name}', description='{self.description}', return_type={self.return_type.__name__}, "
s += f"is_signed={self.is_signed}, set_fn_needs_length={self.set_fn_needs_length}, allowed_lengths={self.allowed_lengths!s}, multiplier={self.multiplier})"
return s
class Register:
"""A singleton class that holds all the DtypeDefinitions. Not (yet) part of the public interface."""
_instance: Optional[Register] = None
names: Dict[str, DtypeDefinition] = {}
def __new__(cls) -> Register:
# Singleton. Only one Register instance can ever exist.
if cls._instance is None:
cls._instance = super(Register, cls).__new__(cls)
return cls._instance
@classmethod
def add_dtype(cls, definition: DtypeDefinition):
cls.names[definition.name] = definition
if definition.get_fn is not None:
setattr(bitstring.bits.Bits, definition.name, property(fget=definition.get_fn, doc=f"The bitstring as {definition.description}. Read only."))
if definition.set_fn is not None:
setattr(bitstring.bitarray_.BitArray, definition.name, property(fget=definition.get_fn, fset=definition.set_fn, doc=f"The bitstring as {definition.description}. Read and write."))
@classmethod
def add_dtype_alias(cls, name: str, alias: str):
cls.names[alias] = cls.names[name]
definition = cls.names[alias]
if definition.get_fn is not None:
setattr(bitstring.bits.Bits, alias, property(fget=definition.get_fn, doc=f"An alias for '{name}'. Read only."))
if definition.set_fn is not None:
setattr(bitstring.bitarray_.BitArray, alias, property(fget=definition.get_fn, fset=definition.set_fn, doc=f"An alias for '{name}'. Read and write."))
@classmethod
def get_dtype(cls, name: str, length: Optional[int], scale: Union[None, float, int] = None) -> Dtype:
try:
definition = cls.names[name]
except KeyError:
raise ValueError(f"Unknown Dtype name '{name}'. Names available: {list(cls.names.keys())}.")
else:
return definition.get_dtype(length, scale)
@classmethod
def __getitem__(cls, name: str) -> DtypeDefinition:
return cls.names[name]
@classmethod
def __delitem__(cls, name: str) -> None:
del cls.names[name]
def __repr__(self) -> str:
s = [f"{'key':<12}:{'name':^12}{'signed':^8}{'set_fn_needs_length':^23}{'allowed_lengths':^16}{'multiplier':^12}{'return_type':<13}"]
s.append('-' * 85)
for key in self.names:
m = self.names[key]
allowed = '' if not m.allowed_lengths else m.allowed_lengths
ret = 'None' if m.return_type is None else m.return_type.__name__
s.append(f"{key:<12}:{m.name:>12}{m.is_signed:^8}{m.set_fn_needs_length:^16}{allowed!s:^16}{m.multiplier:^12}{ret:<13} # {m.description}")
return '\n'.join(s)
# Create the Register singleton
dtype_register = Register()

View File

@@ -0,0 +1,23 @@
class Error(Exception):
"""Base class for errors in the bitstring module."""
def __init__(self, *params: object) -> None:
self.msg = params[0] if params else ''
self.params = params[1:]
class ReadError(Error, IndexError):
"""Reading or peeking past the end of a bitstring."""
InterpretError = ValueError
"""Inappropriate interpretation of binary data."""
class ByteAlignError(Error):
"""Whole-byte position or length needed."""
CreationError = ValueError
"""Inappropriate argument during bitstring creation."""

View File

@@ -0,0 +1,97 @@
"""
The 8-bit float formats used here are from a proposal supported by Graphcore, AMD and Qualcomm.
See https://arxiv.org/abs/2206.02915
"""
import struct
import zlib
import array
import bitarray
from bitstring.luts import binary8_luts_compressed
import math
class Binary8Format:
"""8-bit floating point formats based on draft IEEE binary8"""
def __init__(self, exp_bits: int, bias: int):
self.exp_bits = exp_bits
self.bias = bias
self.pos_clamp_value = 0b01111111
self.neg_clamp_value = 0b11111111
def __str__(self):
return f"Binary8Format(exp_bits={self.exp_bits}, bias={self.bias})"
def decompress_luts(self):
binary8_to_float_compressed, float16_to_binary8_compressed = binary8_luts_compressed[(self.exp_bits, self.bias)]
self.lut_float16_to_binary8 = zlib.decompress(float16_to_binary8_compressed)
dec = zlib.decompress(binary8_to_float_compressed)
self.lut_binary8_to_float = struct.unpack(f'<{len(dec) // 4}f', dec)
def create_luts(self):
self.lut_binary8_to_float = self.createLUT_for_binary8_to_float()
self.lut_float16_to_binary8 = self.createLUT_for_float16_to_binary8()
def float_to_int8(self, f: float) -> int:
"""Given a Python float convert to the best float8 (expressed as an integer in 0-255 range)."""
# First convert the float to a float16, then a 16 bit uint
try:
b = struct.pack('>e', f)
except (OverflowError, struct.error):
# Return the largest representable positive or negative value
return self.pos_clamp_value if f > 0 else self.neg_clamp_value
f16_int = int.from_bytes(b, byteorder='big')
# Then use this as an index to our large LUT
return self.lut_float16_to_binary8[f16_int]
def createLUT_for_float16_to_binary8(self) -> bytes:
# Used to create the LUT that was compressed and stored for the fp8 code
import gfloat
fi = gfloat.formats.format_info_p3109(8 - self.exp_bits)
fp16_to_fp8 = bytearray(1 << 16)
for i in range(1 << 16):
b = struct.pack('>H', i)
f, = struct.unpack('>e', b)
fp = gfloat.round_float(fi, f)
if math.isnan(fp):
fp8_i = 0b10000000
else:
fp8_i = self.lut_binary8_to_float.index(fp)
fp16_to_fp8[i] = fp8_i
return bytes(fp16_to_fp8)
def createLUT_for_binary8_to_float(self):
"""Create a LUT to convert an int in range 0-255 representing a float8 into a Python float"""
i2f = []
for i in range(256):
b = bitarray.util.int2ba(i, length=8, endian='big', signed=False)
sign = b[0]
exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits])
significand = b[1 + self.exp_bits:]
if exponent == 0:
significand = bitarray.bitarray('0') + significand
exponent = -self.bias + 1
else:
significand = bitarray.bitarray('1') + significand
exponent -= self.bias
f = float(bitarray.util.ba2int(significand)) / (2.0 ** (7 - self.exp_bits))
f *= 2 ** exponent
i2f.append(f if not sign else -f)
# One special case for minus zero
i2f[0b10000000] = float('nan')
# and for plus and minus infinity
i2f[0b01111111] = float('inf')
i2f[0b11111111] = float('-inf')
return array.array('f', i2f)
# We create the 1.5.2 and 1.4.3 formats.
p4binary_fmt = Binary8Format(exp_bits=4, bias=8)
p3binary_fmt = Binary8Format(exp_bits=5, bias=16)
def decompress_luts():
p4binary_fmt.decompress_luts()
p3binary_fmt.decompress_luts()

View File

@@ -0,0 +1,245 @@
#
# This file is generated by generate_luts.py. DO NOT EDIT.
#
mxfp_luts_compressed = \
{(2, 1, 1, 'saturate'): (b'x\x01\x1d\xc9\xc1\r\xc00\x00\xc2@o\xd6\x8c\xc6f\xf5h\xb1\x828\xf1\x00^>X\x0c\xa7f1,\x7f'
b'\x13\x83\xfdY\xf4\x027\xf1\x0c\xfb',
b"x\x01\xed\xdd\t\r\xc00\x10\x03\xc1\xf4\xff[\xfehS\x1a'y\x96\x81\x87\x80[\x13\x01\x02"
b'\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80'
b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10'
b' @\x80\x00\x01\x02e\x05\x06\x11 @\x80@\xa2\xc0(\x02\xc9\x02\x93\x92\x05fe\x0b,J\x16X\x95-'
b'\xb0\x89\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04'
b'\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00'
b'\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x14\x14\xe8"@ U`\x17\x01\x02\x04\x08\x10 '
b'@\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @'
b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02u\x05\x0e\x11 @'
b'\x80@\xa2\xc0)\x02\xc9\x02\x97\x92\x05ne\x0b<J\x16x\x95-\xf0\x89\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 @\x80\x00\x01\x02\x04'
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\x85qB\x01\x14DQ\x12\x88\x11#\xeeF\x84\xb8\xbb\xf6_\x15\xa9\x82\xd9\xf9\xf3'
b'\xce\xed`O\x03\xdbj\x89\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01'
b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @'
b'\x80\x00\x81\xc6\x08\x8c\x89\x00\x01\x02\x04*\n\x8c\x8b@e\x81\xb6*\x0bt\x94\x15\x98\xa8\xddd'
b'\xf5\xa6j7\xdd\xf4f\xb2u\xd3\xcdf\x9bK7\x9fm!\xddb\xb6^\xba\xa5l\xcb\xe9V\xb2\xad\xa6['
b'\xcb\xb6\x9en#\xdbf\xba\xadl\xdb\xe9v\xb2\xed\xa6\xdb\xcb\xb6\x9f\xee@\x04\x08\x10 @\x80'
b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10'
b' @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02'
b'\x04\x08\x10\x18\x91\xc0P\x04\x08T\x158\x14\x01\x02\x04\x08\x10 @\x80\x00\x01\x02'
b'\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80'
b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\xcd\x118\x12\x01\x02\x04\x08T\x14\xe8\x8b@e\x81c'
b'U\x168QV\xe0\xb4vg\xd5\x1b\xd4\xee\xbc\xe9]d\xbbLw\x95\xed:\xddM\xb6\xdbtw\xd9\xee\xd3=d{L'
b'\xf7\x94\xed9\xddK\xb6\xd7to\xd9\xde\xd3}d\xfbL\xf7\x95\xed;\xddO\xb6\xdft\x7f"@\x80\x00\x01'
b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @'
b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08'
b'\x10 @\x80\xc0\x88\x04\xaa\x1e\x9f\xdbM\x80\xc0\xf0\x1f\x9f\x0cK\xfb'),
(3, 2, 3, 'saturate'): (b'x\x01\x15\xcbA\x15BQ\x08\x05@\xa2XD\xc5&/\nQ(\xa2\xf2\x9b\x10\xc5\x91\xc3\xc0\xe2B\xc4\xbf'
b'\xean<"\x92\xa2\x19\x96xF\xdcH\x0eE3,\x91r\x92C\xd1\x0cK\xbc\xe4$\x87\xa2\x19\x96(\xfd'
b'\xb6?n(\x9aa\x89\xaf\x7f\x92C\xd1\x0cK\x8c\x9c\xe4P4\xc3\x12\x97\x9c\xe4P4\xc3^?\xc7\x8a;c',
b'x\x01\xed\xdd\xd7U\x15\x00\x00\x04Q$\x89HV@r\x90 Q\xb2\x92\xfb\xaf\xea\xd1\x02\x7f'
b'\x1c\xce\xdc\xe9`o\x03;4$\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01'
b'\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\xe0\xd3\t|'
b'\x11\x01\x02\x04\x08\x14\x05\x86E\xa0,0\xa2\xb2\xc0\xa8>V`\xac\xddx\xbd\xaf\xed&\xea}k7Y\xef'
b'{\xbb\xa9z\xd3\xedf\xea\xcd\xb6\x9b\xab7\xdfn\xa1\xde\x8fv?\xeb-\xb6[\xaa\xb7\xdc\xee'
b'W\xbd\x95v\xab\xf5\xd6\xda\xad\xd7\xdbh\xb7YoK\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10'
b' @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 '
b'@\x80\x00\x01\x02\x04\x08\x10 \xf0.\x81\x81\x08\x10\xa8\nl\x8b\x00\x01\x02\x04\x08\x10 @\x80'
b'\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10'
b' @\x80\x00\x01\x02\x04\x08\x10\xf8|\x02;"@\x80\x00\x81\xa2\xc0\xae\x08\x94\x05~\xab,\xb0'
b'\xa7\x8f\x15\xd8owP\xef\xb0\xdd\x9fzG\xed\x8e\xeb\x9d\xb4;\xadw\xd6\xee\xbc\xde\xdfv\x17'
b'\xf5.\xdb]\xd5\xbbnwS\xef\xb6\xdd]\xbd\x7f\xed\xfe\xd7\xbbo\xf7P\xef\xb1\xddS\xbd\xe7v/\xf5^'
b'E\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @'
b'\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\x04\x08\x10 @\x80\x00\x01\x02\xef\x12'
b'\xa8\x1e\x9f\xdbM\x80\xc0\xe0\r\xd9\xf2;{'),
(4, 3, 7, 'saturate'): (b'x\x01\x1d\xcd[\xb5\x90!\x10\x80Q"\x18\x81\x08\xbc{C-@\x04"\xd0\xe0\x10\x81\x08D\xe0\xdd\x1bj'
b'\x01"\x10\xe1o\xa0\xfb\xc8Z{\xcd\xbc|C\x08\xff\xdf\xdb\x10:\x9b\xf0.\x84H\xa6\xd2\x19'
b'L\x16\x9b\xc3\xe5!\xbc\x0f\xe1\r\x91D\xa6Pit\x06\x93\xc5\xe6py\x08\x1f\xf4D\x12\x99'
b'B\xa5\xd1\x19L\x16\x9b\xc3\xe5!|\xd4\x13Id\n\x95Fg0Yl\x0e\x97\x87\x90\xf5D\x12\x99B\xa5'
b'\xd1\x19L\x16\x9b\xc3\xe5!|\xd2\x13Id\n\x95Fg0Yl\x0e\x97\x87\xf0YO$\x91)T\x1a\x9d\xc1d\xb19'
b'\\\x1e\xc2\x17=\x91D\xa6Pit\x06\x93\xc5\xe6p_\xf7\x17}\xe7\xab\xc1&|s\x8bL\xa53\x98,'
b'6\x87\xcbC\xf8\xee\x7f"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xf2\x10~\xe8\x89$2\x85J\xa33\x98,6'
b"\x87\xcbC\xf8\xa9'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0fa\xeb\x89$2\x85J\xa33\x98,6\x87\xcb"
b'C\xf8\xa5\'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0f\xe1\xb7\x9eH"S\xa84:\x83\xc9bs\xb8<\x84'
b'?z"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xbe\xee\x7f\xff\x01!\xba\xf7\x9b',
b'x\x01\xed\xdd\xd5\x96\x90\x05\x00E\xe1\x01\xe9\x90n\x90\x96F\xbaE\xa4S@\xba\x15\xa4\xbbKX H*'
b'\xdd)\xa9\x12Jw*H\x08Jww\x87\xd25\\\xccC\xec5\xf3\xef\xef\r\xce~\x81\x13\x12"\x0bX'
b'\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX \xc2\x16\x88$\x0b'
b'X\xc0\x02\x16\x08b\x81\xc8\xb2@\x90\x0b|\xa0 \x17\x88"\xb6@\xd4`\x8b\x16t\xd1\x83-Fx\x17\x93'
b'\x15\x8b\x16\x9b\x15\x87\x16\x97\xf5!-\x1e+>-\x01+!-\x11+1-\t+)-\x19+9-\x05+%-\x15+5-\r'
b'\xeb#ZZV:ZzV\x06ZFV&Zf\xd6\xc7\xb4,\xac\xac\xb4l\xac\xec\xb4\x1c\xac\x9c\xb4\\\xac\xdc\xb4O'
b'XyhyY\xf9h\xf9Y\x05h\x05Y\x85h\x85YEhEY\xc5h\xc5Y%h\x9f\xb2J\xd2>c\x95\xa2}\xce*M+\xc3*K+'
b'\xc7*O\xab\xc0\xaaH\xab\xc4\xaaL\xab\xc2\xaaJ\xab\xc6\xfa\x82V\x9dU\x83V\x93\xf5%\xad'
b'\x16\xab6\xad\x0e\xab.\xad\x1e\xab>\xad\x01\xab!\xad\x11\xab1\xad\t\xab)\xad\x19\xeb+\xda'
b'\xd7\xac\xe6\xb4\x16\xacoh-Y\xadh\xadYmhmY\xedh\xedY\x1dh\x1dY\x9dh\x9dY]h]Y\xddh\xddY=h'
b'=Y\xbdh\xbdY}h}Y\xfdh\xfdY\xdf\xd2\x06\xb0\x06\xca\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02'
b'\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05\xc2\n'
b'\x84\xca\x02\x16\x08j\x81A\xb2\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0'
b'\x02\x16\xb0\x80\x05"n\x81\xc1\xb2\x80\x05,`\x81 \x16\xf8N\x16\x08r\x81!\nr\x81\xa1'
b'b\x0b|\x1fl\xc3\x82nx\xb0\x8d\x08\xefF\xb2F\xd1F\xb3~\xa0\xfd\xc8\x1aC\x1b\xcb\x1a'
b'G\x1b\xcf\x9a@\x9b\xc8\x9aD\x9b\xcc\x9aB\x9b\xca\x9aF\x9b\xce\x9aA\x9b\xc9\x9aE\x9b\xcd\x9a'
b'C\x9b\xcb\xfa\x896\x8f5\x9f\xb6\x80\xb5\x90\xb6\x88\xb5\x98\xf63\xeb\x17\xda\xaf\xac'
b'%\xb4\xa5\xace\xb4\xe5\xac\xdfh\xbf\xb3V\xd0V\xb2V\xd1V\xb3\xd6\xd0\xd6\xb2\xd6\xd1\xd6\xb3'
b'6\xd06\xb26\xd16\xb3\xb6\xd0\xb6\xb2\xb6\xd1\xb6\xb3v\xd0v\xb2\xfe\xa0\xfd\xc9\xdaE\xdb\xcd'
b'\xfa\x8b\xb6\x87\xb5\x97\xb6\x8f\xb5\x9f\xf67\xeb\x00\xed \xeb\x1f\xda\xbf\xacC\xb4\xc3'
b'\xac#\xb4\xa3\xacc\xb4\xe3\xac\x13\xb4\x93\xacS\xb4\xd3\xac3\xb4\xb3\xacs\xb4\xf3'
b'\xac\x0b\xb4\x8b\xacK\xb4\xcb\xac+\xb4\xab\xack\xb4\xeb\xac\x1b\xb4\x9b\xac[\xb4\xdb'
b'\xac;\xb4\xbb\xac{\xb4\xfb\xac\x07\xb4\x87\xacG\xb4\xc7\xac\xffh\xff\xb3\x9e\xd0\x9e'
b'\xb2\x9e\xd1\x9e\xb3^\xd0^\xb2^\xd1^\xb3\xde\xd0\xde\xb2\xde\xc9\x02\x16\xb0\x80\x05'
b',`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0'
b'\x02\x16\xb0\x80\x05\xc2\n\x04\xf5\xf8\xdc\xdd\x16\xb0@\xe8{\t?\xc8\x90'),
(5, 2, 15, 'saturate'): (b'x\x01M\xcb\xd9\x11\x10T\x0c@\xd1\x94B\x17 \x08\x06Dh#\x9d\x90R\xd2\x05\xca\x1a\x17'
b'\xa0\x8dt\x02\xc7\x1f\xc7\xcc\x9c\xf7\xf1n\x12\xf1\xef\xf4C\xcf\xa3\x88\xa4\x19\x96#~\x8ax@'
b"R4\xc3r\xc4c\x9d\xa4h\x86\xe5\x88':I\xd1\x0c\xcb\x11?\xeb$E3,G<\xd5I\x8afX\x8ex\xa6\x93"
b'\x14\xcd\xb0\x1c\xf1\x8bNR4\xc3rD\xea$E3,G<\xd7I\x8afX\x8ex\xa1\x93\x14\xcd\xb0\x1c'
b'\xf1\xabNR4\xc3r\xc4K\x9d\xa4h\x86\xe5\x88\xdft\x92\xa2\x19\x96#^\xe9$E3,G\xbc\xd6I'
b'\x8a~\xa3\xfdO\xb4\xbf\xb7\xf6~\xb7C3,G\xfc\xe1\x9e\xa4h\x86\xe5\x88w:I\xd1\x0c'
b'\xcb\x11\xefu\x92\xa2\x19\x96#>\xe8$E3,G|\xd4I\x8afX\x8e\xf8\xa4\x93\x14\xcd\xb0\x1c\xf1Y'
b"')\x9aa9bu\x92\xa2\x19\x96#\xfe\xd4I\x8afX\x8e\xf8K')\x9aa9\xe2o\x9d\xa4h\x86\xe5\x88\x7ft"
b'\x92\xa2\x19\x96#\xbe\xe8$E3,G|\xd5I\x8afX\x8e\xf8\xa6\x93\x14\xfd]\xfb\xcf\x0f'
b'\xd2\x15\xf0\xcf',
b'x\x01\xed\xddC\xa2\x1c\x00\x00D\xc1\xd8\xb6m\xdb\xb6m\xdb66\xb1m\xdb\xb6m\xdb\xb6m'
b',\x92c\xfcEM\xdd\xa0\xdf\x05:X\xb0 \x16\xdc\x16B\x17\xd2\x16J\x17\xda\x16F\x17\xd6'
b'\x16N\x17\xde\x16A\x17\xd1\x16I\x17\xd9\x16E\x17\xd5\x16M\x17\xdd\x16C\x17\xd3\x16K\x17\xdb'
b'\x16G\x17\xd7\x16O\x17\xdf\x96@\x97\xd0\x96H\x97\xd8\x96D\x97\xd4\x96L\x97\xdc\x96B\x97\xd2'
b'\x96J\x97\xda\x96F\x97\xd6\x96N\x97\xde\x96A\x97\xd1\x96I\x97\xd9\x96E\x97\xd5\x96M\x97\xdd'
b'\x96C\x97\xd3\x96K\x97\xdb\x96G\x97\xd7\x96O\x97\xdfV@W\xd0VHW\xd8VDW\xd4VLW\xdcVBW\xd2'
b'VJW\xdaVFW\xd6VNW\xdeVAW\xd1VIW\xd9VEW\xd5VMW\xddVCW\xd3VKW\xdbVGW\xd7VOW\xdf\xd6@\xd7\xd0'
b'\xd6H\xd7\xd8\xd6D\xd7\xd4\xd6L\xd7\xdc\xd6B\xd7\xd2\xd6J\xd7\xda\xd6F\xd7\xd6\xd6N\xd7\xde'
b'\xd6A\xd7\xd1\xd6I\xd7\xd9\xd6E\xd7\xd5\xd6M\xd7\xdd\xd6C\xd7\xd3\xd6K\xd7\xdb\xd6G\xd7\xd7'
b'\xd6O\xd7\xdf6@70 H\x0b\xfc\x0b\x08\x14\x08\x14P\x0b\x0c\nj\x83mCtCm\xc3t\xc3m#t#m'
b'\xa3t\xa3mctcm\xe3t\xe3m\x13t\x13m\x93t\x93mStSm\xd3t\xd3m3t3m\xb3t\xb3mstsm\xf3t\xf3m'
b'\x0bt\x0bm\x8bt\x8bmKtKm\xcbt\xcbm+t+m\xabt\xabmktkm\xebt\xebm\x1bt\x1bm\x9bt\x9bm[t[m'
b"\xdbt\xdbm;t;m\xbbt\xbbm{t{m\xfbt\xfbm\x07t\x07m\x87t\x87mGtGm\xc7t\xc7m't'm\xa7t\xa7mgtgm"
b'\xe7t\xe7m\x17t\x17m\x97t\x97mWtWm\xd7t\xd7m7t7m\xb7t\xb7mwtwm\xf7t\xf7m\x0ft\x0fm'
b'\x8ft\x8fmOtOm\xcft\xcfm/t/m\xaft\xafmotom\xeft\xefm\x1ft\x1fm\x9ft\x9fm_t_m\xdft\xdfm?t?m'
b'\xbft\xbfm\x7ft\x7f\x03\x82\xb4\x80z|\x1e\xd8\x1d(\x10(\xf0\xef?\xe6\xfc\r\x9b'),
(4, 3, 7, 'overflow'): (b'x\x01\x1d\xcd[\xb5\x90!\x10\x80Q"\x18\x81\x08\xbc{C-@\x04"\xd0\xe0\x10\x81\x08D\xe0\xdd\x1bj'
b'\x01"\x10\xe1o\xa0\xfb\xc8Z{\xcd\xbc|C\x08\xff\xdf\xdb\x10:\x9b\xf0.\x84H\xa6\xd2\x19'
b'L\x16\x9b\xc3\xe5!\xbc\x0f\xe1\r\x91D\xa6Pit\x06\x93\xc5\xe6py\x08\x1f\xf4D\x12\x99'
b'B\xa5\xd1\x19L\x16\x9b\xc3\xe5!|\xd4\x13Id\n\x95Fg0Yl\x0e\x97\x87\x90\xf5D\x12\x99B\xa5'
b'\xd1\x19L\x16\x9b\xc3\xe5!|\xd2\x13Id\n\x95Fg0Yl\x0e\x97\x87\xf0YO$\x91)T\x1a\x9d\xc1d\xb19'
b'\\\x1e\xc2\x17=\x91D\xa6Pit\x06\x93\xc5\xe6p_\xf7\x17}\xe7\xab\xc1&|s\x8bL\xa53\x98,'
b'6\x87\xcbC\xf8\xee\x7f"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xf2\x10~\xe8\x89$2\x85J\xa33\x98,6'
b"\x87\xcbC\xf8\xa9'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0fa\xeb\x89$2\x85J\xa33\x98,6\x87\xcb"
b'C\xf8\xa5\'\x92\xc8\x14*\x8d\xce`\xb2\xd8\x1c.\x0f\xe1\xb7\x9eH"S\xa84:\x83\xc9bs\xb8<\x84'
b'?z"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xe1\xbe\xee\x7f\xff\x01!\xba\xf7\x9b',
b'x\x01\xed\xdc\x05\xb2\x90\x05\x00E\xe1\x87\x80\xa4t\x83tw\x97Jw\x0bHKw\x83\x80\x92Cww7'
b'\x92\xd2\x1dRJww\x97\xa4\x92J\xba\x8c3\x8f\xff|\x1b\xb83g\x017$D\x16\xb0\x80\x05,`\x01'
b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\xc0g[ \x8c,`\x01\x0bX '
b'\x88\x05\xbe\x90\x05\x82\\ \xac\x82\\ \x9c\xd8\x02\xe1\x83\xed\xcb\xa0\x8b\x10l\x11'
b'C\xbbH\xac\xc8\xb4(\xac\xa8\xb4\xafX\xd1h\xd1Y1h1Y\xb1h\xb1YqhqY\xf1h\xf1Y\th\tY\x89h\x89Y'
b'Ih_\xb3\x92\xd2\x92\xb1\x92\xd3R\xb0R\xd2R\xb1R\xd3\xd2\xb0\xd2\xd2\xd2\xb1\xd2\xd32\xb0'
b'2\xd22\xb12\xd3\xb2\xb0\xb2\xd2\xb2\xb1\xb2\xd3r\xb0r\xd2r\xb1r\xd3\xf2\xb0\xf2\xd2\xf2\xb1'
b'\xf2\xd3\n\xb0\n\xd2\xbea}K\xfb\x8eU\x88V\x98U\x84V\x94U\x8cV\x9cU\x82V\x92U\x8aV\x9a'
b'U\x86V\x96U\x8eV\x9eU\x81V\x91U\x89V\x99U\x85\xf6=\xab*\xad\x1a\xab:\xed\x07V\rZMV-ZmV\x1dZ]'
b'V=\xda\x8f\xac\xfa\xb4\x06\xac\x86\xb4F\xac\xc6\xb4&\xac\xa6\xb4f\xac\xe6\xb4\x16'
b'\xac\x96\xb4V\xac\xd6\xb46\xac\xb6\xb4v\xac\xf6\xb4\x0e\xac\x8e\xb4N\xac\xce\xb4\x9fX]h]'
b'Y\xddh?\xb3~\xa1ug\xf5\xa0\xf5d\xf5\xa2\xf5f\xf5\xa1}\x92\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80'
b'\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX'
b'\xc0\x02\x16`\x0b\xf4\x95\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0'
b"\x80\x05,\xf0\xf9\x16\xe8'\x0bX\xc0\x02\x16\x08b\x81\xfe\xb2@\x90\x0b\x0cP\x90\x0b\x0c\x14["
b'`P\xb0\r\x0e\xba!\xc164\xb4\x1b\xc6\x1aN\x1b\xc1\x1aI\x1b\xc5\x1aM\x1b\xc3\x1aK\x1b'
b'\xc7\x1aO\x9b\xc0\x9aH\x9b\xc4\x9aL\x9b\xc2\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b'
b'\xc3\x9aK\x9b\xc7\x9aO[\xc0ZH[\xc4\xfa\x95\xb6\x98\xb5\x84\xb6\x94\xb5\x8c\xb6'
b'\x9c\xb5\x82\xf6\x1bk%m\x15k5m\rk-m\x1dk=m\x03k#m\x13k3m\x0bk+m\x1bk;m\x07\xebw\xda'
b"N\xd6.\xdan\xd6\x1e\xda^\xd6\x1f\xb4?Y\xfbh\xfbY\x07h\x07Y\x87h\x87YGhGY\xc7h\xc7Y'h'Y\xa7h"
b'\xa7YghgY\xe7h\xe7Y\x17h\x17Y\x97h\x97YWhWY\xd7h\xd7Y7h7Y\xb7h\xb7YwhwY\xf7h\xf7Y\x0fh'
b'\x7f\xb1\x1e\xd2\x1e\xb1\x1e\xd3\x9e\xb0\x9e\xd2\x9e\xb1\xfe\xa6\xfd\xc3zN{\xc1zI{\xc5zM'
b'{\xc3\xfa\x97\xf6\x1f\xeb-\xed\x1d\xeb=\xed\x03\xeb#\x8d\xbd\xefw\xdd\x02\x16\xb0\x80\x05,`'
b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16'
b'\xb0\x80\x05,`\x01\x0b|\xfa\x1f\xb2\xf6b\xf1'),
(5, 2, 15, 'overflow'): (b'x\x01M\xcb\xd9\x11\x10T\x0c@\xd1\x94B\x17 \x08\x06Dh#\x9d\x90R\xd2\x05\xca\x1a\x17'
b'\xa0\x8dt\x02\xc7\x1f\xc7\xcc\x9c\xf7\xf1n\x12\xf1\xef\xf4C\xcf\xa3\x88\xa4\x19\x96#~\x8ax@'
b"R4\xc3r\xc4c\x9d\xa4h\x86\xe5\x88':I\xd1\x0c\xcb\x11?\xeb$E3,G<\xd5I\x8afX\x8ex\xa6\x93"
b'\x14\xcd\xb0\x1c\xf1\x8bNR4\xc3rD\xea$E3,G<\xd7I\x8afX\x8ex\xa1\x93\x14\xcd\xb0\x1c'
b'\xf1\xabNR4\xc3r\xc4K\x9d\xa4h\x86\xe5\x88\xdft\x92\xa2\x19\x96#^\xe9$E3,G\xbc\xd6I'
b'\x8a~\xa3\xfdO\xb4\xbf\xb7\xf6~\xb7C3,G\xfc\xe1\x9e\xa4h\x86\xe5\x88w:I\xd1\x0c'
b'\xcb\x11\xefu\x92\xa2\x19\x96#>\xe8$E3,G|\xd4I\x8afX\x8e\xf8\xa4\x93\x14\xcd\xb0\x1c\xf1Y'
b"')\x9aa9bu\x92\xa2\x19\x96#\xfe\xd4I\x8afX\x8e\xf8K')\x9aa9\xe2o\x9d\xa4h\x86\xe5\x88\x7ft"
b'\x92\xa2\x19\x96#\xbe\xe8$E3,G|\xd5I\x8afX\x8e\xf8\xa6\x93\x14\xfd]\xfb\xcf\x0f'
b'\xd2\x15\xf0\xcf',
b'x\x01\xed\xddS\xb6\x1e\x06\x00\x85\xd1\x1b\xdb\xb6\x1b\xdb\xb6\x9b\x06\x8dm\xdbz\x88'
b"m\xabI\x1b\xa3\xb1m\xdb\xb6mg\xad\x9ba\xdc\x87\xfd\xef\x19\x9co\x02'((\x84\x85\xb2"
b'\x85\xd6\x85\xb1\x85\xd5\x85\xb3\x85\xd7E\xb0E\xd4E\xb2E\xd6E\xb1E\xd5E\xb3E\xd7\xc5\xb0'
b'\xc5\xd4\xc5\xb2\xc5\xd6\xc5\xb1\xc5\xd5\xc5\xb3\xc5\xd7%\xb0%\xd4%\xb2%\xd6%\xb1%\xd5%\xb3'
b'%\xd7\xa5\xb0\xa5\xd4\xa5\xb2\xa5\xd6\xa5\xb1\xa5\xd5\xa5\xb3\xfd\xa1Ko\xcb\xa0\xcbh'
b'\xcb\xa4\xcbl\xcb\xa2\xcbj\xcb\xa6\xcbn\xcb\xa1\xcbi\xcb\xa5\xcbm\xcb\xa3\xcbk\xcb\xa7\xcbo'
b'+\xa0+h+\xa4+l+\xa2+j+\xa6+n+\xa1+i+\xa5+m+\xa3+k+\xa7+o\xab\xa0\xabh\xab\xa4\xabl'
b'\xfbSW\xc5\xf6\x97\xae\xaa\xad\x9a\xae\xba\xad\x86\xeeo[M]-[m]\x1d[]]=[}]\x03[C]#[c]\x13'
b"[S]3[s]\x0b[K]+[k]\x1b[[];[{]\x07[G]'[g]\x17[W]7[w]\x0f[O]/[o]\x1f[_]?[\x7f\xdd\x00"
b'\xdb\xc0\x90\x16\x1c\x10(\x10(\xa0\x16\x18\x14\xd2\x06\xdb\x86\xe8\x86\xda\x86\xe9\x86\xdb'
b'F\xe8F\xdaF\xe9F\xdb\xc6\xe8\xc6\xda\xc6\xe9\xc6\xdb&\xe8&\xda&\xe9&\xdb\xa6\xe8\xa6\xda'
b'\xa6\xe9\xa6\xdbf\xe8f\xdaf\xe9f\xdb\xe6\xe8\xe6\xda\xe6\xe9\xfe\xb1\xcd\xd7-\xb0'
b'\xfd\xab\xfb\xcf\xb6P\xb7\xc8\xb6X\xb7\xc4\xb6T\xb7\xcc\xb6\\\xb7\xc2\xb6R\xb7\xca'
b'\xf6\xbfn\xb5m\x8dn\xadm\x9dn\xbdm\x83n\xa3m\x93n\xb3m\x8bn\xabm\x9bn\xbbm\x87n\xa7'
b'm\x97n\xb7m\x8fn\xafm\x9fn\xbf\xed\x80\xee\xa0\xed\x90\xee\xb0\xed\x88\xee\xa8'
b'\xed\x98\xee\xb8\xed\x84\xee\xa4\xed\x94\xee\xb4\xed\x8c\xee\xac\xed\x9c\xee\xbc'
b'\xed\x82\xee\xa2\xed\x92\xee\xb2\xed\x8a\xee\xaa\xed\x9a\xee\xba\xed\x86\xee\xa6'
b'\xed\x96\xee\xb6\xed\x8e\xee\xae\xed\x9e\xee\xbe\xed\x81\xee\xa1\xed\x91\xee\xb1'
b'\xed\x89\xee\xa9\xed\x99\xee\xb9\xed\x85\xee\xa5\xed\x95\xee\xb5\xed\x8d\xee\xad'
b'\xed\x9d\xee\xbd\xed\x83\xee\xa3\xed\x93\xee\xb3\xed\x8b\xee\xab\xed\x9b\xee\xbb'
b'\xed\x87\xee\xa7\xedWHS\x8f\xcf\x03\xbb\x03\x05\x02\x05\x82\x7f\x03\xb3\x87\x0e\x9d')}
binary8_luts_compressed = \
{(4, 8): (b'x\x01\x15\xcb[\xd5P!\x10\x80Q"\x18\x81\x08<{E-@\x04"\xd0@"\x10\x81\x08<{E\xff\x02\'\x02\x11h\xa0\xdbY'
b'k\xcf\xcb\xcc\x17\xc2\xff\xe9\xaf\xad7!d:\x93\xcd!\xbc\r\xe1\x15\x91D\xa6Pit\x06\x93\xc5\xe6\xe1p\t\xef\xf4'
b'D\x12\x99B\xa5\xd1\x19L\x16\x9b\x87\xc3%\xbc\xd7\x13Id\n\x95Fg0Yl\x1e\x0e\x97\xf0AO$\x91)T\x1a\x9d\xc1d\xb1'
b"y8\\B\xd6\x13Id\n\x95Fg0Yl\x1e\x0e\x97\xf0QO$\x91)T\x1a\x9d\xc1d\xb1y8\\\xc2'=\x91D\xa6Pit\x06\x93"
b'\xc5\xe6\xe1p\t\x9f\xf5D\x12\x99B\xa5\x7f\xf1O\xff\xea\xef\x9b\x1b\x9d\xc9\xe6\x10\xbe\xeb\x89$2\x85J\xa3'
b'3\x98,6\x0f\x87K\xf8\xa1\'\x92\xc8\x14*\x8d\xce`\xb2\xd8<\x1c.\xe1\xa7\x9eH"S\xa84:\x83\xc9b\xf3p\xb8\x84_z'
b'"\x89L\xa1\xd2\xe8\x0c&\x8b\xcd\xc3\xe1\x12\xb6\x9eH"S\xa84:\x83\xc9b\xf3p\xb8\x84\xdfz"\x89L\xa1\xd2\xe8'
b'\x0c&\x8b\xcd\xc3\xe1\x12\xfe\xe8\x89$2\x85J\xa33\x98,6\x0f\x87Kx\xd1\x13Id\n\x95\xfe\xf7\x1f[)\xf3`',
b'x\x01\xed\xdd\x05\xba\x96\x05\x00\x05\xe1\x9f\xee\x06\xe9FZA\xa4\xbb\xbb;\xa4SB\xba\xeb\xd2\xdd\x8dt\x97'
b'\x92J(\xa14\xa2\x84\x92\x8a\xa4\x82\xd2\x1d\x12.c\x9e\xcb7\xef\x0e\xcel\xe0\x84B\xb2\x80\x05,`\x01'
b'\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xe0\xbd/\x10A\x16\xb0\x80\x05,\x10\xc4\x02\x11e\x81 \x17\x88'
b'\xa4 \x17\x88,\xb6@\x94`\x8b\x1at\xd1\x82-zx\x17\x83\x15\x93\x16\x8b\x15\x9b\x16\x87\x15\x97\x16\x8f\x15'
b'\x9f\x96\x80\x95\x90\x96\x88\x95\x98\x96\x84\xf5\x01-)+\x19-9+\x05-%+\x15-5+\r--+\x1d-=+\x03-#+\x13\xedCV'
b'fZ\x16VVZ6VvZ\x0eVN\xdaG\xac\x8fi\xb9X\xb9i\x9f\xb0\xf2\xd0>e\xe5\xa5\xe5c\xe5\xa7\x15`\x15\xa4\x15b'
b'\x15\xa6\x15a\x15\xa5\x15c\x15\xa7\x95`\x95\xa4\x95b\x95\xa6\x95a\x95\xa5\x95c\x95\xa7U`U\xa4UbU\xa6Ua'
b'U\xa5UcU\xa7\xd5`\xd5\xa4\xd5b\xd5\xa6\xd5a\xd5\xa5\xd5c\xd5\xa75`5\xa45b5\xa65a}Fk\xcajFk\xcejAk\xc9'
b"jEk\xcdjCk\xcbjGk\xcf\xea@\xfb\x9c\xd5\x91\xd6\x89\xd5\x99\xd6\x85\xf5\x05\xad+\xab\x1b\xad;\xab\x07\xad'"
b'\xab\x17\xad7\xab\x0f\xad/\xab\x1f\xad?k\x00m k\x10m0k\x08m\xa8,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,'
b'`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x00\\'
b' L\x16\xb0@P\x0b\xbc\xf7\xff\x86\x0e\xb4\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX '
b'\x14\x1a&\x0bX\xc0\x02\x16\x08b\x81\xe1\xb2@\x90\x0b\x8cP\x90\x0b\x8c\x14[`T\xb0\x8d\x0e\xba1\xc16'
b'6\xbc\x1b\xc7\x1aO\x9b\xc0\x9aH\x9b\xc4\x9aL\x9b\xc2\x9aJ\x9b\xc6\x9aN\x9b\xc1\x9aI\x9b\xc5\x9aM\x9b\xc3'
b'\x9aK\x9b\xc7\x9aO\xfb\x92\xb5\x80\xb6\x90\xb5\x88\xb6\x98\xb5\x84\xb6\x94\xb5\x8c\xb6\x9c\xb5\x82\xb6\x92'
b'\xb5\x8a\xb6\x9a\xb5\x86\xb6\x96\xb5\x8e\xb6\x9e\xb5\x81\xf6\x15\xebk\xdaF\xd6&\xdaf\xd6\x16\xdaV\xd66\xda7'
b'\xacoi\xdbY;h;Y\xbbh\xdf\xb1\xbe\xa7\xedf\xed\xa1\xede\xed\xa3\xfd\xc0\xfa\x91\xb6\x9fu\x80v\x90u\x88v'
b'\x98u\x84v\x94u\x8c\xf6\x13\xeb8\xedg\xd6/\xb4\x13\xac\x93\xb4S\xac\xd3\xb4_Y\xbf\xd1\xce\xb0\xce\xd2'
b'\xce\xb1\xce\xd3.\xb0.\xd2~g\xfdA\xbb\xc4\xfa\x93v\x99u\x85v\x95u\x8dv\x9du\x83\xf6\x17\xebo\xdaM\xd6-'
b'\xda?\xac\x7fi\xb7YwhwY\xf7h\xf7Y\x0fh\x0fY\x8fh\x8fYOhOY\xcfh\xcfY/h/Y\xafh\xff\xb1^\xd3\xde\xb0\xde'
b'\xd2\xde\xc9\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`'
b'\x01\x0bX\xc0\x02\x16\xb0\x80\x05,`\x01\x0b\xc0\x05\x82z|\xeen\x0bX \xec\x7f\xc6\xe4\x02%'),
(5, 16): (b'x\x01\x1d\xc9\x89\r\x10\x04\x0c\x86\xd1\x8e\xc2\x16 \x97\x059\xd6\xe8&t\x94n\xa1\\RQ`\x8dn\x02/6yI'
b"\xf3\x7f\x11\xff\xdf\xc3\x88f\x89G\x11\x0fH\x8afX\x8e\xf8M')\x9aa9\xe2\xb1NR4\xc3r\xc4\x13\x9d\xa4h"
b'\x86\xe5\x88\xa7:I\xd1\x0c\xcb\x11\xcft\x92\xa2\x19\x96#\x9e\xeb$E3,G\xfc\xae\x93\x14\xcd\xb0\x1c\x91'
b":I\xd1\x0c\xcb\x11/t\x92\xa2\x19\x96#^\xea$E3,G\xfc\xa1\x93\x14\xcd\xb0\x1c\xf1J')\x9aa9\xe2\xb5NR4\xc3"
b'r\xc4\x1b\x9d\xa4h\x86\xe5\x88\xb7:I\xbf\xb3\x13\x7f\xfaY\xe2/\x9d\xa4h\x86\xe5\x88\xf7:I\xd1\x0c\xcb'
b'\x11\x1ft\x92\xa2\x19\x96#>\xea$E3,G|\xd2I\x8afX\x8e\xf8\xac\x93\x14\xcd\xb0\x1c\xf1\xb7NR4\xc3r'
b"\xc4\x17\x9d\xa4h\x86\xe5\x88\xd5I\x8afX\x8e\xf8G')\x9aa9\xe2\xabNR4\xc3r\xc4\xbf:I\xd1\x0c\xcb\x11"
b"\xff\xe9$E3,G|\xd3I\x8afX\x8e\xf8\xae\x93\x14\xcd\xb0\x1c\xf1C'\xe9\x9f\xbf\x00Gi\xed\x02",
b'x\x01\xed\xddU\xd6\x96\x05\x00\x85\xd1\x9f\x16\xa4\x14\x04\xe9P\xa4K\x1a\xe9\x14\x10\t\xe9\x0eQ'
b'\xba\xc1\xa0\xbb\xbb\xa5\x1b)%\xa5;%\xa5\xbb;%\x94\x8eAp\xf1.\xd6\xfe\xf6\x0c\xce3\x81\x13\x16\xf6'
b'\x8e\xc2\x05+|\xd0"\x04+b\xd0"\x05+r\xd0\xa2\x04\xeb\x83\xf7]T[4\xdd\x87\xb6\xe8\xba\x18\xb6\x98\xbaX\xb6'
b'\xd8\xba\x8fl\x1f\xeb\xe2\xd8\xe2\xea>\xb1\xc5\xd3\xc5\xb7}\xaaK`K\xa8KdK\xacKbK\xaaKfK\xaeKaK\xa9\xfb\xcc'
b'\xf6\xb9.\x95\xed\x0b]j[\x1a]Z[:]z[\x06]F[&]f[\x16]V\xdb\x97\xbal\xb6\xec\xba\x1c\xb6\x9c\xba\\'
b'\xb6\xdc\xba<\xb6\xbc\xba\xafl\xf9t\xf9m\x05t\x05m\x85t\x85mEtEm\xc5t\xc5m%t%m_\xebJ\xd9J\xeb\xca'
b'\xd8\xbe\xd1\x95\xb5}\xab+g+\xaf\xab`\xab\xa8\xfb\xceVIW\xd9VEW\xd5VMW\xddVCW\xd3VKW\xdbVGW\xd7VOW'
b'\xdf\xd6@\xd7\xd0\xf6\xbd\xae\x91\xed\x07\xdd\x8f\xb6\xc6\xba&\xb6\xa6\xbaf\xb6\xe6\xba\x16\xb6\x96\xba'
b'V\xb6\xd6\xba6\xb6\xb6\xbav\xb6\xf6\xba\x0e\xb6\x8e\xba\x9fl?\xeb~\xb1\xfd\xaa\xebd\xeb\xac\xebb\xeb\xaa'
b"\xeb\x16\x12h\x81\xee!\xa1\x02\xa1\x02j\x81\xb0w\xd5#X=\x83\xd6+X\xbd\x83\xd6'X}\x83\xd6/X\xfd\xdfw"
b'\x03l\x03u\x83l\x83uClCu\xc3l\xc3u#l#u\xa3l\xa3uclcu\xe3l\xe3u\x13l\xbf\xe9&\xda&\xe9&\xdb\xa6\xe8'
b'\xa6\xda\xa6\xe9\xa6\xdbf\xe8f\xdaf\xe9f\xdb\xe6\xe8\xe6\xda\xe6\xe9~\xb7\xcd\xd7-\xb0-\xd4-\xb2-\xd6'
b'\xfda\xfbS\xb7\xc4\xb6T\xb7\xcc\xb6\\\xb7\xc2\xb6R\xf7\x97m\x95n\xb5m\x8dn\xadm\x9dn\xbdm\x83n\xa3m\x93'
b'n\xb3m\x8bn\xabm\x9bn\xbbm\x87n\xa7m\x97n\xb7\xedo\xdd\x1e\xdb^\xdd>\xdb~\xdd\x01\xdbA\xdd?\xb6C'
b'\xba\xc3\xb6#\xba\xa3\xb6c\xba\xe3\xb6\x13\xba\x93\xb6S\xba\xd3\xb63\xba\xb3\xb6s\xba\xf3\xb6\x0b'
b'\xba\x8b\xb6K\xba\xcb\xb6+\xba\xab\xb6k\xba\xeb\xb6\x1b\xba\x9b\xb6[\xba\xdb\xb6;\xba\xbb\xb6{'
b'\xba\xfb\xb6\x7fu\x0fl\x0fu\x8fl\x8fu\xff\xd9\xfe\xd7=\xb1=\xd5=\xb3=\xd7\xbd\xb0\xbd\xd4\xbd\xb2\xbd'
b'\xd6\xbd\t\t\xb4\x80z|\x1e\xda\x1d*\x10*\xd0\xfd-\x8c\x93\xc6\x0e')}

View File

@@ -0,0 +1,95 @@
from __future__ import annotations
import bitstring
from bitstring.bitstream import BitStream
from bitstring.utils import tokenparser
from bitstring.exceptions import CreationError
from typing import Union, List
from bitstring.bitstore import BitStore
from bitstring.bitstore_helpers import bitstore_from_token
def pack(fmt: Union[str, List[str]], *values, **kwargs) -> BitStream:
"""Pack the values according to the format string and return a new BitStream.
fmt -- A single string or a list of strings with comma separated tokens
describing how to create the BitStream.
values -- Zero or more values to pack according to the format.
kwargs -- A dictionary or keyword-value pairs - the keywords used in the
format string will be replaced with their given value.
Token examples: 'int:12' : 12 bits as a signed integer
'uint:8' : 8 bits as an unsigned integer
'float:64' : 8 bytes as a big-endian float
'intbe:16' : 2 bytes as a big-endian signed integer
'uintbe:16' : 2 bytes as a big-endian unsigned integer
'intle:32' : 4 bytes as a little-endian signed integer
'uintle:32' : 4 bytes as a little-endian unsigned integer
'floatle:64': 8 bytes as a little-endian float
'intne:24' : 3 bytes as a native-endian signed integer
'uintne:24' : 3 bytes as a native-endian unsigned integer
'floatne:32': 4 bytes as a native-endian float
'hex:80' : 80 bits as a hex string
'oct:9' : 9 bits as an octal string
'bin:1' : single bit binary string
'ue' / 'uie': next bits as unsigned exp-Golomb code
'se' / 'sie': next bits as signed exp-Golomb code
'bits:5' : 5 bits as a bitstring object
'bytes:10' : 10 bytes as a bytes object
'bool' : 1 bit as a bool
'pad:3' : 3 zero bits as padding
>>> s = pack('uint:12, bits', 100, '0xffe')
>>> t = pack(['bits', 'bin:3'], s, '111')
>>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44)
"""
tokens = []
if isinstance(fmt, str):
fmt = [fmt]
try:
for f_item in fmt:
_, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys())))
tokens.extend(tkns)
except ValueError as e:
raise CreationError(*e.args)
value_iter = iter(values)
bsl: List[BitStore] = []
try:
for name, length, value in tokens:
# If the value is in the kwd dictionary then it takes precedence.
value = kwargs.get(value, value)
# If the length is in the kwd dictionary then use that too.
length = kwargs.get(length, length)
# Also if we just have a dictionary name then we want to use it
if name in kwargs and length is None and value is None:
bsl.append(BitStream(kwargs[name])._bitstore)
continue
if length is not None:
length = int(length)
if value is None and name != 'pad':
# Take the next value from the ones provided
value = next(value_iter)
if name == 'bits':
value = bitstring.bits.Bits(value)
if length is not None and length != len(value):
raise CreationError(f"Token with length {length} packed with value of length {len(value)}.")
bsl.append(value._bitstore)
continue
bsl.append(bitstore_from_token(name, length, value))
except StopIteration:
raise CreationError(f"Not enough parameters present to pack according to the "
f"format. {len(tokens)} values are needed.")
try:
next(value_iter)
except StopIteration:
# Good, we've used up all the *values.
s = BitStream()
if bitstring.options.lsb0:
bsl.reverse()
for b in bsl:
s._bitstore += b
return s
raise CreationError(f"Too many parameters present to pack according to the format. Only {len(tokens)} values were expected.")

View File

@@ -0,0 +1,206 @@
import array
import math
import struct
import bitarray
from bitstring.luts import mxfp_luts_compressed
import zlib
from typing import Optional
def round_to_nearest_ties_to_even(lut_int_to_float, lower: int, f: float) -> Optional[int]:
upper = lower + 1
# Special case for LUTs without a negative zero.
lower_float = 0.0 if lower == 128 else lut_int_to_float[lower]
upper_float = lut_int_to_float[upper]
if upper_float < lower_float:
lower, upper = upper, lower
lower_float, upper_float = upper_float, lower_float
if f == lower_float:
return lower
if f == upper_float:
return upper
if lower_float < f < upper_float:
d1 = f - lower_float
d2 = upper_float - f
if d1 < d2:
return lower
if d2 < d1:
return upper
return lower if lower % 2 == 0 else upper
return None
class MXFPFormat:
"""Defining an MXFP micro-scaling floating point format"""
def __init__(self, exp_bits: int, mantissa_bits: int, bias: int, mxfp_overflow: str):
self.exp_bits = exp_bits
self.mantissa_bits = mantissa_bits
self.bias = bias
self.mxfp_overflow = mxfp_overflow
self.pos_clamp_value = (1 << (self.exp_bits + self.mantissa_bits)) - 1
self.neg_clamp_value = (1 << (1 + self.exp_bits + self.mantissa_bits)) - 1
# Special cases for e4m3 and e5m2
if self.exp_bits == 4 and self.mantissa_bits == 3:
if self.mxfp_overflow == 'saturate':
self.pos_clamp_value = 0b01111110 # 448
self.neg_clamp_value = 0b11111110 # -448
else:
self.pos_clamp_value = self.neg_clamp_value = 0b11111111 # NaN
if self.exp_bits == 5 and self.mantissa_bits == 2:
if self.mxfp_overflow == 'saturate':
self.pos_clamp_value = 0b01111011 # 57344
self.neg_clamp_value = 0b11111011 # -57344
else:
self.pos_clamp_value = 0b01111100 # +inf
self.neg_clamp_value = 0b11111100 # -inf
# If we calculate these LUTs now it creates a bootstrap problem in generate_luts.py.
self.lut_float16_to_mxfp = None
self.lut_int_to_float = None
def __str__(self):
return f"MXFPFormat(exp_bits={self.exp_bits}, mantissa_bits={self.mantissa_bits}, bias={self.bias}, mxfp_overflow='{self.mxfp_overflow}')"
def decompress_luts(self):
int_to_float_compressed, float16_to_mxfp_compressed = mxfp_luts_compressed[(self.exp_bits, self.mantissa_bits, self.bias, self.mxfp_overflow)]
self.lut_float16_to_mxfp = zlib.decompress(float16_to_mxfp_compressed)
dec = zlib.decompress(int_to_float_compressed)
self.lut_int_to_float = struct.unpack(f'<{len(dec) // 4}f', dec)
def create_luts(self):
self.lut_int_to_float = self.createLUT_for_int_to_float()
self.lut_float16_to_mxfp = self.createLUT_for_float16_to_mxfp()
def float_to_int(self, f: float) -> int:
"""Given a Python float convert to the best mxfp float (expressed as an int) that represents it."""
# First convert the float to a float16, then a 16 bit uint
try:
b = struct.pack('>e', f)
except (OverflowError, struct.error):
# Return the largest representable positive or negative value
return self.pos_clamp_value if f > 0 else self.neg_clamp_value
f16_int = int.from_bytes(b, byteorder='big')
# Then use this as an index to our large LUT
return self.lut_float16_to_mxfp[f16_int]
def slow_float_to_int(self, f: float) -> int:
# Slow, but easier to follow than the faster version.
# The output int has the binary sequence needed for the float.
length = 1 + self.exp_bits + self.mantissa_bits
values = 1 << length
# First get the NaN case out of the way
if math.isnan(f):
if length == 8:
return 0xff # Works for both e5m2 and e4m3
# For smaller lengths, NaN isn't supported so we instead return an invalid value to detect later
return 0xff
# This is so we can distinguish between 0.0 and -0.0
is_positive = math.copysign(1.0, f) == 1.0
if is_positive:
# Positive, so top bit is not set
for i in range(values // 2 - 1):
upper = self.lut_int_to_float[i + 1]
if upper == float('inf'):
break
x = round_to_nearest_ties_to_even(self.lut_int_to_float, i, f)
if x is not None:
return x
return self.pos_clamp_value
else:
# Negative, so top bit is set
for i in range(values // 2, values - 1):
lower = self.lut_int_to_float[i + 1]
if lower == float('-inf'):
break
x = round_to_nearest_ties_to_even(self.lut_int_to_float, i, f)
if x is not None:
return x
# Clip to negative max
return self.neg_clamp_value
def createLUT_for_int_to_float(self) -> array.array:
"""Create a LUT to convert an int in representing a MXFP float into a Python float"""
i2f = []
length = 1 + self.exp_bits + self.mantissa_bits
for i in range(1 << length):
b = bitarray.util.int2ba(i, length=length, endian='big', signed=False)
sign = b[0]
exponent = bitarray.util.ba2int(b[1:1 + self.exp_bits])
significand = b[1 + self.exp_bits:]
if exponent == 0:
significand = bitarray.bitarray('0') + significand
exponent = -self.bias + 1
else:
significand = bitarray.bitarray('1') + significand
exponent -= self.bias
f = float(bitarray.util.ba2int(significand)) / (2.0 ** self.mantissa_bits)
f *= 2 ** exponent
if length == 8:
# Some special cases
if self.exp_bits == 5:
if i in [0b01111100, 0b11111100]:
f = float('inf')
if i in [0b01111101, 0b11111101, 0b01111110, 0b11111110, 0b01111111, 0b11111111]:
f = float('nan')
if self.exp_bits == 4:
if i in [0b01111111, 0b11111111]:
f = float('nan')
i2f.append(f if not sign else -f)
return array.array('f', i2f)
def createLUT_for_float16_to_mxfp(self) -> bytes:
"""Create a LUT to convert a float16 into a MXFP format"""
# Used to create the LUT that was compressed and stored for the fp8 code
length = 1 + self.exp_bits + self.mantissa_bits
if length == 8:
import gfloat
from gfloat.formats import format_info_ocp_e5m2, format_info_ocp_e4m3
fi = format_info_ocp_e5m2 if self.exp_bits == 5 else format_info_ocp_e4m3
fp16_to_fp8 = bytearray(1 << 16)
for i in range(1 << 16):
b = struct.pack('>H', i)
f, = struct.unpack('>e', b)
fp = gfloat.round_float(fi, f, sat=self.mxfp_overflow == 'saturate')
if math.isnan(fp):
fp8_i = 0b11111111
else:
# Special case for negative zero
if fp == 0.0 and math.copysign(1.0, fp) == -1.0:
fp8_i = 0b10000000
else:
fp8_i = self.lut_int_to_float.index(fp)
fp16_to_fp8[i] = fp8_i
return bytes(fp16_to_fp8)
else:
assert length in [4, 6]
fp16_to_fp8 = bytearray(1 << 16)
for i in range(1 << 16):
b = struct.pack('>H', i)
f, = struct.unpack('>e', b)
fp8_i = self.slow_float_to_int(f)
fp16_to_fp8[i] = fp8_i
return bytes(fp16_to_fp8)
e2m1mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=1, bias=1, mxfp_overflow='saturate')
e2m3mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=3, bias=1, mxfp_overflow='saturate')
e3m2mxfp_fmt = MXFPFormat(exp_bits=3, mantissa_bits=2, bias=3, mxfp_overflow='saturate')
e4m3mxfp_saturate_fmt = MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='saturate')
e5m2mxfp_saturate_fmt = MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='saturate')
e4m3mxfp_overflow_fmt = MXFPFormat(exp_bits=4, mantissa_bits=3, bias=7, mxfp_overflow='overflow')
e5m2mxfp_overflow_fmt = MXFPFormat(exp_bits=5, mantissa_bits=2, bias=15, mxfp_overflow='overflow')
def decompress_luts():
e2m1mxfp_fmt.decompress_luts()
e2m3mxfp_fmt.decompress_luts()
e3m2mxfp_fmt.decompress_luts()
e4m3mxfp_saturate_fmt.decompress_luts()
e5m2mxfp_saturate_fmt.decompress_luts()
e4m3mxfp_overflow_fmt.decompress_luts()
e5m2mxfp_overflow_fmt.decompress_luts()

View File

@@ -0,0 +1,238 @@
from __future__ import annotations
import functools
import re
from typing import Tuple, List, Optional, Pattern, Dict, Union, Match
# A token name followed by optional : then an integer number
NAME_INT_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?(\d*)$')
# A token name followed by optional : then an arbitrary keyword
NAME_KWARG_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?([a-zA-Z0-9_]+)$')
CACHE_SIZE = 256
DEFAULT_BITS: Pattern[str] = re.compile(r'^(?P<len>[^=]+)?(=(?P<value>.*))?$', re.IGNORECASE)
MULTIPLICATIVE_RE: Pattern[str] = re.compile(r'^(?P<factor>.*)\*(?P<token>.+)')
# Hex, oct or binary literals
LITERAL_RE: Pattern[str] = re.compile(r'^(?P<name>0([xob]))(?P<value>.+)', re.IGNORECASE)
# An endianness indicator followed by one or more struct.pack codes
STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P<endian>[<>@=])(?P<fmt>(?:\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<endian>[<>@=])?(?P<fmt>(?:\d*[bBhHlLqQefd])+)$')
# An endianness indicator followed by exactly one struct.pack codes
SINGLE_STRUCT_PACK_RE: Pattern[str] = re.compile(r'^(?P<endian>[<>@=])(?P<fmt>[bBhHlLqQefd])$')
# A number followed by a single character struct.pack code
STRUCT_SPLIT_RE: Pattern[str] = re.compile(r'\d*[bBhHlLqQefd]')
# These replicate the struct.pack codes
# Big-endian
REPLACEMENTS_BE: Dict[str, str] = {'b': 'int8', 'B': 'uint8',
'h': 'intbe16', 'H': 'uintbe16',
'l': 'intbe32', 'L': 'uintbe32',
'q': 'intbe64', 'Q': 'uintbe64',
'e': 'floatbe16', 'f': 'floatbe32', 'd': 'floatbe64'}
# Little-endian
REPLACEMENTS_LE: Dict[str, str] = {'b': 'int8', 'B': 'uint8',
'h': 'intle16', 'H': 'uintle16',
'l': 'intle32', 'L': 'uintle32',
'q': 'intle64', 'Q': 'uintle64',
'e': 'floatle16', 'f': 'floatle32', 'd': 'floatle64'}
# Native-endian
REPLACEMENTS_NE: Dict[str, str] = {'b': 'int8', 'B': 'uint8',
'h': 'intne16', 'H': 'uintne16',
'l': 'intne32', 'L': 'uintne32',
'q': 'intne64', 'Q': 'uintne64',
'e': 'floatne16', 'f': 'floatne32', 'd': 'floatne64'}
# Size in bytes of all the pack codes.
PACK_CODE_SIZE: Dict[str, int] = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4, 'L': 4,
'q': 8, 'Q': 8, 'e': 2, 'f': 4, 'd': 8}
def structparser(m: Match[str]) -> List[str]:
"""Parse struct-like format string token into sub-token list."""
endian = m.group('endian')
# Split the format string into a list of 'q', '4h' etc.
formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt'))
# Now deal with multiplicative factors, 4h -> hhhh etc.
fmt = ''.join([f[-1] * int(f[:-1]) if len(f) != 1 else
f for f in formatlist])
if endian in '@=':
# Native endianness
tokens = [REPLACEMENTS_NE[c] for c in fmt]
elif endian == '<':
tokens = [REPLACEMENTS_LE[c] for c in fmt]
else:
assert endian == '>'
tokens = [REPLACEMENTS_BE[c] for c in fmt]
return tokens
@functools.lru_cache(CACHE_SIZE)
def parse_name_length_token(fmt: str, **kwargs) -> Tuple[str, Optional[int]]:
# Any single token with just a name and length
if m2 := NAME_INT_RE.match(fmt):
name = m2.group(1)
length_str = m2.group(2)
length = None if length_str == '' else int(length_str)
else:
# Maybe the length is in the kwargs?
if m := NAME_KWARG_RE.match(fmt):
name = m.group(1)
try:
length_str = kwargs[m.group(2)]
except KeyError:
raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.")
length = int(length_str)
else:
raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.")
return name, length
@functools.lru_cache(CACHE_SIZE)
def parse_single_struct_token(fmt: str) -> Optional[Tuple[str, Optional[int]]]:
if m := SINGLE_STRUCT_PACK_RE.match(fmt):
endian = m.group('endian')
f = m.group('fmt')
if endian == '>':
fmt = REPLACEMENTS_BE[f]
elif endian == '<':
fmt = REPLACEMENTS_LE[f]
else:
assert endian in '=@'
fmt = REPLACEMENTS_NE[f]
return parse_name_length_token(fmt)
else:
return None
@functools.lru_cache(CACHE_SIZE)
def parse_single_token(token: str) -> Tuple[str, str, Optional[str]]:
if (equals_pos := token.find('=')) == -1:
value = None
else:
value = token[equals_pos + 1:]
token = token[:equals_pos]
if m2 := NAME_INT_RE.match(token):
name = m2.group(1)
length_str = m2.group(2)
length = None if length_str == '' else length_str
elif m3 := NAME_KWARG_RE.match(token):
# name then a keyword for a length
name = m3.group(1)
length = m3.group(2)
else:
# If you don't specify a 'name' then the default is 'bits'
name = 'bits'
length = token
return name, length, value
@functools.lru_cache(CACHE_SIZE)
def preprocess_tokens(fmt: str) -> List[str]:
# Remove whitespace and expand brackets
fmt = expand_brackets(''.join(fmt.split()))
# Split tokens by ',' and remove whitespace
# The meta_tokens can either be ordinary single tokens or multiple struct-format token strings.
meta_tokens = [f.strip() for f in fmt.split(',')]
final_tokens = []
for meta_token in meta_tokens:
if meta_token == '':
continue
# Extract factor and actual token if a multiplicative factor exists
factor = 1
if m := MULTIPLICATIVE_RE.match(meta_token):
factor = int(m.group('factor'))
meta_token = m.group('token')
# Parse struct-like format into sub-tokens or treat as single token
tokens = structparser(m) if (m := STRUCT_PACK_RE.match(meta_token)) else [meta_token]
# Extend final tokens list with parsed tokens, repeated by the factor
final_tokens.extend(tokens * factor)
return final_tokens
@functools.lru_cache(CACHE_SIZE)
def tokenparser(fmt: str, keys: Tuple[str, ...] = ()) -> \
Tuple[bool, List[Tuple[str, Union[int, str, None], Optional[str]]]]:
"""Divide the format string into tokens and parse them.
Return stretchy token and list of [initialiser, length, value]
initialiser is one of: hex, oct, bin, uint, int, se, ue, 0x, 0o, 0b etc.
length is None if not known, as is value.
If the token is in the keyword dictionary (keys) then it counts as a
special case and isn't messed with.
tokens must be of the form: [factor*][initialiser][:][length][=value]
"""
tokens = preprocess_tokens(fmt)
stretchy_token = False
ret_vals: List[Tuple[str, Union[str, int, None], Optional[str]]] = []
for token in tokens:
if keys and token in keys:
# Don't bother parsing it, it's a keyword argument
ret_vals.append((token, None, None))
continue
if token == '':
continue
# Match literal tokens of the form 0x... 0o... and 0b...
if m := LITERAL_RE.match(token):
ret_vals.append((m.group('name'), None, m.group('value')))
continue
name, length, value = parse_single_token(token)
if length is None:
stretchy_token = True
if length is not None:
# Try converting length to int, otherwise check it's a key.
try:
length = int(length)
except ValueError:
if not keys or length not in keys:
raise ValueError(f"Don't understand length '{length}' of token.")
ret_vals.append((name, length, value))
return stretchy_token, ret_vals
BRACKET_RE = re.compile(r'(?P<factor>\d+)\*\(')
def expand_brackets(s: str) -> str:
"""Expand all brackets."""
while True:
start = s.find('(')
if start == -1:
break
count = 1 # Number of hanging open brackets
p = start + 1
while p < len(s):
count += (s[p] == '(') - (s[p] == ')')
if count == 0:
break
p += 1
if count != 0:
raise ValueError(f"Unbalanced parenthesis in '{s}'.")
if start == 0 or s[start - 1] != '*':
s = s[0:start] + s[start + 1:p] + s[p + 1:]
else:
# Looks for first number*(
m = BRACKET_RE.search(s)
if m:
factor = int(m.group('factor'))
matchstart = m.start('factor')
s = s[0:matchstart] + (factor - 1) * (s[start + 1:p] + ',') + s[start + 1:p] + s[p + 1:]
else:
raise ValueError(f"Failed to parse '{s}'.")
return s

View File

@@ -0,0 +1,26 @@
Except when otherwise stated (look for LICENSE files in directories or
information at the beginning of each file) all software and
documentation is licensed as follows:
The MIT License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,40 @@
Metadata-Version: 2.1
Name: cffi
Version: 1.17.0
Summary: Foreign Function Interface for Python calling C code.
Home-page: http://cffi.readthedocs.org
Author: Armin Rigo, Maciej Fijalkowski
Author-email: python-cffi@googlegroups.com
License: MIT
Project-URL: Documentation, http://cffi.readthedocs.org/
Project-URL: Source Code, https://github.com/python-cffi/cffi
Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues
Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html
Project-URL: Downloads, https://github.com/python-cffi/cffi/releases
Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: License :: OSI Approved :: MIT License
Requires-Python: >=3.8
License-File: LICENSE
Requires-Dist: pycparser
CFFI
====
Foreign Function Interface for Python calling C code.
Please see the `Documentation <http://cffi.readthedocs.org/>`_.
Contact
-------
`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_

View File

@@ -0,0 +1,48 @@
_cffi_backend.cpython-312-x86_64-linux-gnu.so,sha256=6xKpkTOZLhQwLs-koyNTRWLl0uU2zelLme8BFaQtoAA,1114632
cffi-1.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
cffi-1.17.0.dist-info/LICENSE,sha256=BLgPWwd7vtaICM_rreteNSPyqMmpZJXFh72W3x6sKjM,1294
cffi-1.17.0.dist-info/METADATA,sha256=nQRP9FLzGuTmMppBjZq_FTiwkP3LFhTwwbk-6-_Z9uw,1531
cffi-1.17.0.dist-info/RECORD,,
cffi-1.17.0.dist-info/WHEEL,sha256=3kl6A45eSyE5JSahRrEmKMwp_kGk5NpuqX0WHIJWabQ,151
cffi-1.17.0.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75
cffi-1.17.0.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19
cffi/__init__.py,sha256=5QOG9wcFEENTxG1y_G0_IXDdFCooTVgAy4ZGMFCSpD8,513
cffi/__pycache__/__init__.cpython-312.pyc,,
cffi/__pycache__/_imp_emulation.cpython-312.pyc,,
cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc,,
cffi/__pycache__/api.cpython-312.pyc,,
cffi/__pycache__/backend_ctypes.cpython-312.pyc,,
cffi/__pycache__/cffi_opcode.cpython-312.pyc,,
cffi/__pycache__/commontypes.cpython-312.pyc,,
cffi/__pycache__/cparser.cpython-312.pyc,,
cffi/__pycache__/error.cpython-312.pyc,,
cffi/__pycache__/ffiplatform.cpython-312.pyc,,
cffi/__pycache__/lock.cpython-312.pyc,,
cffi/__pycache__/model.cpython-312.pyc,,
cffi/__pycache__/pkgconfig.cpython-312.pyc,,
cffi/__pycache__/recompiler.cpython-312.pyc,,
cffi/__pycache__/setuptools_ext.cpython-312.pyc,,
cffi/__pycache__/vengine_cpy.cpython-312.pyc,,
cffi/__pycache__/vengine_gen.cpython-312.pyc,,
cffi/__pycache__/verifier.cpython-312.pyc,,
cffi/_cffi_errors.h,sha256=zQXt7uR_m8gUW-fI2hJg0KoSkJFwXv8RGUkEDZ177dQ,3908
cffi/_cffi_include.h,sha256=Exhmgm9qzHWzWivjfTe0D7Xp4rPUkVxdNuwGhMTMzbw,15055
cffi/_embedding.h,sha256=39XMUYv8obSIww-s0MXQt5LoV_U6-BI_Fn0-43DVt0U,18787
cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960
cffi/_shimmed_dist_utils.py,sha256=mLuEtxw4gbuA2De_gD7zEhb6Q8Wm2lBPtwC68gd9XTs,2007
cffi/api.py,sha256=wtJU0aGUC3TyYnjBIgsOIlv7drF19jV-y_srt7c8yhg,42085
cffi/backend_ctypes.py,sha256=h5ZIzLc6BFVXnGyc9xPqZWUS7qGy7yFSDqXe68Sa8z4,42454
cffi/cffi_opcode.py,sha256=JDV5l0R0_OadBX_uE7xPPTYtMdmpp8I9UYd6av7aiDU,5731
cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805
cffi/cparser.py,sha256=0qI3mEzZSNVcCangoyXOoAcL-RhpQL08eG8798T024s,44789
cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877
cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584
cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747
cffi/model.py,sha256=W30UFQZE73jL5Mx5N81YT77us2W2iJjTm0XYfnwz1cg,21797
cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976
cffi/pkgconfig.py,sha256=LP1w7vmWvmKwyqLaU1Z243FOWGNQMrgMUZrvgFuOlco,4374
cffi/recompiler.py,sha256=V-DEjnxaPxMUIdkzrJutW7Is8s_T2pB6Henq6Aud4FQ,64607
cffi/setuptools_ext.py,sha256=-ebj79lO2_AUH-kRcaja2pKY1Z_5tloGwsJgzK8P3Cc,8871
cffi/vengine_cpy.py,sha256=8UagT6ZEOZf6Dju7_CfNulue8CnsHLEzJYhnqUhoF04,43752
cffi/vengine_gen.py,sha256=DUlEIrDiVin1Pnhn1sfoamnS5NLqfJcOdhRoeSNeJRg,26939
cffi/verifier.py,sha256=oX8jpaohg2Qm3aHcznidAdvrVm5N4sQYG0a3Eo5mIl4,11182

View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: setuptools (72.1.0)
Root-Is-Purelib: false
Tag: cp312-cp312-manylinux_2_17_x86_64
Tag: cp312-cp312-manylinux2014_x86_64

View File

@@ -0,0 +1,2 @@
[distutils.setup_keywords]
cffi_modules = cffi.setuptools_ext:cffi_modules

View File

@@ -0,0 +1,2 @@
_cffi_backend
cffi

View File

@@ -0,0 +1,14 @@
__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError',
'FFIError']
from .api import FFI
from .error import CDefError, FFIError, VerificationError, VerificationMissing
from .error import PkgConfigError
__version__ = "1.17.0"
__version_info__ = (1, 17, 0)
# The verifier module file names are based on the CRC32 of a string that
# contains the following version number. It may be older than __version__
# if nothing is clearly incompatible.
__version_verifier_modules__ = "0.8.6"

Some files were not shown because too many files have changed in this diff Show More