././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9104946 bitstruct-8.20.0/0000755000175100001660000000000014756336142013242 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/LICENSE0000644000175100001660000000207514756336136014256 0ustar00runnerdockerThe MIT License (MIT) Copyright (c) 2015-2019 Erik Moqvist 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/MANIFEST.in0000644000175100001660000000036314756336136015005 0ustar00runnerdockerinclude LICENSE include Makefile include src/bitstruct/c.c include src/bitstruct/bitstream.[hc] recursive-include docs *.bat recursive-include docs *.py recursive-include docs *.rst recursive-include docs Makefile recursive-include tests *.py ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/Makefile0000644000175100001660000000007014756336136014702 0ustar00runnerdockertest: python3 -m pip install -e . python3 -m unittest ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9104946 bitstruct-8.20.0/PKG-INFO0000644000175100001660000001627714756336142014354 0ustar00runnerdockerMetadata-Version: 2.2 Name: bitstruct Version: 8.20.0 Summary: This module performs conversions between Python values and C bit field structs represented as Python byte strings. Author: Ilya Petukhov Author-email: Erik Moqvist License: MIT Project-URL: Documentation, https://bitstruct.readthedocs.io/en/latest/ Project-URL: Issues, https://github.com/eerimoq/bitstruct/issues Project-URL: Source, https://github.com/eerimoq/bitstruct Project-URL: Homepage, https://github.com/eerimoq/bitstruct Keywords: bit field,bit parsing,bit unpack,bit pack Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.7 Description-Content-Type: text/x-rst License-File: LICENSE About ===== This module is intended to have a similar interface as the python struct module, but working on bits instead of primitive data types (char, int, ...). Project homepage: https://github.com/eerimoq/bitstruct Documentation: https://bitstruct.readthedocs.io Installation ============ .. code-block:: python pip install bitstruct Performance =========== Parts of this package has been re-implemented in C for faster pack and unpack operations. There are two independent C implementations; `bitstruct.c`, which is part of this package, and the standalone package `cbitstruct`_. These implementations are only available in CPython 3, and must be explicitly imported. By default the pure Python implementation is used. To use `bitstruct.c`, do ``import bitstruct.c as bitstruct``. To use `cbitstruct`_, do ``import cbitstruct as bitstruct``. `bitstruct.c` has a few limitations compared to the pure Python implementation: - Integers and booleans must be 64 bits or less. - Text and raw must be a multiple of 8 bits. - Bit endianness and byte order are not yet supported. - ``byteswap()`` can only swap 1, 2, 4 and 8 bytes. See `cbitstruct`_ for its limitations. MicroPython =========== The C implementation has been ported to `MicroPython`_. See `bitstruct-micropython`_ for more details. Example usage ============= A basic example of `packing`_ and `unpacking`_ four integers using the format string ``'u1u3u4s16'``: .. code-block:: python >>> from bitstruct import * >>> pack('u1u3u4s16', 1, 2, 3, -4) b'\xa3\xff\xfc' >>> unpack('u1u3u4s16', b'\xa3\xff\xfc') (1, 2, 3, -4) >>> calcsize('u1u3u4s16') 24 An example `compiling`_ the format string once, and use it to `pack`_ and `unpack`_ data: .. code-block:: python >>> import bitstruct >>> cf = bitstruct.compile('u1u3u4s16') >>> cf.pack(1, 2, 3, -4) b'\xa3\xff\xfc' >>> cf.unpack(b'\xa3\xff\xfc') (1, 2, 3, -4) Use the `pack into`_ and `unpack from`_ functions to pack/unpack values at a bit offset into the data, in this example the bit offset is 5: .. code-block:: python >>> from bitstruct import * >>> data = bytearray(b'\x00\x00\x00\x00') >>> pack_into('u1u3u4s16', data, 5, 1, 2, 3, -4) >>> data bytearray(b'\x05\x1f\xff\xe0') >>> unpack_from('u1u3u4s16', data, 5) (1, 2, 3, -4) The unpacked values can be named by assigning them to variables or by wrapping the result in a named tuple: .. code-block:: python >>> from bitstruct import * >>> from collections import namedtuple >>> MyName = namedtuple('myname', ['a', 'b', 'c', 'd']) >>> unpacked = unpack('u1u3u4s16', b'\xa3\xff\xfc') >>> myname = MyName(*unpacked) >>> myname myname(a=1, b=2, c=3, d=-4) >>> myname.c 3 Use the `pack_dict`_ and `unpack_dict`_ functions to pack/unpack values in dictionaries: .. code-block:: python >>> from bitstruct import * >>> names = ['a', 'b', 'c', 'd'] >>> pack_dict('u1u3u4s16', names, {'a': 1, 'b': 2, 'c': 3, 'd': -4}) b'\xa3\xff\xfc' >>> unpack_dict('u1u3u4s16', names, b'\xa3\xff\xfc') {'a': 1, 'b': 2, 'c': 3, 'd': -4} An example of `packing`_ and `unpacking`_ an unsigned integer, a signed integer, a float, a boolean, a byte string and a string: .. code-block:: python >>> from bitstruct import * >>> pack('u5s5f32b1r13t40', 1, -1, 3.75, True, b'\xff\xff', 'hello') b'\x0f\xd0\x1c\x00\x00?\xffhello' >>> unpack('u5s5f32b1r13t40', b'\x0f\xd0\x1c\x00\x00?\xffhello') (1, -1, 3.75, True, b'\xff\xf8', 'hello') >>> calcsize('u5s5f32b1r13t40') 96 The same format string and values as in the previous example, but using LSB (Least Significant Bit) first instead of the default MSB (Most Significant Bit) first: .. code-block:: python >>> from bitstruct import * >>> pack('>> unpack('>> calcsize('>> from bitstruct import * >>> from binascii import unhexlify >>> unpack('s17s13r24', unhexlify('0123456789abcdef')) (582, -3751, b'\xe2j\xf3') >>> with open("test.bin", "rb") as fin: ... unpack('s17s13r24', fin.read(8)) ... ... (582, -3751, b'\xe2j\xf3') Change endianness of the data with `byteswap`_, and then unpack the values: .. code-block:: python >>> from bitstruct import * >>> packed = pack('u1u3u4s16', 1, 2, 3, 1) >>> unpack('u1u3u4s16', byteswap('12', packed)) (1, 2, 3, 256) A basic example of `packing`_ and `unpacking`_ four integers using the format string ``'u1u3u4s16'`` using the C implementation: .. code-block:: python >>> from bitstruct.c import * >>> pack('u1u3u4s16', 1, 2, 3, -4) b'\xa3\xff\xfc' >>> unpack('u1u3u4s16', b'\xa3\xff\xfc') (1, 2, 3, -4) Contributing ============ #. Fork the repository. #. Install prerequisites. .. code-block:: text pip install -r requirements.txt #. Implement the new feature or bug fix. #. Implement test case(s) to ensure that future changes do not break legacy. #. Run the tests. .. code-block:: text make test #. Create a pull request. .. _packing: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack .. _unpacking: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack .. _pack: http://bitstruct.readthedocs.io/en/latest/#bitstruct.CompiledFormat.pack .. _unpack: http://bitstruct.readthedocs.io/en/latest/#bitstruct.CompiledFormat.unpack .. _pack into: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack_into .. _unpack from: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack_from .. _pack_dict: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack_dict .. _unpack_dict: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack_dict .. _byteswap: http://bitstruct.readthedocs.io/en/latest/#bitstruct.byteswap .. _compiling: http://bitstruct.readthedocs.io/en/latest/#bitstruct.compile .. _cbitstruct: https://github.com/qchateau/cbitstruct .. _MicroPython: https://github.com/micropython/micropython .. _bitstruct-micropython: https://github.com/peterzuger/bitstruct-micropython ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/README.rst0000644000175100001660000001444214756336136014741 0ustar00runnerdockerAbout ===== This module is intended to have a similar interface as the python struct module, but working on bits instead of primitive data types (char, int, ...). Project homepage: https://github.com/eerimoq/bitstruct Documentation: https://bitstruct.readthedocs.io Installation ============ .. code-block:: python pip install bitstruct Performance =========== Parts of this package has been re-implemented in C for faster pack and unpack operations. There are two independent C implementations; `bitstruct.c`, which is part of this package, and the standalone package `cbitstruct`_. These implementations are only available in CPython 3, and must be explicitly imported. By default the pure Python implementation is used. To use `bitstruct.c`, do ``import bitstruct.c as bitstruct``. To use `cbitstruct`_, do ``import cbitstruct as bitstruct``. `bitstruct.c` has a few limitations compared to the pure Python implementation: - Integers and booleans must be 64 bits or less. - Text and raw must be a multiple of 8 bits. - Bit endianness and byte order are not yet supported. - ``byteswap()`` can only swap 1, 2, 4 and 8 bytes. See `cbitstruct`_ for its limitations. MicroPython =========== The C implementation has been ported to `MicroPython`_. See `bitstruct-micropython`_ for more details. Example usage ============= A basic example of `packing`_ and `unpacking`_ four integers using the format string ``'u1u3u4s16'``: .. code-block:: python >>> from bitstruct import * >>> pack('u1u3u4s16', 1, 2, 3, -4) b'\xa3\xff\xfc' >>> unpack('u1u3u4s16', b'\xa3\xff\xfc') (1, 2, 3, -4) >>> calcsize('u1u3u4s16') 24 An example `compiling`_ the format string once, and use it to `pack`_ and `unpack`_ data: .. code-block:: python >>> import bitstruct >>> cf = bitstruct.compile('u1u3u4s16') >>> cf.pack(1, 2, 3, -4) b'\xa3\xff\xfc' >>> cf.unpack(b'\xa3\xff\xfc') (1, 2, 3, -4) Use the `pack into`_ and `unpack from`_ functions to pack/unpack values at a bit offset into the data, in this example the bit offset is 5: .. code-block:: python >>> from bitstruct import * >>> data = bytearray(b'\x00\x00\x00\x00') >>> pack_into('u1u3u4s16', data, 5, 1, 2, 3, -4) >>> data bytearray(b'\x05\x1f\xff\xe0') >>> unpack_from('u1u3u4s16', data, 5) (1, 2, 3, -4) The unpacked values can be named by assigning them to variables or by wrapping the result in a named tuple: .. code-block:: python >>> from bitstruct import * >>> from collections import namedtuple >>> MyName = namedtuple('myname', ['a', 'b', 'c', 'd']) >>> unpacked = unpack('u1u3u4s16', b'\xa3\xff\xfc') >>> myname = MyName(*unpacked) >>> myname myname(a=1, b=2, c=3, d=-4) >>> myname.c 3 Use the `pack_dict`_ and `unpack_dict`_ functions to pack/unpack values in dictionaries: .. code-block:: python >>> from bitstruct import * >>> names = ['a', 'b', 'c', 'd'] >>> pack_dict('u1u3u4s16', names, {'a': 1, 'b': 2, 'c': 3, 'd': -4}) b'\xa3\xff\xfc' >>> unpack_dict('u1u3u4s16', names, b'\xa3\xff\xfc') {'a': 1, 'b': 2, 'c': 3, 'd': -4} An example of `packing`_ and `unpacking`_ an unsigned integer, a signed integer, a float, a boolean, a byte string and a string: .. code-block:: python >>> from bitstruct import * >>> pack('u5s5f32b1r13t40', 1, -1, 3.75, True, b'\xff\xff', 'hello') b'\x0f\xd0\x1c\x00\x00?\xffhello' >>> unpack('u5s5f32b1r13t40', b'\x0f\xd0\x1c\x00\x00?\xffhello') (1, -1, 3.75, True, b'\xff\xf8', 'hello') >>> calcsize('u5s5f32b1r13t40') 96 The same format string and values as in the previous example, but using LSB (Least Significant Bit) first instead of the default MSB (Most Significant Bit) first: .. code-block:: python >>> from bitstruct import * >>> pack('>> unpack('>> calcsize('>> from bitstruct import * >>> from binascii import unhexlify >>> unpack('s17s13r24', unhexlify('0123456789abcdef')) (582, -3751, b'\xe2j\xf3') >>> with open("test.bin", "rb") as fin: ... unpack('s17s13r24', fin.read(8)) ... ... (582, -3751, b'\xe2j\xf3') Change endianness of the data with `byteswap`_, and then unpack the values: .. code-block:: python >>> from bitstruct import * >>> packed = pack('u1u3u4s16', 1, 2, 3, 1) >>> unpack('u1u3u4s16', byteswap('12', packed)) (1, 2, 3, 256) A basic example of `packing`_ and `unpacking`_ four integers using the format string ``'u1u3u4s16'`` using the C implementation: .. code-block:: python >>> from bitstruct.c import * >>> pack('u1u3u4s16', 1, 2, 3, -4) b'\xa3\xff\xfc' >>> unpack('u1u3u4s16', b'\xa3\xff\xfc') (1, 2, 3, -4) Contributing ============ #. Fork the repository. #. Install prerequisites. .. code-block:: text pip install -r requirements.txt #. Implement the new feature or bug fix. #. Implement test case(s) to ensure that future changes do not break legacy. #. Run the tests. .. code-block:: text make test #. Create a pull request. .. _packing: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack .. _unpacking: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack .. _pack: http://bitstruct.readthedocs.io/en/latest/#bitstruct.CompiledFormat.pack .. _unpack: http://bitstruct.readthedocs.io/en/latest/#bitstruct.CompiledFormat.unpack .. _pack into: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack_into .. _unpack from: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack_from .. _pack_dict: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack_dict .. _unpack_dict: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack_dict .. _byteswap: http://bitstruct.readthedocs.io/en/latest/#bitstruct.byteswap .. _compiling: http://bitstruct.readthedocs.io/en/latest/#bitstruct.compile .. _cbitstruct: https://github.com/qchateau/cbitstruct .. _MicroPython: https://github.com/micropython/micropython .. _bitstruct-micropython: https://github.com/peterzuger/bitstruct-micropython ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9084947 bitstruct-8.20.0/docs/0000755000175100001660000000000014756336142014172 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/docs/Makefile0000644000175100001660000001637514756336136015651 0ustar00runnerdocker# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/bitstruct.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/bitstruct.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/bitstruct" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/bitstruct" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/docs/conf.py0000644000175100001660000002212114756336136015472 0ustar00runnerdocker# -*- coding: utf-8 -*- # # bitstruct documentation build configuration file, created by # sphinx-quickstart on Sat Apr 25 11:54:09 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) import bitstruct # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'bitstruct' copyright = u'2015-2019, Erik Moqvist' author = u'Erik Moqvist' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = bitstruct.__version__ # The full version, including alpha/beta/rc tags. release = bitstruct.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'bitstructdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'bitstruct.tex', u'bitstruct Documentation', u'Erik Moqvist', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'bitstruct', u'bitstruct Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'bitstruct', u'bitstruct Documentation', author, 'bitstruct', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/docs/index.rst0000644000175100001660000000252714756336136016044 0ustar00runnerdocker.. bitstruct documentation master file, created by sphinx-quickstart on Sat Apr 25 11:54:09 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. toctree:: :maxdepth: 2 bitstruct - Interpret strings as packed binary data =================================================== .. include:: ../README.rst Functions ========= .. autofunction:: bitstruct.pack .. autofunction:: bitstruct.unpack .. autofunction:: bitstruct.pack_into .. autofunction:: bitstruct.unpack_from .. autofunction:: bitstruct.pack_dict .. autofunction:: bitstruct.unpack_dict .. autofunction:: bitstruct.pack_into_dict .. autofunction:: bitstruct.unpack_from_dict .. autofunction:: bitstruct.calcsize .. autofunction:: bitstruct.byteswap .. autofunction:: bitstruct.compile Classes ======= .. autoclass:: bitstruct.CompiledFormat .. automethod:: bitstruct.CompiledFormat.pack .. automethod:: bitstruct.CompiledFormat.unpack .. automethod:: bitstruct.CompiledFormat.pack_into .. automethod:: bitstruct.CompiledFormat.unpack_from .. autoclass:: bitstruct.CompiledFormatDict .. automethod:: bitstruct.CompiledFormatDict.pack .. automethod:: bitstruct.CompiledFormatDict.unpack .. automethod:: bitstruct.CompiledFormatDict.pack_into .. automethod:: bitstruct.CompiledFormatDict.unpack_from ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/docs/make.bat0000644000175100001660000001612214756336136015604 0ustar00runnerdocker@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 2> nul if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\bitstruct.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\bitstruct.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/pyproject.toml0000644000175100001660000000255414756336136016167 0ustar00runnerdocker[build-system] build-backend = 'setuptools.build_meta' requires = ["setuptools >= 68.0"] [project] name = "bitstruct" description = """This module performs conversions between Python values \ and C bit field structs represented as Python \ byte strings.""" readme = "README.rst" requires-python = ">=3.7" license = { text = "MIT" } keywords = [ "bit field", "bit parsing", "bit unpack", "bit pack", ] authors = [ { name = "Erik Moqvist", email = "erik.moqvist@gmail.com" }, { name = "Ilya Petukhov" }, ] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", ] dependencies = [] dynamic = ["version"] [project.urls] Documentation = "https://bitstruct.readthedocs.io/en/latest/" Issues = "https://github.com/eerimoq/bitstruct/issues" Source = "https://github.com/eerimoq/bitstruct" Homepage = "https://github.com/eerimoq/bitstruct" [tool.setuptools.packages.find] where = ["src"] [tool.setuptools.dynamic] version = {attr = "bitstruct.__version__"} [tool.setuptools.package-data] "*" = [ "**/py.typed", "**.pyi", ] [tool.cibuildwheel] test-requires = "pytest" test-command = "pytest -v --assert=plain {project}/tests" build-frontend = "build" skip = "pp*" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9104946 bitstruct-8.20.0/setup.cfg0000644000175100001660000000004614756336142015063 0ustar00runnerdocker[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/setup.py0000755000175100001660000000061514756336136014764 0ustar00runnerdocker#!/usr/bin/env python import platform import setuptools if platform.python_implementation() == "CPython": ext_modules = [ setuptools.Extension( "bitstruct.c", sources=[ "src/bitstruct/c.c", "src/bitstruct/bitstream.c", ], ) ] else: ext_modules = [] setuptools.setup( ext_modules=ext_modules, ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9064946 bitstruct-8.20.0/src/0000755000175100001660000000000014756336142014031 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9084947 bitstruct-8.20.0/src/bitstruct/0000755000175100001660000000000014756336142016054 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/src/bitstruct/__init__.py0000644000175100001660000004730614756336136020202 0ustar00runnerdocker__version__ = '8.20.0' import binascii import re import struct from io import BytesIO class Error(Exception): pass class _Info: def __init__(self, size, name=None): self.size = size self.name = name self.endianness = None class _SignedInteger(_Info): def __init__(self, size, name): super().__init__(size, name) self.minimum = -2 ** (size - 1) self.maximum = -self.minimum - 1 def pack(self, arg): value = int(arg) if value < self.minimum or value > self.maximum: raise Error( '"s{}" requires {} <= integer <= {} (got {})'.format( self.size, self.minimum, self.maximum, arg)) if value < 0: value += (1 << self.size) value += (1 << self.size) return bin(value)[3:] def unpack(self, bits): value = int(bits, 2) if bits[0] == '1': value -= (1 << len(bits)) return value class _UnsignedInteger(_Info): def __init__(self, size, name): super().__init__(size, name) self.maximum = 2 ** size - 1 def pack(self, arg): value = int(arg) if value < 0 or value > self.maximum: raise Error( f'"u{self.size}" requires 0 <= integer <= {self.maximum} (got {arg})') return bin(value + (1 << self.size))[3:] def unpack(self, bits): return int(bits, 2) class _Boolean(_UnsignedInteger): def pack(self, arg): return super().pack(int(bool(arg))) def unpack(self, bits): return bool(super().unpack(bits)) class _Float(_Info): def pack(self, arg): value = float(arg) if self.size == 16: value = struct.pack('>e', value) elif self.size == 32: value = struct.pack('>f', value) elif self.size == 64: value = struct.pack('>d', value) else: raise Error(f'expected float size of 16, 32, or 64 bits (got {self.size})') return bin(int(b'01' + binascii.hexlify(value), 16))[3:] def unpack(self, bits): packed = _unpack_bytearray(self.size, bits) if self.size == 16: value = struct.unpack('>e', packed)[0] elif self.size == 32: value = struct.unpack('>f', packed)[0] elif self.size == 64: value = struct.unpack('>d', packed)[0] else: raise Error(f'expected float size of 16, 32, or 64 bits (got {self.size})') return value class _Raw(_Info): def pack(self, arg): number_of_padding_bytes = ((self.size - 8 * len(arg)) // 8) arg += (number_of_padding_bytes * b'\x00') return bin(int(b'01' + binascii.hexlify(arg), 16))[3:self.size + 3] def unpack(self, bits): rest = self.size % 8 if rest > 0: bits += (8 - rest) * '0' return binascii.unhexlify(hex(int('10000000' + bits, 2))[4:].rstrip('L')) class _Padding(_Info): pass class _ZeroPadding(_Padding): def pack(self): return self.size * '0' class _OnePadding(_Padding): def pack(self): return self.size * '1' class _Text(_Info): def __init__(self, size, name, encoding, errors): super().__init__(size, name) self.encoding = encoding self.errors = errors def pack(self, arg): encoded = arg.encode('utf-8') number_of_padding_bytes = ((self.size - 8 * len(encoded)) // 8) encoded += (number_of_padding_bytes * b'\x00') return _pack_bytearray(self.size, encoded) def unpack(self, bits): return _unpack_bytearray(self.size, bits).decode(self.encoding, self.errors) def _parse_format(fmt, names, text_encoding, text_errors): if fmt and fmt[-1] in '><': byte_order = fmt[-1] fmt = fmt[:-1] else: byte_order = '' parsed_infos = re.findall(r'([<>]?)([a-zA-Z])(\d+)(\s*)', fmt) if ''.join([''.join(info) for info in parsed_infos]) != fmt: raise Error(f"bad format '{fmt + byte_order}'") # Use big endian as default and use the endianness of the previous # value if none is given for the current value. infos = [] endianness = ">" i = 0 for parsed_info in parsed_infos: if parsed_info[0] != "": endianness = parsed_info[0] type_ = parsed_info[1] size = int(parsed_info[2]) if size == 0: raise Error(f"bad format '{fmt + byte_order}'") if names is None: name = i elif type_ not in 'pP': name = names[i] if type_ == 's': info = _SignedInteger(size, name) i += 1 elif type_ == 'u': info = _UnsignedInteger(size, name) i += 1 elif type_ == 'f': info = _Float(size, name) i += 1 elif type_ == 'b': info = _Boolean(size, name) i += 1 elif type_ == 't': info = _Text(size, name, text_encoding, text_errors) i += 1 elif type_ == 'r': info = _Raw(size, name) i += 1 elif type_ == 'p': info = _ZeroPadding(size) elif type_ == 'P': info = _OnePadding(size) else: raise Error(f"bad char '{type_}' in format") info.endianness = endianness infos.append(info) return infos, byte_order or '>' def _pack_bytearray(size, arg): return bin(int(b'01' + binascii.hexlify(arg), 16))[3:size + 3] def _unpack_bytearray(size, bits): rest = size % 8 if rest > 0: bits += (8 - rest) * '0' return binascii.unhexlify(hex(int('10000000' + bits, 2))[4:].rstrip('L')) class _CompiledFormat: def __init__(self, fmt, names=None, text_encoding='utf-8', text_errors='strict'): infos, byte_order = _parse_format(fmt, names, text_encoding, text_errors) self._infos = infos self._byte_order = byte_order self._number_of_bits_to_unpack = sum([info.size for info in infos]) def pack_value(self, info, value, bits): value_bits = info.pack(value) # Reverse the bit order in little endian values. if info.endianness == "<": value_bits = value_bits[::-1] # Reverse bytes order for least significant byte first. if self._byte_order == ">" or isinstance(info, (_Raw, _Text)): bits += value_bits else: aligned_offset = len(value_bits) - (8 - (len(bits) % 8)) while aligned_offset > 0: bits += value_bits[aligned_offset:] value_bits = value_bits[:aligned_offset] aligned_offset -= 8 bits += value_bits return bits def pack_any(self, values): bits = '' for info in self._infos: if isinstance(info, _Padding): bits += info.pack() else: bits = self.pack_value(info, values[info.name], bits) # Padding of last byte. tail = len(bits) % 8 if tail != 0: bits += (8 - tail) * '0' return bytes(_unpack_bytearray(len(bits), bits)) def unpack_from_any(self, data, offset, allow_truncated): bits = bin(int(b'01' + binascii.hexlify(data), 16))[3 + offset:] # Sanity check. if not allow_truncated and self._number_of_bits_to_unpack > len(bits): raise Error( "unpack requires at least {} bits to unpack (got {})".format( self._number_of_bits_to_unpack, len(bits))) offset = 0 for info in self._infos: if offset + info.size > len(bits): # Stop unpacking if we ran out of bytes to # unpack. Note that this condition will never trigger # if `allow_truncated` is not `True` because of the # sanity check above. return if isinstance(info, _Padding): pass else: # Reverse bytes order for least significant byte # first. if self._byte_order == ">" or isinstance(info, (_Raw, _Text)): value_bits = bits[offset:offset + info.size] else: value_bits_tmp = bits[offset:offset + info.size] aligned_offset = (info.size - ((offset + info.size) % 8)) value_bits = '' while aligned_offset > 0: value_bits += value_bits_tmp[aligned_offset:aligned_offset + 8] value_bits_tmp = value_bits_tmp[:aligned_offset] aligned_offset -= 8 value_bits += value_bits_tmp # Reverse the bit order in little endian values. if info.endianness == "<": value_bits = value_bits[::-1] yield info, info.unpack(value_bits) offset += info.size def pack_into_any(self, buf, offset, data, **kwargs): fill_padding = kwargs.get('fill_padding', True) buf_bits = _pack_bytearray(8 * len(buf), buf) bits = buf_bits[0:offset] for info in self._infos: if isinstance(info, _Padding): if fill_padding: bits += info.pack() else: bits += buf_bits[len(bits):len(bits) + info.size] else: bits = self.pack_value(info, data[info.name], bits) bits += buf_bits[len(bits):] if len(bits) > len(buf_bits): raise Error( f'pack_into requires a buffer of at least {len(bits)} bits') buf[:] = _unpack_bytearray(len(bits), bits) def calcsize(self): """Return the number of bits in the compiled format string. """ return self._number_of_bits_to_unpack class CompiledFormat(_CompiledFormat): """A compiled format string that can be used to pack and/or unpack data multiple times. Instances of this class are created by the factory function :func:`~bitstruct.compile()`. """ def __init__(self, fmt, text_encoding='utf-8', text_errors='strict'): super().__init__(fmt, None, text_encoding, text_errors) self._number_of_arguments = 0 for info in self._infos: if not isinstance(info, _Padding): self._number_of_arguments += 1 def pack(self, *args): """See :func:`~bitstruct.pack()`. """ # Sanity check of the number of arguments. if len(args) < self._number_of_arguments: raise Error( "pack expected {} item(s) for packing (got {})".format( self._number_of_arguments, len(args))) return self.pack_any(args) def unpack(self, data, allow_truncated=False): """See :func:`~bitstruct.unpack()`. """ return self.unpack_from(data, allow_truncated=allow_truncated) def pack_into(self, buf, offset, *args, **kwargs): """See :func:`~bitstruct.pack_into()`. """ # Sanity check of the number of arguments. if len(args) < self._number_of_arguments: raise Error( "pack expected {} item(s) for packing (got {})".format( self._number_of_arguments, len(args))) self.pack_into_any(buf, offset, args, **kwargs) def unpack_from(self, data, offset=0, allow_truncated=False): """See :func:`~bitstruct.unpack_from()`. """ return tuple([v[1] for v in self.unpack_from_any( data, offset, allow_truncated=allow_truncated)]) class CompiledFormatDict(_CompiledFormat): """See :class:`~bitstruct.CompiledFormat`. """ def pack(self, data): """See :func:`~bitstruct.pack_dict()`. """ try: return self.pack_any(data) except KeyError as e: raise Error(f'{str(e)} not found in data dictionary') def unpack(self, data, allow_truncated=False): """See :func:`~bitstruct.unpack_dict()`. """ return self.unpack_from(data, allow_truncated=allow_truncated) def pack_into(self, buf, offset, data, **kwargs): """See :func:`~bitstruct.pack_into_dict()`. """ try: self.pack_into_any(buf, offset, data, **kwargs) except KeyError as e: raise Error(f'{str(e)} not found in data dictionary') def unpack_from(self, data, offset=0, allow_truncated=False): """See :func:`~bitstruct.unpack_from_dict()`. """ return { info.name: v for info, v in self.unpack_from_any( data, offset, allow_truncated=allow_truncated)} def pack(fmt, *args): """Return a bytes object containing the values v1, v2, ... packed according to given format string `fmt`. If the total number of bits are not a multiple of 8, padding will be added at the end of the last byte. `fmt` is a string of bit order-type-length groups, and optionally a byte order identifier after the groups. Bit Order and byte order may be omitted. Bit Order is either ``>`` or ``<``, where ``>`` means MSB first and ``<`` means LSB first. If bit order is omitted, the previous values' bit order is used for the current value. For example, in the format string ``'u1`` or ``<``, where ``>`` means most significant byte first and ``<`` means least significant byte first. If byte order is omitted, most significant byte first is used. There are eight types; ``u``, ``s``, ``f``, ``b``, ``t``, ``r``, ``p`` and ``P``. - ``u`` -- unsigned integer - ``s`` -- signed integer - ``f`` -- floating point number of 16, 32, or 64 bits - ``b`` -- boolean - ``t`` -- text (ascii or utf-8) - ``r`` -- raw, bytes - ``p`` -- padding with zeros, ignore - ``P`` -- padding with ones, ignore Length is the number of bits to pack the value into. Example format string with default bit and byte ordering: ``'u1u3p7s16'`` Same format string, but with least significant byte first: ``'u1u3p7s16<'`` Same format string, but with LSB first (``<`` prefix) and least significant byte first (``<`` suffix): ``'>> pack_dict('u4u4', ['foo', 'bar'], {'foo': 1, 'bar': 2}) b'\\x12' """ return CompiledFormatDict(fmt, names).pack(data) def unpack_dict(fmt, names, data, allow_truncated=False, text_encoding='utf-8', text_errors='strict'): """Same as :func:`~bitstruct.unpack()`, but returns a dictionary. See :func:`~bitstruct.pack_dict()` for details on `names`. >>> unpack_dict('u4u4', ['foo', 'bar'], b'\\x12') {'foo': 1, 'bar': 2} """ return CompiledFormatDict(fmt, names, text_encoding, text_errors).unpack( data, allow_truncated=allow_truncated) def pack_into_dict(fmt, names, buf, offset, data, **kwargs): """Same as :func:`~bitstruct.pack_into()`, but data is read from a dictionary. See :func:`~bitstruct.pack_dict()` for details on `names`. """ return CompiledFormatDict(fmt, names).pack_into(buf, offset, data, **kwargs) def unpack_from_dict(fmt, names, data, offset=0, allow_truncated=False, text_encoding='utf-8', text_errors='strict'): """Same as :func:`~bitstruct.unpack_from()`, but returns a dictionary. See :func:`~bitstruct.pack_dict()` for details on `names`. """ return CompiledFormatDict(fmt, names, text_encoding, text_errors).unpack_from( data, offset, allow_truncated=allow_truncated) def calcsize(fmt): """Return the number of bits in given format string `fmt`. >>> calcsize('u1s3p4') 8 """ return CompiledFormat(fmt).calcsize() def byteswap(fmt, data, offset=0): """Swap bytes in `data` according to `fmt`, starting at byte `offset` and return the result. `fmt` must be an iterable, iterating over number of bytes to swap. For example, the format string ``'24'`` applied to the bytes ``b'\\x00\\x11\\x22\\x33\\x44\\x55'`` will produce the result ``b'\\x11\\x00\\x55\\x44\\x33\\x22'``. """ data = BytesIO(data) data.seek(offset) data_swapped = BytesIO() for f in fmt: swapped = data.read(int(f))[::-1] data_swapped.write(swapped) return data_swapped.getvalue() def compile(fmt, names=None, text_encoding='utf-8', text_errors='strict'): """Compile given format string `fmt` and return a compiled format object that can be used to pack and/or unpack data multiple times. Returns a :class:`~bitstruct.CompiledFormat` object if `names` is ``None``, and otherwise a :class:`~bitstruct.CompiledFormatDict` object. See :func:`~bitstruct.pack_dict()` for details on `names`. See :func:`~bitstruct.unpack()` for details on `text_encoding` and `text_errors`. """ if names is None: return CompiledFormat(fmt, text_encoding, text_errors) else: return CompiledFormatDict(fmt, names, text_encoding, text_errors) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/src/bitstruct/bitstream.c0000644000175100001660000004211614756336136020221 0ustar00runnerdocker/** * The MIT License (MIT) * * Copyright (c) 2019 Erik Moqvist * * 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. */ #include #include "bitstream.h" void bitstream_writer_init(struct bitstream_writer_t *self_p, uint8_t *buf_p) { self_p->buf_p = buf_p; self_p->byte_offset = 0; self_p->bit_offset = 0; } int bitstream_writer_size_in_bits(struct bitstream_writer_t *self_p) { return (8 * self_p->byte_offset + self_p->bit_offset); } int bitstream_writer_size_in_bytes(struct bitstream_writer_t *self_p) { return (self_p->byte_offset + (self_p->bit_offset + 7) / 8); } void bitstream_writer_write_bit(struct bitstream_writer_t *self_p, int value) { if (self_p->bit_offset == 0) { self_p->buf_p[self_p->byte_offset] = (value << 7); self_p->bit_offset = 1; } else { self_p->buf_p[self_p->byte_offset] |= (value << (8 - self_p->bit_offset - 1)); if (self_p->bit_offset == 7) { self_p->bit_offset = 0; self_p->byte_offset++; } else { self_p->bit_offset++; } } } void bitstream_writer_write_bytes(struct bitstream_writer_t *self_p, const uint8_t *buf_p, int length) { int i; uint8_t *dst_p; dst_p = &self_p->buf_p[self_p->byte_offset]; if (self_p->bit_offset == 0) { memcpy(dst_p, buf_p, sizeof(uint8_t) * length); } else { for (i = 0; i < length; i++) { dst_p[i] |= (buf_p[i] >> self_p->bit_offset); dst_p[i + 1] = (uint8_t)(buf_p[i] << (8 - self_p->bit_offset)); } } self_p->byte_offset += length; } void bitstream_writer_write_u8(struct bitstream_writer_t *self_p, uint8_t value) { if (self_p->bit_offset == 0) { self_p->buf_p[self_p->byte_offset] = value; } else { self_p->buf_p[self_p->byte_offset] |= (value >> self_p->bit_offset); self_p->buf_p[self_p->byte_offset + 1] = (uint8_t)(value << (8 - self_p->bit_offset)); } self_p->byte_offset++; } void bitstream_writer_write_u16(struct bitstream_writer_t *self_p, uint16_t value) { if (self_p->bit_offset == 0) { self_p->buf_p[self_p->byte_offset] = (value >> 8); } else { self_p->buf_p[self_p->byte_offset] |= (value >> (8 + self_p->bit_offset)); self_p->buf_p[self_p->byte_offset + 2] = (uint8_t)(value << (8 - self_p->bit_offset)); value >>= self_p->bit_offset; } self_p->buf_p[self_p->byte_offset + 1] = (uint8_t)value; self_p->byte_offset += 2; } void bitstream_writer_write_u32(struct bitstream_writer_t *self_p, uint32_t value) { int i; if (self_p->bit_offset == 0) { self_p->buf_p[self_p->byte_offset] = (value >> 24); } else { self_p->buf_p[self_p->byte_offset] |= (value >> (24 + self_p->bit_offset)); self_p->buf_p[self_p->byte_offset + 4] = (uint8_t)(value << (8 - self_p->bit_offset)); value >>= self_p->bit_offset; } for (i = 3; i > 0; i--) { self_p->buf_p[self_p->byte_offset + i] = value; value >>= 8; } self_p->byte_offset += 4; } void bitstream_writer_write_u64(struct bitstream_writer_t *self_p, uint64_t value) { int i; if (self_p->bit_offset == 0) { self_p->buf_p[self_p->byte_offset] = (value >> 56); } else { self_p->buf_p[self_p->byte_offset] |= (value >> (56 + self_p->bit_offset)); self_p->buf_p[self_p->byte_offset + 8] = (uint8_t)(value << (8 - self_p->bit_offset)); value >>= self_p->bit_offset; } for (i = 7; i > 0; i--) { self_p->buf_p[self_p->byte_offset + i] = (uint8_t)value; value >>= 8; } self_p->byte_offset += 8; } void bitstream_writer_write_u64_bits(struct bitstream_writer_t *self_p, uint64_t value, int number_of_bits) { int i; int first_byte_bits; int last_byte_bits; int full_bytes; if (number_of_bits == 0) { return; } /* Align beginning. */ first_byte_bits = (8 - self_p->bit_offset); if (first_byte_bits != 8) { if (number_of_bits < first_byte_bits) { self_p->buf_p[self_p->byte_offset] |= (uint8_t)(value << (first_byte_bits - number_of_bits)); self_p->bit_offset += number_of_bits; } else { self_p->buf_p[self_p->byte_offset] |= (value >> (number_of_bits - first_byte_bits)); self_p->byte_offset++; self_p->bit_offset = 0; } number_of_bits -= first_byte_bits; if (number_of_bits <= 0) { return; } } /* Align end. */ last_byte_bits = (number_of_bits % 8); full_bytes = (number_of_bits / 8); if (last_byte_bits != 0) { self_p->buf_p[self_p->byte_offset + full_bytes] = (uint8_t)(value << (8 - last_byte_bits)); value >>= last_byte_bits; self_p->bit_offset = last_byte_bits; } /* Copy middle bytes. */ for (i = full_bytes; i > 0; i--) { self_p->buf_p[self_p->byte_offset + i - 1] = (uint8_t)value; value >>= 8; } self_p->byte_offset += full_bytes; } void bitstream_writer_write_repeated_bit(struct bitstream_writer_t *self_p, int value, int length) { int rest; if (value != 0) { value = 0xff; } rest = (length % 8); bitstream_writer_write_u64_bits(self_p, value & ((1 << rest) - 1), rest); bitstream_writer_write_repeated_u8(self_p, value, length / 8); } void bitstream_writer_write_repeated_u8(struct bitstream_writer_t *self_p, uint8_t value, int length) { int i; for (i = 0; i < length; i++) { bitstream_writer_write_u8(self_p, value); } } void bitstream_writer_insert_bit(struct bitstream_writer_t *self_p, int value) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, 1); bitstream_writer_write_bit(self_p, value); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_insert_bytes(struct bitstream_writer_t *self_p, const uint8_t *buf_p, int length) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, 8 * length); bitstream_writer_write_bytes(self_p, buf_p, length); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_insert_u8(struct bitstream_writer_t *self_p, uint8_t value) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, 8); bitstream_writer_write_u8(self_p, value); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_insert_u16(struct bitstream_writer_t *self_p, uint16_t value) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, 16); bitstream_writer_write_u16(self_p, value); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_insert_u32(struct bitstream_writer_t *self_p, uint32_t value) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, 32); bitstream_writer_write_u32(self_p, value); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_insert_u64(struct bitstream_writer_t *self_p, uint64_t value) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, 64); bitstream_writer_write_u64(self_p, value); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_insert_u64_bits(struct bitstream_writer_t *self_p, uint64_t value, int number_of_bits) { struct bitstream_writer_bounds_t bounds; bitstream_writer_bounds_save(&bounds, self_p, (8 * self_p->byte_offset) + self_p->bit_offset, number_of_bits); bitstream_writer_write_u64_bits(self_p, value, number_of_bits); bitstream_writer_bounds_restore(&bounds); } void bitstream_writer_seek(struct bitstream_writer_t *self_p, int offset) { offset += ((8 * self_p->byte_offset) + self_p->bit_offset); self_p->byte_offset = (offset / 8); self_p->bit_offset = (offset % 8); } void bitstream_writer_bounds_save(struct bitstream_writer_bounds_t *self_p, struct bitstream_writer_t *writer_p, int bit_offset, int length) { int number_of_bits; self_p->writer_p = writer_p; number_of_bits = (bit_offset % 8); if (number_of_bits == 0) { self_p->first_byte_offset = -1; } else { self_p->first_byte_offset = (bit_offset / 8); self_p->first_byte = writer_p->buf_p[self_p->first_byte_offset]; self_p->first_byte &= (0xff00 >> number_of_bits); } number_of_bits = ((bit_offset + length) % 8); if (number_of_bits == 0) { self_p->last_byte_offset = -1; } else { self_p->last_byte_offset = ((bit_offset + length) / 8); self_p->last_byte = writer_p->buf_p[self_p->last_byte_offset]; self_p->last_byte &= ~(0xff00 >> number_of_bits); writer_p->buf_p[self_p->last_byte_offset] = 0; } if (self_p->first_byte_offset != -1) { writer_p->buf_p[self_p->first_byte_offset] = 0; } } void bitstream_writer_bounds_restore(struct bitstream_writer_bounds_t *self_p) { if (self_p->first_byte_offset != -1) { self_p->writer_p->buf_p[self_p->first_byte_offset] |= self_p->first_byte; } if (self_p->last_byte_offset != -1) { self_p->writer_p->buf_p[self_p->last_byte_offset] |= self_p->last_byte; } } void bitstream_reader_init(struct bitstream_reader_t *self_p, const uint8_t *buf_p) { self_p->buf_p = buf_p; self_p->byte_offset = 0; self_p->bit_offset = 0; } int bitstream_reader_read_bit(struct bitstream_reader_t *self_p) { int value; if (self_p->bit_offset == 0) { value = (self_p->buf_p[self_p->byte_offset] >> 7); self_p->bit_offset = 1; } else { value = ((self_p->buf_p[self_p->byte_offset] >> (7 - self_p->bit_offset)) & 0x1); if (self_p->bit_offset == 7) { self_p->bit_offset = 0; self_p->byte_offset++; } else { self_p->bit_offset++; } } return (value); } void bitstream_reader_read_bytes(struct bitstream_reader_t *self_p, uint8_t *buf_p, int length) { int i; const uint8_t *src_p; src_p = &self_p->buf_p[self_p->byte_offset]; if (self_p->bit_offset == 0) { memcpy(buf_p, src_p, sizeof(uint8_t) * length); } else { for (i = 0; i < length; i++) { buf_p[i] = (src_p[i] << self_p->bit_offset); buf_p[i] |= (src_p[i + 1] >> (8 - self_p->bit_offset)); } } self_p->byte_offset += length; } uint8_t bitstream_reader_read_u8(struct bitstream_reader_t *self_p) { uint8_t value; value = (self_p->buf_p[self_p->byte_offset] << self_p->bit_offset); self_p->byte_offset++; if (self_p->bit_offset != 0) { value |= (self_p->buf_p[self_p->byte_offset] >> (8 - self_p->bit_offset)); } return (value); } uint16_t bitstream_reader_read_u16(struct bitstream_reader_t *self_p) { uint16_t value; int i; int offset; const uint8_t *src_p; src_p = &self_p->buf_p[self_p->byte_offset]; offset = (16 + self_p->bit_offset); value = 0; for (i = 0; i < 2; i++) { offset -= 8; value |= ((uint16_t)src_p[i] << offset); } if (offset != 0) { value |= (src_p[2] >> (8 - offset)); } self_p->byte_offset += 2; return (value); } uint32_t bitstream_reader_read_u32(struct bitstream_reader_t *self_p) { uint32_t value; int i; int offset; const uint8_t *src_p; src_p = &self_p->buf_p[self_p->byte_offset]; offset = (32 + self_p->bit_offset); value = 0; for (i = 0; i < 4; i++) { offset -= 8; value |= ((uint32_t)src_p[i] << offset); } if (offset != 0) { value |= (src_p[4] >> (8 - offset)); } self_p->byte_offset += 4; return (value); } uint64_t bitstream_reader_read_u64(struct bitstream_reader_t *self_p) { uint64_t value; int i; int offset; const uint8_t *src_p; src_p = &self_p->buf_p[self_p->byte_offset]; offset = (64 + self_p->bit_offset); value = 0; for (i = 0; i < 8; i++) { offset -= 8; value |= ((uint64_t)src_p[i] << offset); } if (offset != 0) { value |= ((uint64_t)src_p[8] >> (8 - offset)); } self_p->byte_offset += 8; return (value); } uint64_t bitstream_reader_read_u64_bits(struct bitstream_reader_t *self_p, int number_of_bits) { uint64_t value; int i; int first_byte_bits; int last_byte_bits; int full_bytes; if (number_of_bits == 0) { return (0); } /* Align beginning. */ first_byte_bits = (8 - self_p->bit_offset); if (first_byte_bits != 8) { if (number_of_bits < first_byte_bits) { value = (self_p->buf_p[self_p->byte_offset] >> (first_byte_bits - number_of_bits)); value &= ((1 << number_of_bits) - 1); self_p->bit_offset += number_of_bits; } else { value = self_p->buf_p[self_p->byte_offset]; value &= ((1 << first_byte_bits) - 1); self_p->byte_offset++; self_p->bit_offset = 0; } number_of_bits -= first_byte_bits; if (number_of_bits <= 0) { return (value); } } else { value = 0; } /* Copy middle bytes. */ full_bytes = (number_of_bits / 8); for (i = 0; i < full_bytes; i++) { value <<= 8; value |= self_p->buf_p[self_p->byte_offset + i]; } /* Last byte. */ last_byte_bits = (number_of_bits % 8); if (last_byte_bits != 0) { value <<= last_byte_bits; value |= (self_p->buf_p[self_p->byte_offset + full_bytes] >> (8 - last_byte_bits)); self_p->bit_offset = last_byte_bits; } self_p->byte_offset += full_bytes; return (value); } void bitstream_reader_seek(struct bitstream_reader_t *self_p, int offset) { offset += ((8 * self_p->byte_offset) + self_p->bit_offset); self_p->byte_offset = (offset / 8); self_p->bit_offset = (offset % 8); } int bitstream_reader_tell(struct bitstream_reader_t *self_p) { return ((8 * self_p->byte_offset) + self_p->bit_offset); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/src/bitstruct/bitstream.h0000644000175100001660000001363714756336136020234 0ustar00runnerdocker/** * The MIT License (MIT) * * Copyright (c) 2019 Erik Moqvist * * 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. */ #ifndef BITSTREAM_H #define BITSTREAM_H #include #define BITSTREAM_VERSION "0.8.0" struct bitstream_writer_t { uint8_t *buf_p; int byte_offset; int bit_offset; }; struct bitstream_writer_bounds_t { struct bitstream_writer_t *writer_p; int first_byte_offset; uint8_t first_byte; int last_byte_offset; uint8_t last_byte; }; struct bitstream_reader_t { const uint8_t *buf_p; int byte_offset; int bit_offset; }; /* * The writer. */ void bitstream_writer_init(struct bitstream_writer_t *self_p, uint8_t *buf_p); int bitstream_writer_size_in_bits(struct bitstream_writer_t *self_p); int bitstream_writer_size_in_bytes(struct bitstream_writer_t *self_p); /* Write bits to the stream. Clears each byte before bits are written. */ void bitstream_writer_write_bit(struct bitstream_writer_t *self_p, int value); void bitstream_writer_write_bytes(struct bitstream_writer_t *self_p, const uint8_t *buf_p, int length); void bitstream_writer_write_u8(struct bitstream_writer_t *self_p, uint8_t value); void bitstream_writer_write_u16(struct bitstream_writer_t *self_p, uint16_t value); void bitstream_writer_write_u32(struct bitstream_writer_t *self_p, uint32_t value); void bitstream_writer_write_u64(struct bitstream_writer_t *self_p, uint64_t value); /* Upper unused bits must be zero. */ void bitstream_writer_write_u64_bits(struct bitstream_writer_t *self_p, uint64_t value, int number_of_bits); void bitstream_writer_write_repeated_bit(struct bitstream_writer_t *self_p, int value, int length); void bitstream_writer_write_repeated_u8(struct bitstream_writer_t *self_p, uint8_t value, int length); /* Insert bits into the stream. Leaves all other bits unmodified. */ void bitstream_writer_insert_bit(struct bitstream_writer_t *self_p, int value); void bitstream_writer_insert_bytes(struct bitstream_writer_t *self_p, const uint8_t *buf_p, int length); void bitstream_writer_insert_u8(struct bitstream_writer_t *self_p, uint8_t value); void bitstream_writer_insert_u16(struct bitstream_writer_t *self_p, uint16_t value); void bitstream_writer_insert_u32(struct bitstream_writer_t *self_p, uint32_t value); void bitstream_writer_insert_u64(struct bitstream_writer_t *self_p, uint64_t value); void bitstream_writer_insert_u64_bits(struct bitstream_writer_t *self_p, uint64_t value, int number_of_bits); /* Move write position. Seeking backwards makes the written size smaller. Use write with care after seek, as seek does not clear bytes. */ void bitstream_writer_seek(struct bitstream_writer_t *self_p, int offset); /* Save-restore first and last bytes in given range, so write can be used in given range. */ void bitstream_writer_bounds_save(struct bitstream_writer_bounds_t *self_p, struct bitstream_writer_t *writer_p, int bit_offset, int length); void bitstream_writer_bounds_restore(struct bitstream_writer_bounds_t *self_p); /* * The reader. */ void bitstream_reader_init(struct bitstream_reader_t *self_p, const uint8_t *buf_p); /* Read bits from the stream. */ int bitstream_reader_read_bit(struct bitstream_reader_t *self_p); void bitstream_reader_read_bytes(struct bitstream_reader_t *self_p, uint8_t *buf_p, int length); uint8_t bitstream_reader_read_u8(struct bitstream_reader_t *self_p); uint16_t bitstream_reader_read_u16(struct bitstream_reader_t *self_p); uint32_t bitstream_reader_read_u32(struct bitstream_reader_t *self_p); uint64_t bitstream_reader_read_u64(struct bitstream_reader_t *self_p); uint64_t bitstream_reader_read_u64_bits(struct bitstream_reader_t *self_p, int number_of_bits); /* Move read position. */ void bitstream_reader_seek(struct bitstream_reader_t *self_p, int offset); /* Get read position. */ int bitstream_reader_tell(struct bitstream_reader_t *self_p); #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/src/bitstruct/c.c0000644000175100001660000020530214756336136016447 0ustar00runnerdocker/** * CPython 3 C extension. */ #include #include #include "bitstream.h" #include struct field_info_t; typedef void (*pack_field_t)(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p); typedef PyObject *(*unpack_field_t)(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p); struct field_info_t { pack_field_t pack; unpack_field_t unpack; int number_of_bits; bool is_padding; union { struct { int64_t lower; int64_t upper; } s; struct { uint64_t upper; } u; } limits; }; struct info_t { int number_of_bits; int number_of_fields; int number_of_non_padding_fields; struct field_info_t fields[1]; }; struct compiled_format_t { PyObject_HEAD struct info_t *info_p; PyObject *format_p; }; struct compiled_format_dict_t { PyObject_HEAD struct info_t *info_p; PyObject *format_p; PyObject *names_p; }; static const char* pickle_version_key = "_pickle_version"; static int pickle_version = 1; static PyObject *compiled_format_new(PyTypeObject *type_p, PyObject *args_p, PyObject *kwargs_p); static int compiled_format_init(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p); static int compiled_format_init_inner(struct compiled_format_t *self_p, PyObject *format_p); static void compiled_format_dealloc(struct compiled_format_t *self_p); static PyObject *m_compiled_format_pack(struct compiled_format_t *self_p, PyObject *args_p); static PyObject *m_compiled_format_unpack(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p); static PyObject *m_compiled_format_pack_into(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p); static PyObject *m_compiled_format_unpack_from(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p); static PyObject *m_compiled_format_calcsize(struct compiled_format_t *self_p); static PyObject *m_compiled_format_copy(struct compiled_format_t *self_p); static PyObject *m_compiled_format_deepcopy(struct compiled_format_t *self_p, PyObject *args_p); static PyObject *m_compiled_format_getstate(struct compiled_format_t *self_p, PyObject *args_p); static PyObject *m_compiled_format_setstate(struct compiled_format_t *self_p, PyObject *args_p); static PyObject *compiled_format_dict_new(PyTypeObject *type_p, PyObject *args_p, PyObject *kwargs_p); static int compiled_format_dict_init(struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p); static int compiled_format_dict_init_inner(struct compiled_format_dict_t *self_p, PyObject *format_p, PyObject *names_p); static void compiled_format_dict_dealloc(struct compiled_format_dict_t *self_p); static PyObject *m_compiled_format_dict_pack(struct compiled_format_dict_t *self_p, PyObject *data_p); static PyObject *m_compiled_format_dict_unpack( struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p); static PyObject *m_compiled_format_dict_pack_into( struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p); static PyObject *m_compiled_format_dict_unpack_from( struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p); static PyObject *m_compiled_format_dict_calcsize( struct compiled_format_dict_t *self_p); static PyObject *m_compiled_format_dict_copy( struct compiled_format_dict_t *self_p); static PyObject *m_compiled_format_dict_deepcopy( struct compiled_format_dict_t *self_p, PyObject *args_p); static PyObject *m_compiled_format_dict_getstate(struct compiled_format_dict_t *self_p, PyObject *args_p); static PyObject *m_compiled_format_dict_setstate(struct compiled_format_dict_t *self_p, PyObject *args_p); PyDoc_STRVAR(pack___doc__, "pack(fmt, *args)\n" "--\n" "\n"); PyDoc_STRVAR(compiled_format_pack___doc__, "pack(*args)\n" "--\n" "\n"); PyDoc_STRVAR(unpack___doc__, "unpack(fmt, data, allow_truncated=False)\n" "--\n" "\n"); PyDoc_STRVAR(compiled_format_unpack___doc__, "unpack(data, allow_truncated=False)\n" "--\n" "\n"); PyDoc_STRVAR(pack_into___doc__, "pack_into(fmt, buf, offset, *args, **kwargs)\n" "--\n" "\n"); PyDoc_STRVAR(compiled_format_pack_into___doc__, "pack_into(buf, offset, *args, **kwargs)\n" "--\n" "\n"); PyDoc_STRVAR(unpack_from___doc__, "unpack_from(fmt, data, offset=0, allow_truncated=False)\n" "--\n" "\n"); PyDoc_STRVAR(compiled_format_unpack_from___doc__, "unpack_from(data, offset=0, allow_truncated=False)\n" "--\n" "\n"); PyDoc_STRVAR(calcsize___doc__, "calcsize(fmt)\n" "--\n" "\n"); PyDoc_STRVAR(compiled_format_calcsize___doc__, "calcsize()\n" "--\n" "\n"); static PyObject *py_zero_p = NULL; static struct PyMethodDef compiled_format_methods[] = { { "pack", (PyCFunction)m_compiled_format_pack, METH_VARARGS, compiled_format_pack___doc__ }, { "unpack", (PyCFunction)m_compiled_format_unpack, METH_VARARGS | METH_KEYWORDS, compiled_format_unpack___doc__ }, { "pack_into", (PyCFunction)m_compiled_format_pack_into, METH_VARARGS | METH_KEYWORDS, compiled_format_pack_into___doc__ }, { "unpack_from", (PyCFunction)m_compiled_format_unpack_from, METH_VARARGS | METH_KEYWORDS, compiled_format_unpack_from___doc__ }, { "calcsize", (PyCFunction)m_compiled_format_calcsize, METH_NOARGS, compiled_format_calcsize___doc__ }, { "__copy__", (PyCFunction)m_compiled_format_copy, METH_NOARGS }, { "__deepcopy__", (PyCFunction)m_compiled_format_deepcopy, METH_VARARGS }, { "__getstate__", (PyCFunction)m_compiled_format_getstate, METH_NOARGS }, { "__setstate__", (PyCFunction)m_compiled_format_setstate, METH_O }, { NULL } }; static PyTypeObject compiled_format_type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "bitstruct.c.CompiledFormat", .tp_doc = NULL, .tp_basicsize = sizeof(struct compiled_format_t), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = compiled_format_new, .tp_init = (initproc)compiled_format_init, .tp_dealloc = (destructor)compiled_format_dealloc, .tp_methods = compiled_format_methods, }; static struct PyMethodDef compiled_format_dict_methods[] = { { "pack", (PyCFunction)m_compiled_format_dict_pack, METH_O, pack___doc__ }, { "unpack", (PyCFunction)m_compiled_format_dict_unpack, METH_VARARGS | METH_KEYWORDS, unpack___doc__ }, { "pack_into", (PyCFunction)m_compiled_format_dict_pack_into, METH_VARARGS | METH_KEYWORDS, pack_into___doc__ }, { "unpack_from", (PyCFunction)m_compiled_format_dict_unpack_from, METH_VARARGS | METH_KEYWORDS, unpack_from___doc__ }, { "calcsize", (PyCFunction)m_compiled_format_dict_calcsize, METH_NOARGS, calcsize___doc__ }, { "__copy__", (PyCFunction)m_compiled_format_dict_copy, METH_NOARGS }, { "__deepcopy__", (PyCFunction)m_compiled_format_dict_deepcopy, METH_VARARGS }, { "__getstate__", (PyCFunction)m_compiled_format_dict_getstate, METH_NOARGS }, { "__setstate__", (PyCFunction)m_compiled_format_dict_setstate, METH_O }, { NULL } }; static PyTypeObject compiled_format_dict_type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "bitstruct.c.CompiledFormatDict", .tp_doc = NULL, .tp_basicsize = sizeof(struct compiled_format_dict_t), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = compiled_format_dict_new, .tp_init = (initproc)compiled_format_dict_init, .tp_dealloc = (destructor)compiled_format_dict_dealloc, .tp_methods = compiled_format_dict_methods, }; static bool is_names_list(PyObject *names_p) { if (!PyList_Check(names_p)) { PyErr_SetString(PyExc_TypeError, "Names is not a list."); return (false); } return (true); } static void pack_signed_integer(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { int64_t value; int64_t lower; int64_t upper; value = PyLong_AsLongLong(value_p); if ((value == -1) && PyErr_Occurred()) { return; } if (field_info_p->number_of_bits < 64) { lower = field_info_p->limits.s.lower; upper = field_info_p->limits.s.upper; if ((value < lower) || (value > upper)) { PyErr_Format(PyExc_OverflowError, "Signed integer value %lld out of range.", (long long)value); } value &= ((1ull << field_info_p->number_of_bits) - 1); } bitstream_writer_write_u64_bits(self_p, (uint64_t)value, field_info_p->number_of_bits); } static PyObject *unpack_signed_integer(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { uint64_t value; uint64_t sign_bit; value = bitstream_reader_read_u64_bits(self_p, field_info_p->number_of_bits); sign_bit = (1ull << (field_info_p->number_of_bits - 1)); if (value & sign_bit) { value |= ~(((sign_bit) << 1) - 1); } return (PyLong_FromLongLong((long long)value)); } static void pack_unsigned_integer(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { uint64_t value; value = PyLong_AsUnsignedLongLong(value_p); if ((value == (uint64_t)-1) && PyErr_Occurred()) { return; } if (value > field_info_p->limits.u.upper) { PyErr_Format(PyExc_OverflowError, "Unsigned integer value %llu out of range.", (unsigned long long)value); } bitstream_writer_write_u64_bits(self_p, value, field_info_p->number_of_bits); } static PyObject *unpack_unsigned_integer(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { uint64_t value; value = bitstream_reader_read_u64_bits(self_p, field_info_p->number_of_bits); return (PyLong_FromUnsignedLongLong(value)); } #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 static void pack_float_16(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { uint8_t buf[2]; #if PY_VERSION_HEX >= 0x030B00A7 PyFloat_Pack2(PyFloat_AsDouble(value_p), (char*)&buf[0], PY_BIG_ENDIAN); #else _PyFloat_Pack2(PyFloat_AsDouble(value_p), &buf[0], PY_BIG_ENDIAN); #endif bitstream_writer_write_bytes(self_p, &buf[0], sizeof(buf)); } static PyObject *unpack_float_16(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { uint8_t buf[2]; double value; bitstream_reader_read_bytes(self_p, &buf[0], sizeof(buf)); #if PY_VERSION_HEX >= 0x030B00A7 value = PyFloat_Unpack2((const char*)&buf[0], PY_BIG_ENDIAN); #else value = _PyFloat_Unpack2(&buf[0], PY_BIG_ENDIAN); #endif return (PyFloat_FromDouble(value)); } #endif static void pack_float_32(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { float value; uint32_t data; value = (float)PyFloat_AsDouble(value_p); memcpy(&data, &value, sizeof(data)); bitstream_writer_write_u32(self_p, data); } static PyObject *unpack_float_32(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { float value; uint32_t data; data = bitstream_reader_read_u32(self_p); memcpy(&value, &data, sizeof(value)); return (PyFloat_FromDouble(value)); } static void pack_float_64(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { double value; uint64_t data; value = PyFloat_AsDouble(value_p); memcpy(&data, &value, sizeof(data)); bitstream_writer_write_u64_bits(self_p, data, field_info_p->number_of_bits); } static PyObject *unpack_float_64(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { double value; uint64_t data; data = bitstream_reader_read_u64(self_p); memcpy(&value, &data, sizeof(value)); return (PyFloat_FromDouble(value)); } static void pack_bool(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { bitstream_writer_write_u64_bits(self_p, PyObject_IsTrue(value_p), field_info_p->number_of_bits); } static PyObject *unpack_bool(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { return (PyBool_FromLong((long)bitstream_reader_read_u64_bits( self_p, field_info_p->number_of_bits))); } static void pack_text(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { Py_ssize_t size; const char* buf_p; buf_p = PyUnicode_AsUTF8AndSize(value_p, &size); if (buf_p != NULL) { if (size < (field_info_p->number_of_bits / 8)) { PyErr_SetString(PyExc_NotImplementedError, "Short text."); } else { bitstream_writer_write_bytes(self_p, (uint8_t *)buf_p, field_info_p->number_of_bits / 8); } } } static PyObject *unpack_text(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { uint8_t *buf_p; PyObject *value_p; int number_of_bytes; number_of_bytes = (field_info_p->number_of_bits / 8); buf_p = PyMem_RawMalloc(number_of_bytes); if (buf_p == NULL) { return (NULL); } bitstream_reader_read_bytes(self_p, buf_p, number_of_bytes); value_p = PyUnicode_FromStringAndSize((const char *)buf_p, number_of_bytes); PyMem_RawFree(buf_p); return (value_p); } static void pack_raw(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { Py_ssize_t size; char* buf_p; int res; res = PyBytes_AsStringAndSize(value_p, &buf_p, &size); if (res != -1) { if (size < (field_info_p->number_of_bits / 8)) { PyErr_SetString(PyExc_NotImplementedError, "Short raw data."); } else { bitstream_writer_write_bytes(self_p, (uint8_t *)buf_p, field_info_p->number_of_bits / 8); } } } static PyObject *unpack_raw(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { uint8_t *buf_p; PyObject *value_p; int number_of_bytes; number_of_bytes = (field_info_p->number_of_bits / 8); value_p = PyBytes_FromStringAndSize(NULL, number_of_bytes); buf_p = (uint8_t *)PyBytes_AS_STRING(value_p); bitstream_reader_read_bytes(self_p, buf_p, number_of_bytes); return (value_p); } static void pack_zero_padding(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { bitstream_writer_write_repeated_bit(self_p, 0, field_info_p->number_of_bits); } static void pack_one_padding(struct bitstream_writer_t *self_p, PyObject *value_p, struct field_info_t *field_info_p) { bitstream_writer_write_repeated_bit(self_p, 1, field_info_p->number_of_bits); } static PyObject *unpack_padding(struct bitstream_reader_t *self_p, struct field_info_t *field_info_p) { bitstream_reader_seek(self_p, field_info_p->number_of_bits); return (NULL); } static int field_info_init_signed(struct field_info_t *self_p, int number_of_bits) { uint64_t limit; self_p->pack = pack_signed_integer; self_p->unpack = unpack_signed_integer; if (number_of_bits > 64) { PyErr_SetString(PyExc_NotImplementedError, "Signed integer over 64 bits."); return (-1); } limit = (1ull << (number_of_bits - 1)); self_p->limits.s.lower = -limit; self_p->limits.s.upper = (limit - 1); return (0); } static int field_info_init_unsigned(struct field_info_t *self_p, int number_of_bits) { self_p->pack = pack_unsigned_integer; self_p->unpack = unpack_unsigned_integer; if (number_of_bits > 64) { PyErr_SetString(PyExc_NotImplementedError, "Unsigned integer over 64 bits."); return (-1); } if (number_of_bits < 64) { self_p->limits.u.upper = ((1ull << number_of_bits) - 1); } else { self_p->limits.u.upper = (uint64_t)-1; } return (0); } static int field_info_init_float(struct field_info_t *self_p, int number_of_bits) { switch (number_of_bits) { #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 case 16: self_p->pack = pack_float_16; self_p->unpack = unpack_float_16; break; #endif case 32: self_p->pack = pack_float_32; self_p->unpack = unpack_float_32; break; case 64: self_p->pack = pack_float_64; self_p->unpack = unpack_float_64; break; default: #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 PyErr_SetString(PyExc_NotImplementedError, "Float not 16, 32 or 64 bits."); #else PyErr_SetString(PyExc_NotImplementedError, "Float not 32 or 64 bits."); #endif return (-1); } return (0); } static int field_info_init_bool(struct field_info_t *self_p, int number_of_bits) { self_p->pack = pack_bool; self_p->unpack = unpack_bool; if (number_of_bits > 64) { PyErr_SetString(PyExc_NotImplementedError, "Bool over 64 bits."); return (-1); } return (0); } static int field_info_init_text(struct field_info_t *self_p, int number_of_bits) { self_p->pack = pack_text; self_p->unpack = unpack_text; if ((number_of_bits % 8) != 0) { PyErr_SetString(PyExc_NotImplementedError, "Text not multiple of 8 bits."); return (-1); } return (0); } static int field_info_init_raw(struct field_info_t *self_p, int number_of_bits) { self_p->pack = pack_raw; self_p->unpack = unpack_raw; if ((number_of_bits % 8) != 0) { PyErr_SetString(PyExc_NotImplementedError, "Raw not multiple of 8 bits."); return (-1); } return (0); } static int field_info_init_zero_padding(struct field_info_t *self_p) { self_p->pack = pack_zero_padding; self_p->unpack = unpack_padding; return (0); } static int field_info_init_one_padding(struct field_info_t *self_p) { self_p->pack = pack_one_padding; self_p->unpack = unpack_padding; return (0); } static int field_info_init(struct field_info_t *self_p, int kind, int number_of_bits) { int res; bool is_padding; is_padding = false; switch (kind) { case 's': res = field_info_init_signed(self_p, number_of_bits); break; case 'u': res = field_info_init_unsigned(self_p, number_of_bits); break; case 'f': res = field_info_init_float(self_p, number_of_bits); break; case 'b': res = field_info_init_bool(self_p, number_of_bits); break; case 't': res = field_info_init_text(self_p, number_of_bits); break; case 'r': res = field_info_init_raw(self_p, number_of_bits); break; case 'p': is_padding = true; res = field_info_init_zero_padding(self_p); break; case 'P': is_padding = true; res = field_info_init_one_padding(self_p); break; default: PyErr_Format(PyExc_ValueError, "Bad format field type '%c'.", kind); res = -1; break; } self_p->number_of_bits = number_of_bits; self_p->is_padding = is_padding; return (res); } static int count_number_of_fields(const char *format_p, int *number_of_padding_fields_p) { int count; count = 0; *number_of_padding_fields_p = 0; while (*format_p != '\0') { if ((*format_p >= 'A') && (*format_p <= 'z')) { count++; if ((*format_p == 'p') || (*format_p == 'P')) { (*number_of_padding_fields_p)++; } } format_p++; } return (count); } const char *parse_field(const char *format_p, int *kind_p, int *number_of_bits_p) { while (isspace(*format_p)) { format_p++; } *kind_p = *format_p; *number_of_bits_p = 0; format_p++; while (isdigit(*format_p)) { if (*number_of_bits_p > (INT_MAX / 100)) { PyErr_SetString(PyExc_ValueError, "Field too long."); return (NULL); } *number_of_bits_p *= 10; *number_of_bits_p += (*format_p - '0'); format_p++; } if (*number_of_bits_p == 0) { PyErr_SetString(PyExc_ValueError, "Field of size 0."); format_p = NULL; } return (format_p); } static struct info_t *parse_format(PyObject *format_obj_p) { int number_of_fields; struct info_t *info_p; const char *format_p; int i; int kind; int number_of_bits; int number_of_padding_fields; int res; format_p = PyUnicode_AsUTF8(format_obj_p); if (format_p == NULL) { return (NULL); } number_of_fields = count_number_of_fields(format_p, &number_of_padding_fields); info_p = PyMem_RawMalloc( sizeof(*info_p) + number_of_fields * sizeof(info_p->fields[0])); if (info_p == NULL) { return (NULL); } info_p->number_of_bits = 0; info_p->number_of_fields = number_of_fields; info_p->number_of_non_padding_fields = ( number_of_fields - number_of_padding_fields); for (i = 0; i < info_p->number_of_fields; i++) { format_p = parse_field(format_p, &kind, &number_of_bits); if (format_p == NULL) { PyMem_RawFree(info_p); return (NULL); } res = field_info_init(&info_p->fields[i], kind, number_of_bits); if (res != 0) { PyMem_RawFree(info_p); return (NULL); } info_p->number_of_bits += number_of_bits; } return (info_p); } static void pack_pack(struct info_t *info_p, PyObject *args_p, int consumed_args, struct bitstream_writer_t *writer_p) { PyObject *value_p; int i; struct field_info_t *field_p; for (i = 0; i < info_p->number_of_fields; i++) { field_p = &info_p->fields[i]; if (field_p->is_padding) { value_p = NULL; } else { value_p = PyTuple_GET_ITEM(args_p, consumed_args); consumed_args++; } info_p->fields[i].pack(writer_p, value_p, field_p); } } static PyObject *pack_prepare(struct info_t *info_p, struct bitstream_writer_t *writer_p) { PyObject *packed_p; packed_p = PyBytes_FromStringAndSize(NULL, (info_p->number_of_bits + 7) / 8); if (packed_p == NULL) { return (NULL); } bitstream_writer_init(writer_p, (uint8_t *)PyBytes_AS_STRING(packed_p)); return (packed_p); } static PyObject *pack_finalize(PyObject *packed_p) { if (PyErr_Occurred() != NULL) { Py_DECREF(packed_p); packed_p = NULL; } return (packed_p); } static PyObject *pack(struct info_t *info_p, PyObject *args_p, int consumed_args, Py_ssize_t number_of_args) { struct bitstream_writer_t writer; PyObject *packed_p; if (number_of_args < info_p->number_of_non_padding_fields) { PyErr_SetString(PyExc_ValueError, "Too few arguments."); return (NULL); } packed_p = pack_prepare(info_p, &writer); if (packed_p == NULL) { return (NULL); } pack_pack(info_p, args_p, consumed_args, &writer); return (pack_finalize(packed_p)); } static PyObject *m_pack(PyObject *module_p, PyObject *args_p) { Py_ssize_t number_of_args; PyObject *packed_p; struct info_t *info_p; number_of_args = PyTuple_GET_SIZE(args_p); if (number_of_args < 1) { PyErr_SetString(PyExc_ValueError, "No format string."); return (NULL); } info_p = parse_format(PyTuple_GET_ITEM(args_p, 0)); if (info_p == NULL) { return (NULL); } packed_p = pack(info_p, args_p, 1, number_of_args - 1); PyMem_RawFree(info_p); return (packed_p); } static PyObject *unpack(struct info_t *info_p, PyObject *data_p, long offset, PyObject *allow_truncated_p) { struct bitstream_reader_t reader; PyObject *unpacked_p = NULL; PyObject *value_p; Py_buffer view = {NULL, NULL}; int i; int tmp; int produced_args; int res; int allow_truncated; int num_result_fields; res = PyObject_GetBuffer(data_p, &view, PyBUF_C_CONTIGUOUS); if (res == -1) { return (NULL); } allow_truncated = PyObject_IsTrue(allow_truncated_p); if (allow_truncated) { num_result_fields = 0; tmp = 0; for (i = 0; i < info_p->number_of_fields; i++) { if (view.len*8 < tmp + info_p->fields[i].number_of_bits) { break; } tmp += info_p->fields[i].number_of_bits; if (!info_p->fields[i].is_padding) { ++num_result_fields; } } } else { num_result_fields = info_p->number_of_non_padding_fields; if (view.len < ((info_p->number_of_bits + offset + 7) / 8)) { PyErr_SetString(PyExc_ValueError, "Short data."); goto exit; } } unpacked_p = PyTuple_New(num_result_fields); if (unpacked_p == NULL) { goto exit; } bitstream_reader_init(&reader, (uint8_t *)view.buf); bitstream_reader_seek(&reader, offset); produced_args = 0; for (i = 0; i < info_p->number_of_fields; i++) { if (produced_args == num_result_fields) { break; } value_p = info_p->fields[i].unpack(&reader, &info_p->fields[i]); if (value_p != NULL) { PyTuple_SET_ITEM(unpacked_p, produced_args, value_p); produced_args++; } } /* out1: if (PyErr_Occurred() != NULL) { Py_DECREF(unpacked_p); unpacked_p = NULL; } */ exit: PyBuffer_Release(&view); return (unpacked_p); } static PyObject *m_unpack(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *data_p; PyObject *unpacked_p; PyObject *allow_truncated_p; struct info_t *info_p; int res; static char *keywords[] = { "fmt", "data", "allow_truncated", NULL }; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OO|O", &keywords[0], &format_p, &data_p, &allow_truncated_p); if (res == 0) { return (NULL); } info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } unpacked_p = unpack(info_p, data_p, 0, allow_truncated_p); PyMem_RawFree(info_p); return (unpacked_p); } static long parse_offset(PyObject *offset_p) { unsigned long offset; offset = PyLong_AsUnsignedLong(offset_p); if (offset == (unsigned long)-1) { return (-1); } if (offset > 0x7fffffff) { PyErr_Format(PyExc_ValueError, "Offset must be less or equal to %d bits.", 0x7fffffff); return (-1); } return (offset); } static int pack_into_prepare(struct info_t *info_p, PyObject *buf_p, PyObject *offset_p, struct bitstream_writer_t *writer_p, struct bitstream_writer_bounds_t *bounds_p) { uint8_t *packed_p; Py_ssize_t size; long offset; offset = parse_offset(offset_p); if (offset == -1) { return (-1); } if (!PyByteArray_Check(buf_p)) { PyErr_SetString(PyExc_TypeError, "Bytearray needed."); return (-1); } packed_p = (uint8_t *)PyByteArray_AsString(buf_p); if (packed_p == NULL) { return (-1); } size = PyByteArray_GET_SIZE(buf_p); if (size < ((info_p->number_of_bits + offset + 7) / 8)) { PyErr_Format(PyExc_ValueError, "pack_into requires a buffer of at least %ld bits", info_p->number_of_bits + offset); return (-1); } bitstream_writer_init(writer_p, packed_p); bitstream_writer_bounds_save(bounds_p, writer_p, offset, info_p->number_of_bits); bitstream_writer_seek(writer_p, offset); return (0); } static PyObject *pack_into_finalize(struct bitstream_writer_bounds_t *bounds_p) { bitstream_writer_bounds_restore(bounds_p); if (PyErr_Occurred() != NULL) { return (NULL); } Py_RETURN_NONE; } static PyObject *pack_into(struct info_t *info_p, PyObject *buf_p, PyObject *offset_p, PyObject *args_p, Py_ssize_t consumed_args, Py_ssize_t number_of_args) { struct bitstream_writer_t writer; struct bitstream_writer_bounds_t bounds; int res; if ((number_of_args - consumed_args) < info_p->number_of_non_padding_fields) { PyErr_SetString(PyExc_ValueError, "Too few arguments."); return (NULL); } res = pack_into_prepare(info_p, buf_p, offset_p, &writer, &bounds); if (res != 0) { return (NULL); } pack_pack(info_p, args_p, consumed_args, &writer); return (pack_into_finalize(&bounds)); } static PyObject *m_pack_into(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *buf_p; PyObject *offset_p; PyObject *res_p; Py_ssize_t number_of_args; struct info_t *info_p; number_of_args = PyTuple_GET_SIZE(args_p); if (number_of_args < 3) { PyErr_SetString(PyExc_ValueError, "Too few arguments."); return (NULL); } format_p = PyTuple_GET_ITEM(args_p, 0); buf_p = PyTuple_GET_ITEM(args_p, 1); offset_p = PyTuple_GET_ITEM(args_p, 2); info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } res_p = pack_into(info_p, buf_p, offset_p, args_p, 3, number_of_args); PyMem_RawFree(info_p); return (res_p); } static PyObject *unpack_from(struct info_t *info_p, PyObject *data_p, PyObject *offset_p, PyObject *allow_truncated_p) { long offset; offset = parse_offset(offset_p); if (offset == -1) { return (NULL); } return (unpack(info_p, data_p, offset, allow_truncated_p)); } static PyObject *m_unpack_from(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *data_p; PyObject *offset_p; PyObject *unpacked_p; PyObject *allow_truncated_p; struct info_t *info_p; int res; static char *keywords[] = { "fmt", "data", "offset", "allow_truncated", NULL }; offset_p = py_zero_p; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OO|OO", &keywords[0], &format_p, &data_p, &offset_p, &allow_truncated_p); if (res == 0) { return (NULL); } info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } unpacked_p = unpack_from(info_p, data_p, offset_p, allow_truncated_p); PyMem_RawFree(info_p); return (unpacked_p); } static void pack_dict_pack(struct info_t *info_p, PyObject *names_p, PyObject *data_p, struct bitstream_writer_t *writer_p) { PyObject *value_p; int i; int consumed_args; struct field_info_t *field_p; consumed_args = 0; for (i = 0; i < info_p->number_of_fields; i++) { field_p = &info_p->fields[i]; if (field_p->is_padding) { value_p = NULL; } else { value_p = PyDict_GetItem(data_p, PyList_GET_ITEM(names_p, consumed_args)); consumed_args++; if (value_p == NULL) { PyErr_SetString(PyExc_KeyError, "Missing value."); break; } } info_p->fields[i].pack(writer_p, value_p, field_p); } } static PyObject *pack_dict(struct info_t *info_p, PyObject *names_p, PyObject *data_p) { struct bitstream_writer_t writer; PyObject *packed_p; if (PyList_GET_SIZE(names_p) < info_p->number_of_non_padding_fields) { PyErr_SetString(PyExc_ValueError, "Too few names."); return (NULL); } packed_p = pack_prepare(info_p, &writer); if (packed_p == NULL) { return (NULL); } pack_dict_pack(info_p, names_p, data_p, &writer); return (pack_finalize(packed_p)); } PyDoc_STRVAR(pack_dict___doc__, "pack_dict(fmt, names, data)\n" "--\n" "\n"); static PyObject *m_pack_dict(PyObject *module_p, PyObject *args_p) { PyObject *format_p; PyObject *names_p; PyObject *data_p; PyObject *packed_p; struct info_t *info_p; int res; res = PyArg_ParseTuple(args_p, "OOO", &format_p, &names_p, &data_p); if (res == 0) { return (NULL); } info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } if (!is_names_list(names_p)) { return (NULL); } packed_p = pack_dict(info_p, names_p, data_p); PyMem_RawFree(info_p); return (packed_p); } static PyObject *unpack_dict(struct info_t *info_p, PyObject *names_p, PyObject *data_p, long offset, PyObject *allow_truncated_p) { struct bitstream_reader_t reader; PyObject *unpacked_p; PyObject *value_p; Py_buffer view = {NULL, NULL}; int i; int res; int produced_args; int allow_truncated; if (PyList_GET_SIZE(names_p) < info_p->number_of_non_padding_fields) { PyErr_SetString(PyExc_ValueError, "Too few names."); return (NULL); } unpacked_p = PyDict_New(); if (unpacked_p == NULL) { return (NULL); } res = PyObject_GetBuffer(data_p, &view, PyBUF_C_CONTIGUOUS); if (res == -1) { goto out1; } allow_truncated = PyObject_IsTrue(allow_truncated_p); if (!allow_truncated && view.len < ((info_p->number_of_bits + offset + 7) / 8)) { PyErr_SetString(PyExc_ValueError, "Short data."); goto out1; } bitstream_reader_init(&reader, (uint8_t *)view.buf); bitstream_reader_seek(&reader, offset); produced_args = 0; for (i = 0; i < info_p->number_of_fields; i++) { if (view.len*8 < reader.bit_offset + info_p->fields[i].number_of_bits) break; value_p = info_p->fields[i].unpack(&reader, &info_p->fields[i]); if (value_p != NULL) { PyDict_SetItem(unpacked_p, PyList_GET_ITEM(names_p, produced_args), value_p); Py_DECREF(value_p); produced_args++; } } out1: if (PyErr_Occurred() != NULL) { Py_DECREF(unpacked_p); unpacked_p = NULL; } if (view.obj != NULL) { PyBuffer_Release(&view); } return (unpacked_p); } PyDoc_STRVAR(unpack_dict___doc__, "unpack_dict(fmt, names, data, allow_truncated=False)\n" "--\n" "\n"); static PyObject *m_unpack_dict(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *names_p; PyObject *data_p; PyObject *allow_truncated_p; PyObject *unpacked_p; struct info_t *info_p; int res; static char *keywords[] = { "fmt", "names", "data", "allow_truncated", NULL }; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OOO|O", &keywords[0], &format_p, &names_p, &data_p, &allow_truncated_p); if (res == 0) { return (NULL); } info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } if (!is_names_list(names_p)) { return (NULL); } unpacked_p = unpack_dict(info_p, names_p, data_p, 0, allow_truncated_p); PyMem_RawFree(info_p); return (unpacked_p); } static PyObject *unpack_from_dict(struct info_t *info_p, PyObject *names_p, PyObject *data_p, PyObject *offset_p, PyObject *allow_truncated_p) { long offset; offset = parse_offset(offset_p); if (offset == -1) { return (NULL); } return (unpack_dict(info_p, names_p, data_p, offset, allow_truncated_p)); } static PyObject *pack_into_dict(struct info_t *info_p, PyObject *names_p, PyObject *buf_p, PyObject *offset_p, PyObject *data_p) { struct bitstream_writer_t writer; struct bitstream_writer_bounds_t bounds; int res; res = pack_into_prepare(info_p, buf_p, offset_p, &writer, &bounds); if (res != 0) { return (NULL); } pack_dict_pack(info_p, names_p, data_p, &writer); return (pack_into_finalize(&bounds)); } PyDoc_STRVAR(pack_into_dict___doc__, "pack_into_dict(fmt, names, buf, offset, data, **kwargs)\n" "--\n" "\n"); static PyObject *m_pack_into_dict(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *names_p; PyObject *buf_p; PyObject *offset_p; PyObject *data_p; PyObject *res_p; struct info_t *info_p; int res; static char *keywords[] = { "fmt", "names", "buf", "offset", "data", NULL }; offset_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OOOOO", &keywords[0], &format_p, &names_p, &buf_p, &offset_p, &data_p); if (res == 0) { return (NULL); } info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } if (!is_names_list(names_p)) { return (NULL); } res_p = pack_into_dict(info_p, names_p, buf_p, offset_p, data_p); PyMem_RawFree(info_p); return (res_p); } PyDoc_STRVAR(unpack_from_dict___doc__, "unpack_from_dict(fmt, names, data, offset=0, allow_truncated=False)\n" "--\n" "\n"); static PyObject *m_unpack_from_dict(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *names_p; PyObject *data_p; PyObject *offset_p; PyObject *allow_truncated_p; PyObject *unpacked_p; struct info_t *info_p; int res; static char *keywords[] = { "fmt", "names", "data", "offset", "allow_truncated", NULL }; offset_p = py_zero_p; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OOO|OO", &keywords[0], &format_p, &names_p, &data_p, &offset_p, &allow_truncated_p); if (res == 0) { return (NULL); } info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } if (!is_names_list(names_p)) { return (NULL); } unpacked_p = unpack_from_dict(info_p, names_p, data_p, offset_p, allow_truncated_p); PyMem_RawFree(info_p); return (unpacked_p); } static PyObject *calcsize(struct info_t *info_p) { return (PyLong_FromLong(info_p->number_of_bits)); } static PyObject *m_calcsize(PyObject *module_p, PyObject *format_p) { PyObject *size_p; struct info_t *info_p; info_p = parse_format(format_p); if (info_p == NULL) { return (NULL); } size_p = calcsize(info_p); PyMem_RawFree(info_p); return (size_p); } PyDoc_STRVAR(byteswap___doc__, "byteswap(fmt, data, offset=0)\n" "--\n" "\n"); static PyObject *m_byteswap(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *data_p; PyObject *swapped_p; const char *c_format_p; uint8_t *src_p; uint8_t *dst_p; Py_ssize_t size; int res; int offset; static char *keywords[] = { "fmt", "data", NULL }; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OO", &keywords[0], &format_p, &data_p); if (res == 0) { return (NULL); } c_format_p = PyUnicode_AsUTF8(format_p); if (c_format_p == NULL) { return (NULL); } res = PyBytes_AsStringAndSize(data_p, (char **)&src_p, &size); if (res == -1) { return (NULL); } swapped_p = PyBytes_FromStringAndSize(NULL, size); if (swapped_p == NULL) { return (NULL); } dst_p = (uint8_t *)PyBytes_AS_STRING(swapped_p); offset = 0; while (*c_format_p != '\0') { switch (*c_format_p) { case '1': if ((size - offset) < 1) { goto out1; } dst_p[offset] = src_p[offset]; offset += 1; break; case '2': if ((size - offset) < 2) { goto out1; } dst_p[offset + 0] = src_p[offset + 1]; dst_p[offset + 1] = src_p[offset + 0]; offset += 2; break; case '4': if ((size - offset) < 4) { goto out1; } dst_p[offset + 0] = src_p[offset + 3]; dst_p[offset + 1] = src_p[offset + 2]; dst_p[offset + 2] = src_p[offset + 1]; dst_p[offset + 3] = src_p[offset + 0]; offset += 4; break; case '8': if ((size - offset) < 8) { goto out1; } dst_p[offset + 0] = src_p[offset + 7]; dst_p[offset + 1] = src_p[offset + 6]; dst_p[offset + 2] = src_p[offset + 5]; dst_p[offset + 3] = src_p[offset + 4]; dst_p[offset + 4] = src_p[offset + 3]; dst_p[offset + 5] = src_p[offset + 2]; dst_p[offset + 6] = src_p[offset + 1]; dst_p[offset + 7] = src_p[offset + 0]; offset += 8; break; default: PyErr_Format(PyExc_ValueError, "Expected 1, 2, 4 or 8, but got %c.", (char)*c_format_p); goto out2; } c_format_p++; } return (swapped_p); out1: PyErr_SetString(PyExc_ValueError, "Out of data to swap."); out2: return (NULL); } static PyObject *compiled_format_create(PyTypeObject *type_p, PyObject *format_p) { PyObject *self_p; self_p = compiled_format_new(type_p, NULL, NULL); if (self_p == NULL) { return (NULL); } if (compiled_format_init_inner((struct compiled_format_t *)self_p, format_p) != 0) { return (NULL); } return (self_p); } static PyObject *compiled_format_new(PyTypeObject *type_p, PyObject *args_p, PyObject *kwargs_p) { return (type_p->tp_alloc(type_p, 0)); } static int compiled_format_init(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p) { int res; PyObject *format_p; static char *keywords[] = { "fmt", NULL }; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "O", &keywords[0], &format_p); if (res == 0) { return (-1); } return (compiled_format_init_inner(self_p, format_p)); } static int compiled_format_init_inner(struct compiled_format_t *self_p, PyObject *format_p) { self_p->info_p = parse_format(format_p); if (self_p->info_p == NULL) { PyObject_Free(self_p); return (-1); } Py_INCREF(format_p); self_p->format_p = format_p; return (0); } static void compiled_format_dealloc(struct compiled_format_t *self_p) { PyMem_RawFree(self_p->info_p); Py_DECREF(self_p->format_p); Py_TYPE(self_p)->tp_free((PyObject *)self_p); } static PyObject *m_compiled_format_pack(struct compiled_format_t *self_p, PyObject *args_p) { return (pack(self_p->info_p, args_p, 0, PyTuple_GET_SIZE(args_p))); } static PyObject *m_compiled_format_unpack(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *data_p; PyObject *allow_truncated_p; int res; static char *keywords[] = { "data", "allow_truncated", NULL }; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "O|O", &keywords[0], &data_p, &allow_truncated_p); if (res == 0) { return (NULL); } return (unpack(self_p->info_p, data_p, 0, allow_truncated_p)); } static PyObject *m_compiled_format_pack_into(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *buf_p; PyObject *offset_p; Py_ssize_t number_of_args; number_of_args = PyTuple_GET_SIZE(args_p); if (number_of_args < 2) { PyErr_SetString(PyExc_ValueError, "Too few arguments."); return (NULL); } buf_p = PyTuple_GET_ITEM(args_p, 0); offset_p = PyTuple_GET_ITEM(args_p, 1); return (pack_into(self_p->info_p, buf_p, offset_p, args_p, 2, number_of_args)); } static PyObject *m_compiled_format_unpack_from(struct compiled_format_t *self_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *data_p; PyObject *offset_p; PyObject *allow_truncated_p; int res; static char *keywords[] = { "data", "offset", "allow_truncated", NULL }; offset_p = py_zero_p; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "O|OO", &keywords[0], &data_p, &offset_p, &allow_truncated_p); if (res == 0) { return (NULL); } return (unpack_from(self_p->info_p, data_p, offset_p, allow_truncated_p)); } static PyObject *m_compiled_format_calcsize(struct compiled_format_t *self_p) { return (calcsize(self_p->info_p)); } static PyObject *m_compiled_format_copy(struct compiled_format_t *self_p) { struct compiled_format_t *new_p; size_t info_size; new_p = (struct compiled_format_t *)compiled_format_new( &compiled_format_type, NULL, NULL); if (new_p == NULL) { return (NULL); } info_size = sizeof(*self_p->info_p); info_size += (sizeof(self_p->info_p->fields[0]) * (self_p->info_p->number_of_fields - 1)); new_p->info_p = PyMem_RawMalloc(info_size); if (new_p->info_p == NULL) { /* ToDo: Free new_p. */ return (NULL); } memcpy(new_p->info_p, self_p->info_p, info_size); Py_INCREF(self_p->format_p); new_p->format_p = self_p->format_p; return ((PyObject *)new_p); } static PyObject *m_compiled_format_deepcopy(struct compiled_format_t *self_p, PyObject *args_p) { return (m_compiled_format_copy(self_p)); } static PyObject *m_compiled_format_getstate(struct compiled_format_t *self_p, PyObject *args_p) { return (Py_BuildValue("{sOsi}", "format", self_p->format_p, pickle_version_key, pickle_version)); } static PyObject *m_compiled_format_setstate(struct compiled_format_t *self_p, PyObject *state_p) { PyObject *version_p; int version; PyObject *format_p; if (!PyDict_CheckExact(state_p)) { PyErr_SetString(PyExc_ValueError, "Pickled object is not a dict."); return (NULL); } version_p = PyDict_GetItemString(state_p, pickle_version_key); if (version_p == NULL) { PyErr_Format(PyExc_KeyError, "No \"%s\" in pickled dict.", pickle_version_key); return (NULL); } version = (int)PyLong_AsLong(version_p); if (version != pickle_version) { PyErr_Format(PyExc_ValueError, "Pickle version mismatch. Got version %d but expected version %d.", version, pickle_version); return (NULL); } format_p = PyDict_GetItemString(state_p, "format"); if (format_p == NULL) { PyErr_SetString(PyExc_KeyError, "No \"format\" in pickled dict."); return (NULL); } if (compiled_format_init_inner(self_p, format_p) != 0) { return (NULL); } Py_RETURN_NONE; } static PyObject *compiled_format_dict_create(PyTypeObject *type_p, PyObject *format_p, PyObject *names_p) { PyObject *self_p; self_p = compiled_format_dict_new(type_p, NULL, NULL); if (self_p == NULL) { return (NULL); } if (compiled_format_dict_init_inner((struct compiled_format_dict_t *)self_p, format_p, names_p) != 0) { return (NULL); } return (self_p); } static PyObject *compiled_format_dict_new(PyTypeObject *type_p, PyObject *args_p, PyObject *kwargs_p) { return (type_p->tp_alloc(type_p, 0)); } static int compiled_format_dict_init(struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p) { int res; PyObject *format_p; PyObject *names_p; static char *keywords[] = { "fmt", "names", NULL }; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OO", &keywords[0], &format_p, &names_p); if (res == 0) { return (-1); } return (compiled_format_dict_init_inner(self_p, format_p, names_p)); } static int compiled_format_dict_init_inner(struct compiled_format_dict_t *self_p, PyObject *format_p, PyObject *names_p) { if (!is_names_list(names_p)) { return (-1); } self_p->info_p = parse_format(format_p); if (self_p->info_p == NULL) { PyObject_Free(self_p); return (-1); } Py_INCREF(format_p); self_p->format_p = format_p; Py_INCREF(names_p); self_p->names_p = names_p; return (0); } static void compiled_format_dict_dealloc(struct compiled_format_dict_t *self_p) { PyMem_RawFree(self_p->info_p); Py_DECREF(self_p->names_p); Py_DECREF(self_p->format_p); Py_TYPE(self_p)->tp_free((PyObject *)self_p); } static PyObject *m_compiled_format_dict_pack(struct compiled_format_dict_t *self_p, PyObject *data_p) { return (pack_dict(self_p->info_p, self_p->names_p, data_p)); } static PyObject *m_compiled_format_dict_unpack( struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *data_p; PyObject *allow_truncated_p; int res; static char *keywords[] = { "data", "allow_truncated", NULL }; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "O|O", &keywords[0], &data_p, &allow_truncated_p); if (res == 0) { return (NULL); } return (unpack_dict(self_p->info_p, self_p->names_p, data_p, 0, allow_truncated_p)); } static PyObject *m_compiled_format_dict_pack_into( struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *buf_p; PyObject *data_p; PyObject *offset_p; int res; static char *keywords[] = { "buf", "data", "offset", NULL }; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "OOO", &keywords[0], &buf_p, &data_p, &offset_p); if (res == 0) { return (NULL); } return (pack_into_dict(self_p->info_p, self_p->names_p, buf_p, data_p, offset_p)); } static PyObject *m_compiled_format_dict_unpack_from( struct compiled_format_dict_t *self_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *data_p; PyObject *offset_p; PyObject *allow_truncated_p; int res; static char *keywords[] = { "data", "offset", NULL }; offset_p = py_zero_p; allow_truncated_p = py_zero_p; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "O|OO", &keywords[0], &data_p, &offset_p, &allow_truncated_p); if (res == 0) { return (NULL); } return (unpack_from_dict(self_p->info_p, self_p->names_p, data_p, offset_p, allow_truncated_p)); } static PyObject *m_compiled_format_dict_calcsize( struct compiled_format_dict_t *self_p) { return (calcsize(self_p->info_p)); } static PyObject *m_compiled_format_dict_copy(struct compiled_format_dict_t *self_p) { struct compiled_format_dict_t *new_p; size_t info_size; new_p = (struct compiled_format_dict_t *)compiled_format_dict_new( &compiled_format_dict_type, NULL, NULL); if (new_p == NULL) { return (NULL); } info_size = sizeof(*self_p->info_p); info_size += (sizeof(self_p->info_p->fields[0]) * (self_p->info_p->number_of_fields - 1)); new_p->info_p = PyMem_RawMalloc(info_size); if (new_p->info_p == NULL) { /* ToDo: Free new_p. */ return (NULL); } memcpy(new_p->info_p, self_p->info_p, info_size); Py_INCREF(self_p->names_p); new_p->names_p = self_p->names_p; Py_INCREF(self_p->format_p); new_p->format_p = self_p->format_p; return ((PyObject *)new_p); } static PyObject *m_compiled_format_dict_deepcopy(struct compiled_format_dict_t *self_p, PyObject *args_p) { return (m_compiled_format_dict_copy(self_p)); } PyDoc_STRVAR(compile___doc__, "compile(fmt, names=None)\n" "--\n" "\n"); static PyObject *m_compiled_format_dict_getstate(struct compiled_format_dict_t *self_p, PyObject *args_p) { return (Py_BuildValue("{sOsOsi}", "format", self_p->format_p, "names", self_p->names_p, pickle_version_key, pickle_version)); } static PyObject *m_compiled_format_dict_setstate(struct compiled_format_dict_t *self_p, PyObject *state_p) { PyObject *version_p; int version; PyObject *format_p; PyObject *names_p; if (!PyDict_CheckExact(state_p)) { PyErr_SetString(PyExc_ValueError, "Pickled object is not a dict."); return (NULL); } version_p = PyDict_GetItemString(state_p, pickle_version_key); if (version_p == NULL) { PyErr_Format(PyExc_KeyError, "No \"%s\" in pickled dict.", pickle_version_key); return (NULL); } version = (int)PyLong_AsLong(version_p); if (version != pickle_version) { PyErr_Format(PyExc_ValueError, "Pickle version mismatch. Got version %d but expected version %d.", version, pickle_version); return (NULL); } format_p = PyDict_GetItemString(state_p, "format"); if (format_p == NULL) { PyErr_SetString(PyExc_KeyError, "No \"format\" in pickled dict."); return (NULL); } names_p = PyDict_GetItemString(state_p, "names"); if (names_p == NULL) { PyErr_SetString(PyExc_KeyError, "No \"names\" in pickled dict."); return (NULL); } if (compiled_format_dict_init_inner(self_p, format_p, names_p) != 0) { return (NULL); } Py_RETURN_NONE; } static PyObject *m_compile(PyObject *module_p, PyObject *args_p, PyObject *kwargs_p) { PyObject *format_p; PyObject *names_p; int res; static char *keywords[] = { "fmt", "names", NULL }; names_p = Py_None; res = PyArg_ParseTupleAndKeywords(args_p, kwargs_p, "O|O", &keywords[0], &format_p, &names_p); if (res == 0) { return (NULL); } if (names_p == Py_None) { return (compiled_format_create(&compiled_format_type, format_p)); } else { return (compiled_format_dict_create(&compiled_format_dict_type, format_p, names_p)); } } static struct PyMethodDef methods[] = { { "pack", m_pack, METH_VARARGS, pack___doc__ }, { "unpack", (PyCFunction)m_unpack, METH_VARARGS | METH_KEYWORDS, unpack___doc__ }, { "pack_into", (PyCFunction)m_pack_into, METH_VARARGS | METH_KEYWORDS, pack_into___doc__ }, { "unpack_from", (PyCFunction)m_unpack_from, METH_VARARGS | METH_KEYWORDS, unpack_from___doc__ }, { "pack_dict", m_pack_dict, METH_VARARGS, pack_dict___doc__ }, { "unpack_dict", (PyCFunction)m_unpack_dict, METH_VARARGS | METH_KEYWORDS, unpack_dict___doc__ }, { "pack_into_dict", (PyCFunction)m_pack_into_dict, METH_VARARGS | METH_KEYWORDS, pack_into_dict___doc__ }, { "unpack_from_dict", (PyCFunction)m_unpack_from_dict, METH_VARARGS | METH_KEYWORDS, unpack_from_dict___doc__ }, { "calcsize", m_calcsize, METH_O, calcsize___doc__ }, { "byteswap", (PyCFunction)m_byteswap, METH_VARARGS | METH_KEYWORDS, byteswap___doc__ }, { "compile", (PyCFunction)m_compile, METH_VARARGS | METH_KEYWORDS, compile___doc__ }, { NULL } }; static PyModuleDef module = { PyModuleDef_HEAD_INIT, .m_name = "bitstruct.c", .m_doc = "bitstruct C extension", .m_size = -1, .m_methods = methods }; PyMODINIT_FUNC PyInit_c(void) { PyObject *module_p; if (PyType_Ready(&compiled_format_type) < 0) { return (NULL); } if (PyType_Ready(&compiled_format_dict_type) < 0) { return (NULL); } py_zero_p = PyLong_FromLong(0); module_p = PyModule_Create(&module); if (module_p == NULL) { return (NULL); } Py_INCREF(&compiled_format_type); if (PyModule_AddObject(module_p, "CompiledFormat", (PyObject *)&compiled_format_type) < 0) { Py_DECREF(&compiled_format_type); Py_DECREF(module_p); return (NULL); } if (PyModule_AddObject(module_p, "CompiledFormatDict", (PyObject *)&compiled_format_dict_type) < 0) { Py_DECREF(&compiled_format_dict_type); Py_DECREF(module_p); return (NULL); } return (module_p); } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9104946 bitstruct-8.20.0/src/bitstruct.egg-info/0000755000175100001660000000000014756336142017546 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225633.0 bitstruct-8.20.0/src/bitstruct.egg-info/PKG-INFO0000644000175100001660000001627714756336141020657 0ustar00runnerdockerMetadata-Version: 2.2 Name: bitstruct Version: 8.20.0 Summary: This module performs conversions between Python values and C bit field structs represented as Python byte strings. Author: Ilya Petukhov Author-email: Erik Moqvist License: MIT Project-URL: Documentation, https://bitstruct.readthedocs.io/en/latest/ Project-URL: Issues, https://github.com/eerimoq/bitstruct/issues Project-URL: Source, https://github.com/eerimoq/bitstruct Project-URL: Homepage, https://github.com/eerimoq/bitstruct Keywords: bit field,bit parsing,bit unpack,bit pack Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.7 Description-Content-Type: text/x-rst License-File: LICENSE About ===== This module is intended to have a similar interface as the python struct module, but working on bits instead of primitive data types (char, int, ...). Project homepage: https://github.com/eerimoq/bitstruct Documentation: https://bitstruct.readthedocs.io Installation ============ .. code-block:: python pip install bitstruct Performance =========== Parts of this package has been re-implemented in C for faster pack and unpack operations. There are two independent C implementations; `bitstruct.c`, which is part of this package, and the standalone package `cbitstruct`_. These implementations are only available in CPython 3, and must be explicitly imported. By default the pure Python implementation is used. To use `bitstruct.c`, do ``import bitstruct.c as bitstruct``. To use `cbitstruct`_, do ``import cbitstruct as bitstruct``. `bitstruct.c` has a few limitations compared to the pure Python implementation: - Integers and booleans must be 64 bits or less. - Text and raw must be a multiple of 8 bits. - Bit endianness and byte order are not yet supported. - ``byteswap()`` can only swap 1, 2, 4 and 8 bytes. See `cbitstruct`_ for its limitations. MicroPython =========== The C implementation has been ported to `MicroPython`_. See `bitstruct-micropython`_ for more details. Example usage ============= A basic example of `packing`_ and `unpacking`_ four integers using the format string ``'u1u3u4s16'``: .. code-block:: python >>> from bitstruct import * >>> pack('u1u3u4s16', 1, 2, 3, -4) b'\xa3\xff\xfc' >>> unpack('u1u3u4s16', b'\xa3\xff\xfc') (1, 2, 3, -4) >>> calcsize('u1u3u4s16') 24 An example `compiling`_ the format string once, and use it to `pack`_ and `unpack`_ data: .. code-block:: python >>> import bitstruct >>> cf = bitstruct.compile('u1u3u4s16') >>> cf.pack(1, 2, 3, -4) b'\xa3\xff\xfc' >>> cf.unpack(b'\xa3\xff\xfc') (1, 2, 3, -4) Use the `pack into`_ and `unpack from`_ functions to pack/unpack values at a bit offset into the data, in this example the bit offset is 5: .. code-block:: python >>> from bitstruct import * >>> data = bytearray(b'\x00\x00\x00\x00') >>> pack_into('u1u3u4s16', data, 5, 1, 2, 3, -4) >>> data bytearray(b'\x05\x1f\xff\xe0') >>> unpack_from('u1u3u4s16', data, 5) (1, 2, 3, -4) The unpacked values can be named by assigning them to variables or by wrapping the result in a named tuple: .. code-block:: python >>> from bitstruct import * >>> from collections import namedtuple >>> MyName = namedtuple('myname', ['a', 'b', 'c', 'd']) >>> unpacked = unpack('u1u3u4s16', b'\xa3\xff\xfc') >>> myname = MyName(*unpacked) >>> myname myname(a=1, b=2, c=3, d=-4) >>> myname.c 3 Use the `pack_dict`_ and `unpack_dict`_ functions to pack/unpack values in dictionaries: .. code-block:: python >>> from bitstruct import * >>> names = ['a', 'b', 'c', 'd'] >>> pack_dict('u1u3u4s16', names, {'a': 1, 'b': 2, 'c': 3, 'd': -4}) b'\xa3\xff\xfc' >>> unpack_dict('u1u3u4s16', names, b'\xa3\xff\xfc') {'a': 1, 'b': 2, 'c': 3, 'd': -4} An example of `packing`_ and `unpacking`_ an unsigned integer, a signed integer, a float, a boolean, a byte string and a string: .. code-block:: python >>> from bitstruct import * >>> pack('u5s5f32b1r13t40', 1, -1, 3.75, True, b'\xff\xff', 'hello') b'\x0f\xd0\x1c\x00\x00?\xffhello' >>> unpack('u5s5f32b1r13t40', b'\x0f\xd0\x1c\x00\x00?\xffhello') (1, -1, 3.75, True, b'\xff\xf8', 'hello') >>> calcsize('u5s5f32b1r13t40') 96 The same format string and values as in the previous example, but using LSB (Least Significant Bit) first instead of the default MSB (Most Significant Bit) first: .. code-block:: python >>> from bitstruct import * >>> pack('>> unpack('>> calcsize('>> from bitstruct import * >>> from binascii import unhexlify >>> unpack('s17s13r24', unhexlify('0123456789abcdef')) (582, -3751, b'\xe2j\xf3') >>> with open("test.bin", "rb") as fin: ... unpack('s17s13r24', fin.read(8)) ... ... (582, -3751, b'\xe2j\xf3') Change endianness of the data with `byteswap`_, and then unpack the values: .. code-block:: python >>> from bitstruct import * >>> packed = pack('u1u3u4s16', 1, 2, 3, 1) >>> unpack('u1u3u4s16', byteswap('12', packed)) (1, 2, 3, 256) A basic example of `packing`_ and `unpacking`_ four integers using the format string ``'u1u3u4s16'`` using the C implementation: .. code-block:: python >>> from bitstruct.c import * >>> pack('u1u3u4s16', 1, 2, 3, -4) b'\xa3\xff\xfc' >>> unpack('u1u3u4s16', b'\xa3\xff\xfc') (1, 2, 3, -4) Contributing ============ #. Fork the repository. #. Install prerequisites. .. code-block:: text pip install -r requirements.txt #. Implement the new feature or bug fix. #. Implement test case(s) to ensure that future changes do not break legacy. #. Run the tests. .. code-block:: text make test #. Create a pull request. .. _packing: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack .. _unpacking: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack .. _pack: http://bitstruct.readthedocs.io/en/latest/#bitstruct.CompiledFormat.pack .. _unpack: http://bitstruct.readthedocs.io/en/latest/#bitstruct.CompiledFormat.unpack .. _pack into: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack_into .. _unpack from: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack_from .. _pack_dict: http://bitstruct.readthedocs.io/en/latest/#bitstruct.pack_dict .. _unpack_dict: http://bitstruct.readthedocs.io/en/latest/#bitstruct.unpack_dict .. _byteswap: http://bitstruct.readthedocs.io/en/latest/#bitstruct.byteswap .. _compiling: http://bitstruct.readthedocs.io/en/latest/#bitstruct.compile .. _cbitstruct: https://github.com/qchateau/cbitstruct .. _MicroPython: https://github.com/micropython/micropython .. _bitstruct-micropython: https://github.com/peterzuger/bitstruct-micropython ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225633.0 bitstruct-8.20.0/src/bitstruct.egg-info/SOURCES.txt0000644000175100001660000000064514756336141021436 0ustar00runnerdockerLICENSE MANIFEST.in Makefile README.rst pyproject.toml setup.py docs/Makefile docs/conf.py docs/index.rst docs/make.bat src/bitstruct/__init__.py src/bitstruct/bitstream.c src/bitstruct/bitstream.h src/bitstruct/c.c src/bitstruct.egg-info/PKG-INFO src/bitstruct.egg-info/SOURCES.txt src/bitstruct.egg-info/dependency_links.txt src/bitstruct.egg-info/top_level.txt tests/__init__.py tests/test_bitstruct.py tests/test_c.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225633.0 bitstruct-8.20.0/src/bitstruct.egg-info/dependency_links.txt0000644000175100001660000000000114756336141023613 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225633.0 bitstruct-8.20.0/src/bitstruct.egg-info/top_level.txt0000644000175100001660000000001214756336141022270 0ustar00runnerdockerbitstruct ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1740225633.9094946 bitstruct-8.20.0/tests/0000755000175100001660000000000014756336142014404 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/tests/__init__.py0000644000175100001660000000000014756336136016506 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/tests/test_bitstruct.py0000644000175100001660000005747714756336136020067 0ustar00runnerdockerfrom __future__ import print_function import sys import timeit import unittest from bitstruct import * import bitstruct class BitStructTest(unittest.TestCase): def test_pack(self): """Pack values. """ packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') packed = pack('u1', 1) self.assertEqual(packed, b'\x80') packed = pack('u77', 0x100000000001000000) ref = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00' self.assertEqual(packed, ref) packed = pack('u8000', int(8000 * '1', 2)) ref = 1000 * b'\xff' self.assertEqual(packed, ref) packed = pack('s4000', int(8000 * '0', 2)) ref = 500 * b'\x00' self.assertEqual(packed, ref) packed = pack('p1u1s6u7u9', 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') packed = pack('P1u1s6u7u9', 0, -2, 65, 22) self.assertEqual(packed, b'\xbe\x82\x16') packed = pack('p1u1s6p7u9', 0, -2, 22) self.assertEqual(packed, b'\x3e\x00\x16') packed = pack('P1u1s6p7u9', 0, -2, 22) self.assertEqual(packed, b'\xbe\x00\x16') packed = pack('u1s6f32r43', 0, -2, 3.75, b'\x00\xff\x00\xff\x00\xff') self.assertEqual(packed, b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0') packed = pack('b1', True) self.assertEqual(packed, b'\x80') packed = pack('b1p6b1', True, True) self.assertEqual(packed, b'\x81') packed = pack('b1P6b1', True, True) self.assertEqual(packed, b'\xff') packed = pack('u5b2u1', 31, False, 1) self.assertEqual(packed, b'\xf9') packed = pack('b1t24', False, u'Hi!') self.assertEqual(packed, b'$4\x90\x80') packed = pack('b1t24', False, 'Hi!') self.assertEqual(packed, b'$4\x90\x80') packed = pack('t8000', 1000 * '7') self.assertEqual(packed, 1000 * b'\x37') if sys.version_info >= (3, 6): packed = pack('f16', 1.0) self.assertEqual(packed, b'\x3c\x00') packed = pack('f32', 1.0) self.assertEqual(packed, b'\x3f\x80\x00\x00') packed = pack('f64', 1.0) self.assertEqual(packed, b'\x3f\xf0\x00\x00\x00\x00\x00\x00') # Too many values to pack. with self.assertRaises(Error) as cm: pack('b1t24', False) self.assertEqual(str(cm.exception), 'pack expected 2 item(s) for packing (got 1)') # Cannot convert argument to integer. with self.assertRaises(ValueError) as cm: pack('u1', 'foo') self.assertEqual(str(cm.exception), "invalid literal for int() with base 10: 'foo'") # Cannot convert argument to float. with self.assertRaises(ValueError) as cm: pack('f32', 'foo') if sys.version_info[0] < 3: self.assertEqual(str(cm.exception), 'could not convert string to float: foo') else: self.assertEqual(str(cm.exception), "could not convert string to float: 'foo'") # Cannot convert argument to bytearray. with self.assertRaises(TypeError) as cm: pack('r5', 1.0) self.assertIn("'float' has no", str(cm.exception)) # Cannot encode argument as utf-8. with self.assertRaises(AttributeError) as cm: pack('t8', 1.0) self.assertEqual(str(cm.exception), "'float' object has no attribute 'encode'") def test_unpack(self): """Unpack values. """ unpacked = unpack('u1u1s6u7u9', b'\x3e\x82\x16') self.assertEqual(unpacked, (0, 0, -2, 65, 22)) unpacked = unpack('u1', bytearray(b'\x80')) self.assertEqual(unpacked, (1, )) packed = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00' unpacked = unpack('u77', packed) self.assertEqual(unpacked, (0x100000000001000000,)) packed = 1000 * b'\xff' unpacked = unpack('u8000', packed) self.assertEqual(unpacked, (int(8000 * '1', 2), )) packed = 500 * b'\x00' unpacked = unpack('s4000', packed) self.assertEqual(unpacked, (0, )) packed = b'\xbe\x82\x16' unpacked = unpack('P1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\x3e\x82\x16' unpacked = unpack('P1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\xbe\x82\x16' unpacked = unpack('p1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\x3e\x82\x16' unpacked = unpack('p1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\x3e\x82\x16' unpacked = unpack('p1u1s6p7u9', packed) self.assertEqual(unpacked, (0, -2, 22)) packed = b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0' unpacked = unpack('u1s6f32r43', packed) self.assertEqual(unpacked, (0, -2, 3.75, b'\x00\xff\x00\xff\x00\xe0')) packed = bytearray(b'\x80') unpacked = unpack('b1', packed) self.assertEqual(unpacked, (True, )) packed = b'\x80' unpacked = unpack('b1p6b1', packed) self.assertEqual(unpacked, (True, False)) packed = b'\x06' unpacked = unpack('u5b2u1', packed) self.assertEqual(unpacked, (0, True, 0)) packed = b'\x04' unpacked = unpack('u5b2u1', packed) self.assertEqual(unpacked, (0, True, 0)) packed = b'$4\x90\x80' unpacked = unpack('b1t24', packed) self.assertEqual(unpacked, (False, u'Hi!')) packed = 1000 * b'7' unpacked = unpack('t8000', packed) self.assertEqual(packed, 1000 * b'\x37') # Bad float size. with self.assertRaises(Error) as cm: unpack('f33', b'\x00\x00\x00\x00\x00') self.assertEqual(str(cm.exception), 'expected float size of 16, 32, or 64 bits (got 33)') # Too many bits to unpack. with self.assertRaises(Error) as cm: unpack('u9', b'\x00') # partial unpacking of truncated data unpacked = unpack('u4u5', b'\x5f', allow_truncated=True) self.assertEqual(unpacked, (5, )) unpacked = unpack('p8u4u5', b'\x00\x5f', allow_truncated=True) self.assertEqual(unpacked, (5, )) self.assertEqual(str(cm.exception), 'unpack requires at least 9 bits to unpack (got 8)') # gcc packed struct with bitfields # # struct foo_t { # int a; # char b; # uint32_t c : 7; # uint32_t d : 25; # } foo; # # foo.a = 1; # foo.b = 1; # foo.c = 0x67; # foo.d = 0x12345; unpacked = unpack('s32s8u25u7', byteswap('414', b'\x01\x00\x00\x00\x01\xe7\xa2\x91\x00')) self.assertEqual(unpacked, (1, 1, 0x12345, 0x67)) def test_pack_unpack(self): """Pack and unpack values. """ packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) unpacked = unpack('u1u1s6u7u9', packed) self.assertEqual(unpacked, (0, 0, -2, 65, 22)) packed = pack('f64', 1.0) unpacked = unpack('f64', packed) self.assertEqual(unpacked, (1.0, )) if sys.version_info >= (3, 6): packed = pack('f16', 1.0) unpacked = unpack('f16', packed) self.assertEqual(unpacked, (1.0, )) def test_calcsize(self): """Calculate size. """ size = calcsize('u1u1s6u7u9') self.assertEqual(size, 24) size = calcsize('u1') self.assertEqual(size, 1) size = calcsize('u77') self.assertEqual(size, 77) size = calcsize('u1s6u7u9') self.assertEqual(size, 23) size = calcsize('b1s6u7u9p1t8') self.assertEqual(size, 32) size = calcsize('b1s6u7u9P1t8') self.assertEqual(size, 32) def test_byteswap(self): """Byte swap. """ res = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a' ref = b'\x01\x03\x02\x04\x08\x07\x06\x05\x0a\x09' self.assertEqual(byteswap('12142', ref), res) packed = pack('u1u5u2u16', 1, 2, 3, 4) unpacked = unpack('u1u5u2u16', byteswap('12', packed)) self.assertEqual(unpacked, (1, 2, 3, 1024)) def test_endianness(self): """Test pack/unpack with endianness information in the format string. """ # Big endian. ref = b'\x02\x46\x9a\xfe\x00\x00\x00' packed = pack('>u19s3f32', 0x1234, -2, -1.0) self.assertEqual(packed, ref) unpacked = unpack('>u19s3f32', packed) self.assertEqual(unpacked, (0x1234, -2, -1.0)) # Little endian. ref = b'\x2c\x48\x0c\x00\x00\x07\xf4' packed = pack('u19f64r3p4', 1, -2, 1.0, b'\x80') self.assertEqual(packed, ref) unpacked = unpack('>u19f64r3p4', packed) self.assertEqual(unpacked, (1, -2, 1.0, b'\x80')) # Opposite endianness of the 'mixed endianness' test. ref = b'\x80\x00\x1e\x00\x00\x00\x00\x00\x00\x0f\xfc\x20' packed = pack('s5s5', 0x1234, -2, -1.0) self.assertEqual(packed, ref) unpacked = unpack('u19s3f32>', packed) self.assertEqual(unpacked, (0x1234, -2, -1.0)) # Least significant byte first. ref = b'\x34\x12\x18\x00\x00\xe0\xbc' packed = pack('u19s3f32<', 0x1234, -2, -1.0) self.assertEqual(packed, ref) unpacked = unpack('u19s3f32<', packed) self.assertEqual(unpacked, (0x1234, -2, -1.0)) # Least significant byte first. ref = b'\x34\x12' packed = pack('u8s8<', 0x34, 0x12) self.assertEqual(packed, ref) unpacked = unpack('u8s8<', packed) self.assertEqual(unpacked, (0x34, 0x12)) # Least significant byte first. ref = b'\x34\x22' packed = pack('u3u12<', 1, 0x234) self.assertEqual(packed, ref) unpacked = unpack('u3s12<', packed) self.assertEqual(unpacked, (1, 0x234)) # Least significant byte first. ref = b'\x34\x11\x00' packed = pack('u3u17<', 1, 0x234) self.assertEqual(packed, ref) unpacked = unpack('u3s17<', packed) self.assertEqual(unpacked, (1, 0x234)) # Least significant byte first. ref = b'\x80' packed = pack('u1<', 1) self.assertEqual(packed, ref) unpacked = unpack('u1<', packed) self.assertEqual(unpacked, (1, )) # Least significant byte first. ref = b'\x45\x23\x25\x82' packed = pack('u19u5u1u7<', 0x12345, 5, 1, 2) self.assertEqual(packed, ref) unpacked = unpack('u19u5u1u7<', packed) self.assertEqual(unpacked, (0x12345, 5, 1, 2)) # Least significant byte first does not affect raw and text. ref = b'123abc' packed = pack('r24t24<', b'123', 'abc') self.assertEqual(packed, ref) unpacked = unpack('r24t24<', packed) self.assertEqual(unpacked, (b'123', 'abc')) def test_compile(self): cf = bitstruct.compile('u1u1s6u7u9') packed = cf.pack(0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') unpacked = cf.unpack(b'\x3e\x82\x16') self.assertEqual(unpacked, (0, 0, -2, 65, 22)) def test_signed_integer(self): """Pack and unpack signed integer values. """ datas = [ ('s2', 0x01, b'\x40'), ('s3', 0x03, b'\x60'), ('s4', 0x07, b'\x70'), ('s5', 0x0f, b'\x78'), ('s6', 0x1f, b'\x7c'), ('s7', 0x3f, b'\x7e'), ('s8', 0x7f, b'\x7f'), ('s9', 0xff, b'\x7f\x80'), ('s1', -1, b'\x80'), ('s2', -1, b'\xc0') ] for fmt, value, packed in datas: self.assertEqual(pack(fmt, value), packed) self.assertEqual(unpack(fmt, packed), (value, )) def test_unsigned_integer(self): """Pack and unpack unsigned integer values. """ datas = [ ('u1', 0x001, b'\x80'), ('u2', 0x003, b'\xc0'), ('u3', 0x007, b'\xe0'), ('u4', 0x00f, b'\xf0'), ('u5', 0x01f, b'\xf8'), ('u6', 0x03f, b'\xfc'), ('u7', 0x07f, b'\xfe'), ('u8', 0x0ff, b'\xff'), ('u9', 0x1ff, b'\xff\x80') ] for fmt, value, packed in datas: self.assertEqual(pack(fmt, value), packed) self.assertEqual(unpack(fmt, packed), (value, )) def test_bad_float_size(self): """Test of bad float size. """ with self.assertRaises(Error) as cm: pack('f31', 1.0) self.assertEqual(str(cm.exception), 'expected float size of 16, 32, or 64 bits (got 31)') with self.assertRaises(Error) as cm: unpack('f33', 8 * b'\x00') self.assertEqual(str(cm.exception), 'expected float size of 16, 32, or 64 bits (got 33)') def test_bad_format(self): """Test of bad format. """ formats = [ ('g1', "bad char 'g' in format"), ('s1u1f32b1t8r8G13', "bad char 'G' in format"), ('s1u1f32b1t8r8G13S3', "bad char 'G' in format"), ('s', "bad format 's'"), ('1', "bad format '1'"), ('ss1', "bad format 'ss1'"), ('1s', "bad format '1s'"), ('foo', "bad format 'foo'"), ('s>1>', "bad format 's>1>'"), ('s0', "bad format 's0'") ] for fmt, expected_error in formats: with self.assertRaises(Error) as cm: bitstruct.compile(fmt) self.assertEqual(str(cm.exception), expected_error) def test_empty_format(self): """Test of empty format type. """ cf = bitstruct.compile('') self.assertEqual(cf.pack(), b'') self.assertEqual(cf.pack(1), b'') self.assertEqual(cf.unpack(b''), ()) self.assertEqual(cf.unpack(b'\x00'), ()) def test_byte_order_format(self): """Test of a format with only byte order information. """ cf = bitstruct.compile('>') self.assertEqual(cf.pack(), b'') self.assertEqual(cf.pack(1), b'') self.assertEqual(cf.unpack(b''), ()) self.assertEqual(cf.unpack(b'\x00'), ()) def test_pack_into(self): """Pack values into a buffer. """ packed = bytearray(3) pack_into('u1u1s6u7u9', packed, 0, 0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') datas = [ (0, b'\x80\x00'), (1, b'\x40\x00'), (7, b'\x01\x00'), (15, b'\x00\x01') ] for offset, expected in datas: packed = bytearray(2) pack_into('u1', packed, offset, 1) self.assertEqual(packed, expected) packed = bytearray(b'\xff\xff\xff') pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=False) self.assertEqual(packed, b'\xf1\xf2\xf3') packed = bytearray(b'\xff\xff\xff') pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=True) self.assertEqual(packed, b'\x01\x02\x03') packed = bytearray(2) with self.assertRaises(Error) as cm: pack_into('u17', packed, 0, 1) self.assertEqual(str(cm.exception), 'pack_into requires a buffer of at least 17 bits') packed = bytearray(b'\x00') pack_into('P4u4', packed, 0, 1) self.assertEqual(packed, b'\xf1') # Too many values to pack. with self.assertRaises(Error) as cm: packed = bytearray(b'\x00') pack_into('b1t24', packed, 0, False) self.assertEqual(str(cm.exception), 'pack expected 2 item(s) for packing (got 1)') def test_unpack_from(self): """Unpack values at given bit offset. """ unpacked = unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b\x00', 1) self.assertEqual(unpacked, (0, 0, -2, 65, 22)) with self.assertRaises(Error) as cm: unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b', 1) self.assertEqual(str(cm.exception), 'unpack requires at least 24 bits to unpack ' '(got 23)') def test_pack_integers_value_checks(self): """Pack integer values range checks. """ # Formats with minimum and maximum allowed values. datas = [ ('s1', -1, 0), ('s2', -2, 1), ('s3', -4, 3), ('u1', 0, 1), ('u2', 0, 3), ('u3', 0, 7) ] for fmt, minimum, maximum in datas: # No exception should be raised for numbers in range. pack(fmt, minimum) pack(fmt, maximum) # Numbers out of range. for number in [minimum - 1, maximum + 1]: with self.assertRaises(Error) as cm: pack(fmt, number) self.assertEqual( str(cm.exception), '"{}" requires {} <= integer <= {} (got {})'.format( fmt, minimum, maximum, number)) def test_pack_unpack_raw(self): """Pack and unpack raw values. """ packed = pack('r24', b'') self.assertEqual(packed, b'\x00\x00\x00') packed = pack('r24', b'12') self.assertEqual(packed, b'12\x00') packed = pack('r24', b'123') self.assertEqual(packed, b'123') packed = pack('r24', b'1234') self.assertEqual(packed, b'123') unpacked = unpack('r24', b'\x00\x00\x00')[0] self.assertEqual(unpacked, b'\x00\x00\x00') unpacked = unpack('r24', b'12\x00')[0] self.assertEqual(unpacked, b'12\x00') unpacked = unpack('r24', b'123')[0] self.assertEqual(unpacked, b'123') unpacked = unpack('r24', b'1234')[0] self.assertEqual(unpacked, b'123') def test_pack_unpack_text(self): """Pack and unpack text values. """ packed = pack('t24', '') self.assertEqual(packed, b'\x00\x00\x00') packed = pack('t24', '12') self.assertEqual(packed, b'12\x00') packed = pack('t24', '123') self.assertEqual(packed, b'123') packed = pack('t24', '1234') self.assertEqual(packed, b'123') unpacked = unpack('t24', b'\x00\x00\x00')[0] self.assertEqual(unpacked, '\x00\x00\x00') unpacked = unpack('t24', b'12\x00')[0] self.assertEqual(unpacked, '12\x00') unpacked = unpack('t24', b'123')[0] self.assertEqual(unpacked, '123') unpacked = unpack('t24', b'1234')[0] self.assertEqual(unpacked, '123') unpacked = unpack('t8', b'\xff', text_errors='replace')[0] self.assertEqual(unpacked, '�') unpacked = unpack('t8', b'\xff', text_errors='ignore')[0] self.assertEqual(unpacked, '') cf = bitstruct.compile('t8', text_encoding='latin-1', text_errors='replace') unpacked = cf.unpack(b'\xff')[0] self.assertEqual(unpacked, 'ÿ') cf = bitstruct.compile('t8', text_encoding='utf-8', text_errors='ignore') unpacked = cf.unpack(b'\xff')[0] self.assertEqual(unpacked, '') cf = bitstruct.compile('t8', names=['a'], text_encoding='utf-8', text_errors='replace') unpacked = cf.unpack(b'\xff') self.assertEqual(unpacked, {'a': '�'}) def test_pack_unpack_dict(self): unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } packed = b'\x3e\x82\x16' fmt = 'u1u1s6u7u9' names = ['foo', 'bar', 'fie', 'fum', 'fam'] self.assertEqual(pack_dict(fmt, names, unpacked), packed) self.assertEqual(unpack_dict(fmt, names, packed), unpacked) def test_pack_into_unpack_from_dict(self): unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } packed = b'\x3e\x82\x16' fmt = 'u1u1s6u7u9' names = ['foo', 'bar', 'fie', 'fum', 'fam'] actual = bytearray(3) pack_into_dict(fmt, names, actual, 0, unpacked) self.assertEqual(actual, packed) self.assertEqual(unpack_from_dict(fmt, names, packed), unpacked) def test_pack_dict_missing_key(self): unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65 } fmt = 'u1u1s6u7u9' names = ['foo', 'bar', 'fie', 'fum', 'fam'] with self.assertRaises(Error) as cm: pack_dict(fmt, names, unpacked) self.assertEqual(str(cm.exception), "'fam' not found in data dictionary") with self.assertRaises(Error) as cm: data = bytearray(3) pack_into_dict(fmt, names, data, 0, unpacked) self.assertEqual(str(cm.exception), "'fam' not found in data dictionary") def test_compile_pack_unpack_formats(self): fmts = [ ('u1s2p3', None, (1, -1)), ('u1 s2 p3', None, (1, -1)), ('u1s2p3', ['a', 'b'], {'a': 1, 'b': -1}) ] for fmt, names, decoded in fmts: if names is None: cf = bitstruct.compile(fmt) packed_1 = cf.pack(*decoded) packed_2 = pack(fmt, *decoded) else: cf = bitstruct.compile(fmt, names) packed_1 = cf.pack(decoded) packed_2 = pack_dict(fmt, names, decoded) self.assertEqual(packed_1, b'\xe0') self.assertEqual(packed_2, b'\xe0') def test_compile_formats(self): bitstruct.compile('p1u1') bitstruct.compile('p1u1', ['a']) def test_pack_unpack_signed(self): datas = [ ('s1', 0, b'\x00'), ('s1', -1, b'\x80'), ('s63', -1, b'\xff\xff\xff\xff\xff\xff\xff\xfe'), ('s64', -1, b'\xff\xff\xff\xff\xff\xff\xff\xff') ] for fmt, value, packed in datas: self.assertEqual(pack(fmt, value), packed) self.assertEqual(unpack(fmt, packed), (value, )) def test_pack_unpack_unsigned(self): datas = [ ('u1', 0, b'\x00'), ('u1', 1, b'\x80'), ('u63', 0x1234567890abcdef, b'$h\xac\xf1!W\x9b\xde'), ('u64', 0x1234567890abcdef, b'\x124Vx\x90\xab\xcd\xef') ] for fmt, value, packed in datas: self.assertEqual(pack(fmt, value), packed) self.assertEqual(unpack(fmt, packed), (value, )) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1740225630.0 bitstruct-8.20.0/tests/test_c.py0000644000175100001660000006254714756336136016260 0ustar00runnerdockerfrom __future__ import print_function import sys import timeit import pickle import unittest import platform import copy def is_cpython_3(): if platform.python_implementation() != 'CPython': return False if sys.version_info[0] < 3: return False return True if is_cpython_3(): import bitstruct.c from bitstruct.c import * else: print('Skipping C extension tests for non-CPython 3.') class CTest(unittest.TestCase): def test_pack(self): """Pack values. """ if not is_cpython_3(): return packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') packed = pack('u1', 1) self.assertEqual(packed, b'\x80') with self.assertRaises(NotImplementedError): packed = pack('u77', 0x100000000001000000) ref = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00' self.assertEqual(packed, ref) with self.assertRaises(NotImplementedError): packed = pack('u8000', int(8000 * '1', 2)) ref = 1000 * b'\xff' self.assertEqual(packed, ref) with self.assertRaises(NotImplementedError): packed = pack('s4000', int(8000 * '0', 2)) ref = 500 * b'\x00' self.assertEqual(packed, ref) packed = pack('p1u1s6u7u9', 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') packed = pack('P1u1s6u7u9', 0, -2, 65, 22) self.assertEqual(packed, b'\xbe\x82\x16') packed = pack('p1u1s6p7u9', 0, -2, 22) self.assertEqual(packed, b'\x3e\x00\x16') packed = pack('P1u1s6p7u9', 0, -2, 22) self.assertEqual(packed, b'\xbe\x00\x16') with self.assertRaises(NotImplementedError): packed = pack('u1s6f32r43', 0, -2, 3.75, b'\x00\xff\x00\xff\x00\xff') self.assertEqual(packed, b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0') packed = pack('b1', True) self.assertEqual(packed, b'\x80') packed = pack('b1p6b1', True, True) self.assertEqual(packed, b'\x81') packed = pack('b1P6b1', True, True) self.assertEqual(packed, b'\xff') packed = pack('u5b2u1', 31, False, 1) self.assertEqual(packed, b'\xf9') packed = pack('b1t24', False, u'Hi!') self.assertEqual(packed, b'$4\x90\x80') packed = pack('b1t24', False, 'Hi!') self.assertEqual(packed, b'$4\x90\x80') packed = pack('t8000', 1000 * '7') self.assertEqual(packed, 1000 * b'\x37') if sys.version_info >= (3, 6): packed = pack('f16', 1.0) self.assertEqual(packed, b'\x3c\x00') packed = pack('f32', 1.0) self.assertEqual(packed, b'\x3f\x80\x00\x00') packed = pack('f64', 1.0) self.assertEqual(packed, b'\x3f\xf0\x00\x00\x00\x00\x00\x00') def test_unpack(self): """Unpack values. """ if not is_cpython_3(): return unpacked = unpack('u1u1s6u7u9', b'\x3e\x82\x16') self.assertEqual(unpacked, (0, 0, -2, 65, 22)) # unpacked = unpack('u1', bytearray(b'\x80')) unpacked = unpack('u1', b'\x80') self.assertEqual(unpacked, (1, )) with self.assertRaises(NotImplementedError): packed = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00' unpacked = unpack('u77', packed) self.assertEqual(unpacked, (0x100000000001000000,)) with self.assertRaises(NotImplementedError): packed = 1000 * b'\xff' unpacked = unpack('u8000', packed) self.assertEqual(unpacked, (int(8000 * '1', 2), )) with self.assertRaises(NotImplementedError): packed = 500 * b'\x00' unpacked = unpack('s4000', packed) self.assertEqual(unpacked, (0, )) packed = b'\xbe\x82\x16' unpacked = unpack('P1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\x3e\x82\x16' unpacked = unpack('P1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\xbe\x82\x16' unpacked = unpack('p1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\x3e\x82\x16' unpacked = unpack('p1u1s6u7u9', packed) self.assertEqual(unpacked, (0, -2, 65, 22)) packed = b'\x3e\x82\x16' unpacked = unpack('p1u1s6p7u9', packed) self.assertEqual(unpacked, (0, -2, 22)) with self.assertRaises(NotImplementedError): packed = b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0' unpacked = unpack('u1s6f32r43', packed) self.assertEqual(unpacked, (0, -2, 3.75, b'\x00\xff\x00\xff\x00\xe0')) # packed = bytearray(b'\x80') packed = b'\x80' unpacked = unpack('b1', packed) self.assertEqual(unpacked, (True, )) packed = b'\x80' unpacked = unpack('b1p6b1', packed) self.assertEqual(unpacked, (True, False)) packed = b'\x06' unpacked = unpack('u5b2u1', packed) self.assertEqual(unpacked, (0, True, 0)) packed = b'\x04' unpacked = unpack('u5b2u1', packed) self.assertEqual(unpacked, (0, True, 0)) packed = b'$4\x90\x80' unpacked = unpack('b1t24', packed) self.assertEqual(unpacked, (False, u'Hi!')) packed = 1000 * b'7' unpacked = unpack('t8000', packed) self.assertEqual(packed, 1000 * b'\x37') if sys.version_info >= (3, 6): unpacked = unpack('f16', b'\x3c\x00') self.assertEqual(unpacked, (1.0, )) unpacked = unpack('f32', b'\x3f\x80\x00\x00') self.assertEqual(unpacked, (1.0, )) unpacked = unpack('f64', b'\x3f\xf0\x00\x00\x00\x00\x00\x00') self.assertEqual(unpacked, (1.0, )) # Too many bits to unpack. with self.assertRaises(ValueError) as cm: unpack('u9', b'\x00') self.assertEqual(str(cm.exception), 'Short data.') # partial unpacking of truncated data unpacked = unpack('u4u5', b'\x5f', allow_truncated=True) self.assertEqual(unpacked, (5, )) unpacked = unpack('p8u4u5', b'\x00\x5f', allow_truncated=True) self.assertEqual(unpacked, (5, )) # test bytearray unpacking unpacked = unpack('u1u1s6u7u9', bytearray(b'\x3e\x82\x16')) self.assertEqual(unpacked, (0, 0, -2, 65, 22)) def test_pack_unpack(self): """Pack and unpack values. """ if not is_cpython_3(): return packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) unpacked = unpack('u1u1s6u7u9', packed) self.assertEqual(unpacked, (0, 0, -2, 65, 22)) packed = pack('f64', 1.0) unpacked = unpack('f64', packed) self.assertEqual(unpacked, (1.0, )) if sys.version_info >= (3, 6): packed = pack('f16', 1.0) unpacked = unpack('f16', packed) self.assertEqual(unpacked, (1.0, )) def test_calcsize(self): """Calculate size. """ if not is_cpython_3(): return size = calcsize('u1u1s6u7u9') self.assertEqual(size, 24) size = calcsize('u1') self.assertEqual(size, 1) size = calcsize('u1s6u7u9') self.assertEqual(size, 23) size = calcsize('b1s6u7u9p1t8') self.assertEqual(size, 32) size = calcsize('b1s6u7u9P1t8') self.assertEqual(size, 32) def test_compiled_calcsize(self): """Calculate size. """ if not is_cpython_3(): return cf = bitstruct.c.compile('u1u1s6u7u9') self.assertEqual(cf.calcsize(), 24) cf = bitstruct.c.compile('u1u1s6u7u9', ['a', 'b', 'c', 'd', 'e']) self.assertEqual(cf.calcsize(), 24) def test_byteswap(self): """Byte swap. """ if not is_cpython_3(): return res = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a' ref = b'\x01\x03\x02\x04\x08\x07\x06\x05\x0a\x09' self.assertEqual(byteswap('12142', ref), res) packed = pack('u1u5u2u16', 1, 2, 3, 4) unpacked = unpack('u1u5u2u16', byteswap('12', packed)) self.assertEqual(unpacked, (1, 2, 3, 1024)) res = b'\x01\x02\x03\x04\x05\x06\x07\x08' ref = b'\x08\x07\x06\x05\x04\x03\x02\x01' self.assertEqual(byteswap('8', ref), res) def test_pack_into(self): """Pack values into a buffer. """ if not is_cpython_3(): return packed = bytearray(3) pack_into('u1u1s6u7u9', packed, 0, 0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') datas = [ (0, b'\x80\x00'), (1, b'\x40\x00'), (7, b'\x01\x00'), (15, b'\x00\x01') ] for offset, expected in datas: packed = bytearray(2) pack_into('u1', packed, offset, 1) self.assertEqual(packed, expected) with self.assertRaises(AssertionError): packed = bytearray(b'\xff\xff\xff') pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=False) self.assertEqual(packed, b'\xf1\xf2\xf3') packed = bytearray(b'\xff\xff\xff') pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=True) self.assertEqual(packed, b'\x01\x02\x03') packed = bytearray(2) with self.assertRaises(ValueError) as cm: pack_into('u17', packed, 0, 1) self.assertEqual(str(cm.exception), 'pack_into requires a buffer of at least 17 bits') packed = bytearray(b'\x00') pack_into('P4u4', packed, 0, 1) self.assertEqual(packed, b'\xf1') datas = [ (0, b'\x7f\xff\xff\xff'), (1, b'\xbf\xff\xff\xff'), (7, b'\xfe\xff\xff\xff'), (8, b'\xff\x7f\xff\xff'), (15, b'\xff\xfe\xff\xff'), (16, b'\xff\xff\x7f\xff'), (31, b'\xff\xff\xff\xfe') ] for offset, expected in datas: packed = bytearray(b'\xff\xff\xff\xff') pack_into('u1', packed, offset, 0) self.assertEqual(packed, expected) # Check for non-writable buffer. with self.assertRaises(TypeError): pack_into('u1', b'\x00', 0, 0) def test_unpack_from(self): """Unpack values at given bit offset. """ if not is_cpython_3(): return unpacked = unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b\x00', 1) self.assertEqual(unpacked, (0, 0, -2, 65, 22)) unpacked = unpack_from('u1', b'\x80') self.assertEqual(unpacked, (1, )) unpacked = unpack_from('u1', b'\x08', 4) self.assertEqual(unpacked, (1, )) with self.assertRaises(ValueError): unpack_from('u1', b'\xff', 8) with self.assertRaises(TypeError): unpack_from('u1', b'\xff', None) def test_compiled_pack_into(self): """Pack values at given bit offset. """ if not is_cpython_3(): return cf = bitstruct.c.compile('u2') packed = bytearray(2) cf.pack_into(packed, 7, 3) self.assertEqual(packed, b'\x01\x80') def test_compiled_unpack_from(self): """Unpack values at given bit offset. """ if not is_cpython_3(): return cf = bitstruct.c.compile('u1') unpacked = cf.unpack_from(b'\x40', 1) self.assertEqual(unpacked, (1, )) unpacked = cf.unpack_from(b'\x80') self.assertEqual(unpacked, (1, )) def test_pack_unpack_raw(self): """Pack and unpack raw values. """ if not is_cpython_3(): return with self.assertRaises(NotImplementedError): packed = pack('r24', b'') self.assertEqual(packed, b'\x00\x00\x00') with self.assertRaises(NotImplementedError): packed = pack('r24', b'12') self.assertEqual(packed, b'12\x00') packed = pack('r24', b'123') self.assertEqual(packed, b'123') packed = pack('r24', b'1234') self.assertEqual(packed, b'123') unpacked = unpack('r24', b'\x00\x00\x00')[0] self.assertEqual(unpacked, b'\x00\x00\x00') unpacked = unpack('r24', b'12\x00')[0] self.assertEqual(unpacked, b'12\x00') unpacked = unpack('r24', b'123')[0] self.assertEqual(unpacked, b'123') unpacked = unpack('r24', b'1234')[0] self.assertEqual(unpacked, b'123') def test_pack_unpack_dict(self): if not is_cpython_3(): return unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } packed = b'\x3e\x82\x16' fmt = 'u1u1s6u7u9' names = ['foo', 'bar', 'fie', 'fum', 'fam'] self.assertEqual(pack_dict(fmt, names, unpacked), packed) self.assertEqual(unpack_dict(fmt, names, packed), unpacked) def test_pack_into_unpack_from_dict(self): if not is_cpython_3(): return unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } packed = b'\x3e\x82\x16' fmt = 'u1u1s6u7u9' names = ['foo', 'bar', 'fie', 'fum', 'fam'] actual = bytearray(3) pack_into_dict(fmt, names, actual, 0, unpacked) self.assertEqual(actual, packed) self.assertEqual(unpack_from_dict(fmt, names, packed), unpacked) def test_compiled_pack_into_unpack_from_dict(self): if not is_cpython_3(): return unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } packed = b'\x3e\x82\x16' fmt = 'u1u1s6u7u9' names = ['foo', 'bar', 'fie', 'fum', 'fam'] cf = bitstruct.c.compile(fmt, names) actual = bytearray(3) cf.pack_into(actual, 0, unpacked) self.assertEqual(actual, packed) self.assertEqual(cf.unpack_from(packed), unpacked) def test_compile(self): if not is_cpython_3(): return cf = bitstruct.c.compile('u1u1s6u7u9') packed = cf.pack(0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') unpacked = cf.unpack(b'\x3e\x82\x16') self.assertEqual(unpacked, (0, 0, -2, 65, 22)) def test_compile_pack_unpack_formats(self): if not is_cpython_3(): return fmts = [ ('u1s2p3', None, (1, -1)), ('u1 s2 p3', None, (1, -1)), ('u1s2p3', ['a', 'b'], {'a': 1, 'b': -1}) ] for fmt, names, decoded in fmts: if names is None: cf = bitstruct.c.compile(fmt) packed_1 = cf.pack(*decoded) packed_2 = pack(fmt, *decoded) else: cf = bitstruct.c.compile(fmt, names) packed_1 = cf.pack(decoded) packed_2 = pack_dict(fmt, names, decoded) self.assertEqual(packed_1, b'\xe0') self.assertEqual(packed_2, b'\xe0') if names is None: unpacked_1 = cf.unpack(packed_1) unpacked_2 = unpack(fmt, packed_2) unpacked_3 = cf.unpack(bytearray(packed_1)) # test bytearray unpacking else: unpacked_1 = cf.unpack(packed_1) unpacked_2 = unpack_dict(fmt, names, packed_2) unpacked_3 = cf.unpack(bytearray(packed_1)) # test bytearray unpacking self.assertEqual(unpacked_1, decoded) self.assertEqual(unpacked_2, decoded) self.assertEqual(unpacked_3, decoded) def test_compile_formats(self): if not is_cpython_3(): return bitstruct.c.compile('p1u1') bitstruct.c.compile('p1u1', ['a']) with self.assertRaises(TypeError): bitstruct.c.compile() def test_pack_unpack_signed(self): if not is_cpython_3(): return datas = [ ('s1', 0, b'\x00'), ('s1', -1, b'\x80'), ('s63', -1, b'\xff\xff\xff\xff\xff\xff\xff\xfe'), ('s64', -1, b'\xff\xff\xff\xff\xff\xff\xff\xff') ] for fmt, value, packed in datas: self.assertEqual(pack(fmt, value), packed) self.assertEqual(unpack(fmt, packed), (value, )) def test_pack_unpack_unsigned(self): if not is_cpython_3(): return datas = [ ('u1', 0, b'\x00'), ('u1', 1, b'\x80'), ('u63', 0x1234567890abcdef, b'$h\xac\xf1!W\x9b\xde'), ('u64', 0x1234567890abcdef, b'\x124Vx\x90\xab\xcd\xef') ] for fmt, value, packed in datas: self.assertEqual(pack(fmt, value), packed) self.assertEqual(unpack(fmt, packed), (value, )) def test_various(self): if not is_cpython_3(): return with self.assertRaises(ValueError): pack('u89999888888888888888899', 1) # Fewer names than fields in the format. with self.assertRaises(ValueError): pack_dict('u1u1', ['foo'], {'foo': 1}) # Missing value for name. with self.assertRaises(KeyError): pack_dict('u1', ['foo'], {}) # Fewer names than fields in the format. with self.assertRaises(ValueError): unpack_dict('u1u1', ['foo'], b'\xff') # Short data. with self.assertRaises(ValueError): unpack_dict('u1', ['foo'], b'') # Padding last. self.assertEqual(pack('u1p1', 1), b'\x80') self.assertEqual(pack_dict('u1p1', ['foo'], {'foo': 1}), b'\x80') # Short text. with self.assertRaises(NotImplementedError) as cm: pack('t16', '1') self.assertEqual(str(cm.exception), 'Short text.') # Bad float length. with self.assertRaises(NotImplementedError) as cm: pack('f1', 1.0) if sys.version_info >= (3, 6): self.assertEqual(str(cm.exception), 'Float not 16, 32 or 64 bits.') else: self.assertEqual(str(cm.exception), 'Float not 32 or 64 bits.') # Long bool. with self.assertRaises(NotImplementedError) as cm: pack('b65', True) self.assertEqual(str(cm.exception), 'Bool over 64 bits.') # Text not multiple of 8 bits. with self.assertRaises(NotImplementedError) as cm: pack('t1', '') self.assertEqual(str(cm.exception), 'Text not multiple of 8 bits.') # Bad format kind. with self.assertRaises(ValueError) as cm: pack('x1', '') self.assertEqual(str(cm.exception), "Bad format field type 'x'.") # Too few arguments. with self.assertRaises(ValueError) as cm: pack('u1u1', 1) self.assertEqual(str(cm.exception), 'Too few arguments.') # No format string. with self.assertRaises(ValueError) as cm: pack() self.assertEqual(str(cm.exception), 'No format string.') # No format string. with self.assertRaises(ValueError) as cm: unpack('u1', b'') self.assertEqual(str(cm.exception), 'Short data.') # Bad format in compile. with self.assertRaises(ValueError) as cm: bitstruct.c.compile('x1') self.assertEqual(str(cm.exception), "Bad format field type 'x'.") # Bad format in compile dict. with self.assertRaises(ValueError) as cm: bitstruct.c.compile('x1', ['foo']) self.assertEqual(str(cm.exception), "Bad format field type 'x'.") # Offset too big. with self.assertRaises(ValueError) as cm: packed = bytearray(1) pack_into('u1', packed, 0x80000000, 1) self.assertIn("Offset must be less or equal to ", str(cm.exception)) # Offset too big. with self.assertRaises(ValueError) as cm: packed = bytearray(1) pack_into_dict('u1', ['a'], packed, 0x80000000, {'a': 1}) self.assertIn("Offset must be less or equal to ", str(cm.exception)) # Offset too big. with self.assertRaises(ValueError) as cm: unpack_from('u1', b'\x00', 0x80000000) self.assertIn("Offset must be less or equal to ", str(cm.exception)) # Offset too big. with self.assertRaises(ValueError) as cm: packed = bytearray(1) unpack_from_dict('u1', ['a'], b'\x00', 0x80000000) self.assertIn("Offset must be less or equal to ", str(cm.exception)) # Out of data to swap. datas = ['11', '2', '4', '8'] for fmt in datas: with self.assertRaises(ValueError) as cm: byteswap(fmt, b'\x00') self.assertEqual(str(cm.exception), 'Out of data to swap.') # Bad swap format. with self.assertRaises(ValueError) as cm: byteswap('3', b'\x00') # Bad swap format type. with self.assertRaises(TypeError): byteswap(None, b'\x00') self.assertEqual(str(cm.exception), 'Expected 1, 2, 4 or 8, but got 3.') # Bad format type. with self.assertRaises(TypeError): pack(None, 1) # Out of range checks. datas = [ ('s64', (1 << 63)), ('s64', -(1 << 63) - 1), ('u64', 1 << 64), ('u64', -1), ('u1', 2), ('s1', 1), ('s1', -2), ('s2', 2) ] for fmt, value in datas: with self.assertRaises(OverflowError) as cm: pack(fmt, value) # Bad value types. with self.assertRaises(TypeError) as cm: pack('s1', None) with self.assertRaises(TypeError) as cm: pack('u1', None) with self.assertRaises(TypeError) as cm: pack('f32', None) with self.assertRaises(TypeError) as cm: pack('r8', None) with self.assertRaises(TypeError) as cm: pack('t8', None) # Everything can be bool. self.assertEqual(pack('b8', None), b'\x00') # Zero bits bool. with self.assertRaises(ValueError) as cm: pack('b0', None) self.assertEqual(str(cm.exception), 'Field of size 0.') # pack/unpack dict with names as a non-list. Caused an segfault. with self.assertRaises(TypeError) as cm: pack_into_dict('u1', None, bytearray(b'\x00'), 0, {'a': 1}) self.assertEqual(str(cm.exception), 'Names is not a list.') with self.assertRaises(TypeError) as cm: pack_dict('u1', None, {'a': 1}) self.assertEqual(str(cm.exception), 'Names is not a list.') with self.assertRaises(TypeError) as cm: bitstruct.c.compile('u1', 1) self.assertEqual(str(cm.exception), 'Names is not a list.') def test_whitespaces(self): if not is_cpython_3(): return fmts = [ ' ', ' u1 s2 p3 ' ] for fmt in fmts: bitstruct.c.compile(fmt) def test_copy(self): if not is_cpython_3(): return cf = bitstruct.c.compile('u1u1s6u7u9') cf = copy.copy(cf) packed = cf.pack(0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') cf = copy.deepcopy(cf) packed = cf.pack(0, 0, -2, 65, 22) self.assertEqual(packed, b'\x3e\x82\x16') def test_copy_dict(self): if not is_cpython_3(): return unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } names = ['foo', 'bar', 'fie', 'fum', 'fam'] cf = bitstruct.c.compile('u1u1s6u7u9', names) cf = copy.copy(cf) packed = cf.pack(unpacked) self.assertEqual(packed, b'\x3e\x82\x16') self.assertEqual(cf.unpack(packed), unpacked) cf = copy.deepcopy(cf) packed = cf.pack(unpacked) self.assertEqual(packed, b'\x3e\x82\x16') self.assertEqual(cf.unpack(packed), unpacked) def test_pickle(self): if not is_cpython_3(): return unpacked = (0, 0, -2, 65, 22) cf = bitstruct.c.compile('u1u1s6u7u9') cf = pickle.loads(pickle.dumps(cf)) packed = cf.pack(*unpacked) self.assertEqual(packed, b'\x3e\x82\x16') self.assertEqual(cf.unpack(packed), unpacked) # Dump and load once more. cf = pickle.loads(pickle.dumps(cf)) packed = cf.pack(*unpacked) self.assertEqual(packed, b'\x3e\x82\x16') self.assertEqual(cf.unpack(packed), unpacked) def test_pickle_dict(self): if not is_cpython_3(): return unpacked = { 'foo': 0, 'bar': 0, 'fie': -2, 'fum': 65, 'fam': 22 } names = ['foo', 'bar', 'fie', 'fum', 'fam'] cf = bitstruct.c.compile('u1u1s6u7u9', names) cf = pickle.loads(pickle.dumps(cf)) packed = cf.pack(unpacked) self.assertEqual(packed, b'\x3e\x82\x16') self.assertEqual(cf.unpack(packed), unpacked) # Dump and load once more. cf = pickle.loads(pickle.dumps(cf)) packed = cf.pack(unpacked) self.assertEqual(packed, b'\x3e\x82\x16') self.assertEqual(cf.unpack(packed), unpacked) if __name__ == '__main__': unittest.main()