├── cbitstruct ├── tests │ ├── __init__.py │ ├── test_perf.py │ ├── test_against_bitstruct.py │ ├── test_cornercase.py │ ├── test_api.py │ ├── test_bitstruct.py │ └── original │ │ └── test_bitstruct.py ├── __init__.py ├── clinic │ ├── _cbitstruct.c.313.h │ ├── _cbitstruct.c.36.h │ └── _cbitstruct.c.37.h └── _cbitstruct.c ├── MANIFEST.in ├── .gitignore ├── .ci └── coverage.sh ├── pyproject.toml ├── .github └── workflows │ └── build_and_publish.yml ├── setup.py ├── .clang-format ├── README.md └── LICENSE.md /cbitstruct/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include cbitstruct/clinic/*.h 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history/ 2 | .vscode/ 3 | build/ 4 | tmp/ 5 | dist/ 6 | __pycache__/ 7 | wheelhouse/ 8 | compile_commands.json 9 | 10 | *.so 11 | *.egg-info/ 12 | *.pyc 13 | 14 | clinic/_cbitstruct.c.h 15 | MANIFEST 16 | -------------------------------------------------------------------------------- /.ci/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -x 3 | 4 | export COVERAGE=1 5 | 6 | python -m pip install .[test] 7 | python -m pip install -U cpp-coveralls 8 | 9 | # Move to a different directory to avoid picking-up the local files. 10 | # Alternatively, in Python 3.11+ use the interpreter's -P option to ignore local files. 11 | mkdir ci-coverage && cd ci-coverage 12 | python -m unittest discover cbitstruct 13 | coveralls --exclude clinic --gcov-options '\-lp' 14 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.cibuildwheel] 6 | build = [ 7 | "cp36-manylinux_x86_64", 8 | "cp37-manylinux_x86_64", 9 | "cp38-manylinux_x86_64", 10 | "cp39-manylinux_x86_64", 11 | "cp310-manylinux_x86_64", 12 | "cp311-manylinux_x86_64", 13 | "cp312-manylinux_x86_64", 14 | "cp313-manylinux_x86_64", 15 | ] 16 | test-extras = "test" 17 | test-command = "python -m unittest discover cbitstruct" 18 | -------------------------------------------------------------------------------- /cbitstruct/__init__.py: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | from ._cbitstruct import ( 6 | pack, 7 | pack_into, 8 | pack_dict, 9 | pack_into_dict, 10 | unpack, 11 | unpack_from, 12 | unpack_dict, 13 | unpack_from_dict, 14 | calcsize, 15 | byteswap, 16 | compile, 17 | CompiledFormat, 18 | CompiledFormatDict, 19 | ) 20 | -------------------------------------------------------------------------------- /.github/workflows/build_and_publish.yml: -------------------------------------------------------------------------------- 1 | name: Python package build and publish 2 | 3 | on: push 4 | 5 | jobs: 6 | build_sdist: 7 | name: Build source distribution 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | 12 | - name: Build sdist 13 | run: pipx run build --sdist 14 | 15 | - uses: actions/upload-artifact@v4 16 | with: 17 | name: sources 18 | path: dist/*.tar.gz 19 | 20 | build_wheels: 21 | name: Build and test manylinux wheels 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Build wheels 28 | uses: pypa/cibuildwheel@v2.21.1 29 | 30 | - uses: actions/upload-artifact@v4 31 | with: 32 | name: wheel 33 | path: ./wheelhouse/*.whl 34 | 35 | publish_pypi: 36 | name: Publish sdist and wheels to PyPI 37 | needs: [build_sdist, build_wheels] 38 | runs-on: ubuntu-latest 39 | environment: pypi 40 | permissions: 41 | id-token: write 42 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 43 | steps: 44 | - uses: actions/download-artifact@v4.1.8 45 | with: 46 | merge-multiple: true 47 | path: dist 48 | 49 | - uses: pypa/gh-action-pypi-publish@release/v1 50 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | import os 6 | import sys 7 | from setuptools import setup, Extension 8 | 9 | 10 | extra_compile_args = [] 11 | extra_link_args = [] 12 | undef_macros = [] 13 | 14 | 15 | if sys.platform != "win32": 16 | extra_compile_args.extend(("-std=c11", "-Wall", "-Werror", "-O3")) 17 | 18 | 19 | if os.environ.get("COVERAGE"): 20 | extra_compile_args.extend(("-g", "-O0", "-fprofile-arcs", "-ftest-coverage")) 21 | extra_link_args.append("-fprofile-arcs") 22 | undef_macros.append("NDEBUG") 23 | 24 | 25 | with open("README.md", "r") as fh: 26 | long_description = fh.read() 27 | 28 | 29 | setup( 30 | name="cbitstruct", 31 | version="1.1.1", 32 | author="Quentin CHATEAU", 33 | author_email="quentin.chateau@gmail.com", 34 | license="MPL-2.0", 35 | url="https://github.com/qchateau/cbitstruct", 36 | description="Faster C implementation of bitstruct", 37 | long_description=long_description, 38 | long_description_content_type="text/markdown", 39 | python_requires=">=3.6", 40 | classifiers=[ 41 | "Programming Language :: Python :: 3", 42 | "Programming Language :: C", 43 | "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", 44 | "Operating System :: OS Independent", 45 | ], 46 | keywords=[ 47 | "bit", 48 | "pack", 49 | "unpack", 50 | "struct", 51 | "bitfield", 52 | "bit parsing", 53 | "bit unpack", 54 | "bit pack", 55 | "C", 56 | ], 57 | extras_require={"test": ["bitstruct"]}, 58 | ext_modules=[ 59 | Extension( 60 | "cbitstruct._cbitstruct", 61 | extra_compile_args=extra_compile_args, 62 | extra_link_args=extra_link_args, 63 | sources=["cbitstruct/_cbitstruct.c"], 64 | include_dirs=["cbitstruct/"], 65 | undef_macros=undef_macros, 66 | ) 67 | ], 68 | packages=["cbitstruct", "cbitstruct.tests"], 69 | ) 70 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: Google 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Left 8 | AlignOperands: true 9 | AlignTrailingComments: false 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: Inline 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: true 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: false 21 | BinPackParameters: false 22 | BreakBeforeBinaryOperators: NonAssignment 23 | BreakBeforeBraces: Stroustrup 24 | BreakBeforeInheritanceComma: false 25 | BreakBeforeTernaryOperators: true 26 | BreakConstructorInitializers: BeforeColon 27 | BreakStringLiterals: true 28 | ColumnLimit: 80 29 | CommentPragmas: '^ IWYU pragma:' 30 | CompactNamespaces: false 31 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 32 | ConstructorInitializerIndentWidth: 4 33 | ContinuationIndentWidth: 4 34 | Cpp11BracedListStyle: true 35 | DerivePointerAlignment: false 36 | DisableFormat: false 37 | ExperimentalAutoDetectBinPacking: false 38 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 39 | FixNamespaceComments: false 40 | IncludeBlocks: Preserve 41 | IncludeCategories: 42 | - Regex: '^<[a-z_]+>$' 43 | Priority: 10 44 | - Regex: '^<.*>$' 45 | Priority: 50 46 | - Regex: '.*' 47 | Priority: 1 48 | IncludeIsMainRegex: '([-_](test|unittest))?$' 49 | IndentCaseLabels: false 50 | IndentPPDirectives: None 51 | IndentWidth: 4 52 | IndentWrappedFunctionNames: false 53 | KeepEmptyLinesAtTheStartOfBlocks: false 54 | MacroBlockBegin: '' 55 | MacroBlockEnd: '' 56 | MaxEmptyLinesToKeep: 1 57 | NamespaceIndentation: None 58 | PenaltyBreakAssignment: 50 59 | PenaltyBreakBeforeFirstCallParameter: 100 60 | PenaltyBreakComment: 300 61 | PenaltyBreakFirstLessLess: 0 62 | PenaltyBreakString: 100 63 | PenaltyExcessCharacter: 10 64 | PenaltyReturnTypeOnItsOwnLine: 10000 65 | PointerAlignment: Left 66 | ReflowComments: true 67 | SortIncludes: true 68 | SortUsingDeclarations: true 69 | SpaceAfterCStyleCast: false 70 | SpaceAfterTemplateKeyword: true 71 | SpaceBeforeAssignmentOperators: true 72 | SpaceBeforeCpp11BracedList: false 73 | SpaceBeforeCtorInitializerColon: true 74 | SpaceBeforeInheritanceColon: true 75 | SpaceBeforeParens: ControlStatements 76 | SpaceBeforeRangeBasedForLoopColon: true 77 | SpaceInEmptyParentheses: false 78 | SpacesBeforeTrailingComments: 1 79 | SpacesInAngles: false 80 | SpacesInContainerLiterals: false 81 | SpacesInCStyleCastParentheses: false 82 | SpacesInParentheses: false 83 | SpacesInSquareBrackets: false 84 | Standard: Cpp11 85 | TabWidth: 4 86 | UseTab: Never 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![License](https://img.shields.io/badge/license-MPL--2.0-brightgreen) [![Build Status](https://travis-ci.com/qchateau/cbitstruct.svg?branch=master)](https://travis-ci.com/qchateau/cbitstruct) 2 | 3 | # About 4 | 5 | `cbitstruct` is a C implementation of the `bitstruct` library. Credits to Erik Moqvist for the original `bitstruct` library available on [Github](https://github.com/eerimoq/bitstruct) and [pip](https://pypi.org/project/bitstruct/). 6 | 7 | The goal is to provide the same API an idealy to be able to interchange `import bitstruct` and `import cbitstruct as bitstruct`. 8 | 9 | Obvious increased performance comes with limitations described below. 10 | 11 | # Installation 12 | 13 | ```bash 14 | pip3 install cbitstruct 15 | ``` 16 | 17 | # Documentation 18 | 19 | Please refer to the [`bitstruct` documentation](https://bitstruct.readthedocs.io/en/latest/) as the aim of this library is to provide the same API with increased performance. 20 | 21 | If you are not used to `bitstruct`, you should seriously consider using it first, before migrating to this library if you require higher performance. 22 | 23 | # Limitations 24 | 25 | | Limitation | Will it be lifted ? | 26 | |------------|---------------------| 27 | | All types except padding are limited to 64 bits | Maybe for 'raw' and 'text' types | 28 | | May not work on big-endian architectures | Maybe | 29 | | Exceptions differ from `bitstruct` | Probably not | 30 | | CPython only | Probably not | 31 | | Error messages are unclear | Will never be as clear as `bitstruct` | 32 | | Python >= 3.5 | No | 33 | 34 | Some limitations are there because I did not get the time or motivation to lift them up. Some other are deeply rooted into this library and may never be lifted. 35 | 36 | # Performance 37 | 38 | ## Comparing to `bitstruct` 39 | 40 | The script available in `tests/test_perf.py` measures performance comparing to the `bitstruct` library. 41 | 42 | Here are the result "on my machine" (Ubuntu in Virtualbox on a laptop): 43 | ``` 44 | byteswap list of int | x 8.779 ( 8.638us -> 0.984us) 45 | byteswap str | x 17.466 ( 9.158us -> 0.524us) 46 | calcsize | x139.330 ( 61.060us -> 0.438us) 47 | compiled pack | x 47.389 ( 35.968us -> 0.759us) 48 | compiled pack_dict | x 27.184 ( 34.588us -> 1.272us) 49 | compiled pack_into | x 32.037 ( 38.650us -> 1.206us) 50 | compiled pack_into_dict | x 27.343 ( 37.718us -> 1.379us) 51 | compiled unpack | x 33.928 ( 31.278us -> 0.922us) 52 | compiled unpack_dict | x 21.627 ( 31.597us -> 1.461us) 53 | compiled unpack_from | x 30.622 ( 29.977us -> 0.979us) 54 | compiled unpack_from_dict | x 20.479 ( 30.936us -> 1.511us) 55 | pack | x 77.003 ( 103.030us -> 1.338us) 56 | pack_dict | x 53.254 ( 103.255us -> 1.939us) 57 | pack_into | x 82.829 ( 119.373us -> 1.441us) 58 | pack_into_dict | x 52.173 ( 108.135us -> 2.073us) 59 | unpack | x 78.459 ( 91.896us -> 1.171us) 60 | unpack_dict | x 40.287 ( 89.300us -> 2.217us) 61 | unpack_from | x 77.027 ( 91.202us -> 1.184us) 62 | unpack_from_dict | x 39.467 ( 88.043us -> 2.231us) 63 | ``` 64 | 65 | *Disclaimer:* these results may and will vary largely depending on the number of elements and types you pack/unpack. This script is provided as-is, and I will gladly accept an improved script providing more reliable results. 66 | 67 | 68 | ## The dict API 69 | The `dict` API is marginally slower than the traditional one. As the packing/unpacking performance is quite high, the overhead of performing dictionary lookups and hashing significantly increas pack and unpacking duration. 70 | -------------------------------------------------------------------------------- /cbitstruct/tests/test_perf.py: -------------------------------------------------------------------------------- 1 | import time 2 | import cbitstruct 3 | import bitstruct 4 | import timeit 5 | import unittest 6 | 7 | import string 8 | import random 9 | import cbitstruct 10 | import bitstruct 11 | 12 | fmt = "s12u45p127f32f64s32r13p2P8r32u16u2s12" 13 | nbytes = (bitstruct.calcsize(fmt) + 7) // 8 14 | 15 | random.seed(0) 16 | data = bytes([random.randint(0, 255) for _ in range(nbytes)]) 17 | dst = bytearray([0] * nbytes) 18 | values = bitstruct.unpack(fmt, data) 19 | names = string.ascii_letters[: len(values)] 20 | values_dict = {n: v for n, v in zip(names, values)} 21 | 22 | bs = bitstruct.compile(fmt) 23 | cbs = cbitstruct.compile(fmt) 24 | 25 | dbs = bitstruct.compile(fmt, names) 26 | cdbs = cbitstruct.compile(fmt, names) 27 | 28 | NBS = 10000 29 | NCBS = 100000 30 | 31 | 32 | class PerfTest(unittest.TestCase): 33 | def generic(self, name, bitstruct, cbitstruct): 34 | bstime = timeit.timeit(bitstruct, number=NBS, globals=globals()) / NBS 35 | cbstime = timeit.timeit(cbitstruct, number=NCBS, globals=globals()) / NCBS 36 | improvement = bstime / cbstime 37 | 38 | print( 39 | "\r{:<25} | x{:>7.3f} ".format(name, improvement) 40 | + "({:>8.3f}us -> {:>7.3f}us)".format(1e6 * bstime, 1e6 * cbstime) 41 | ) 42 | 43 | def test_unpack(self): 44 | self.generic( 45 | "unpack", "bitstruct.unpack(fmt, data)", "cbitstruct.unpack(fmt, data)" 46 | ) 47 | 48 | def test_unpack_from(self): 49 | self.generic( 50 | "unpack_from", 51 | "bitstruct.unpack_from(fmt, data, 3)", 52 | "cbitstruct.unpack_from(fmt, data, 3)", 53 | ) 54 | 55 | def test_unpack_dict(self): 56 | self.generic( 57 | "unpack_dict", 58 | "bitstruct.unpack_dict(fmt, names, data)", 59 | "cbitstruct.unpack_dict(fmt, names, data)", 60 | ) 61 | 62 | def test_unpack_from_dict(self): 63 | self.generic( 64 | "unpack_from_dict", 65 | "bitstruct.unpack_from_dict(fmt, names, data, 3)", 66 | "cbitstruct.unpack_from_dict(fmt, names, data, 3)", 67 | ) 68 | 69 | def test_compiled_unpack(self): 70 | self.generic("compiled unpack", "bs.unpack(data)", "cbs.unpack(data)") 71 | 72 | def test_compiled_unpack_from(self): 73 | self.generic( 74 | "compiled unpack_from", 75 | "bs.unpack_from(data, 3)", 76 | "cbs.unpack_from(data, 3)", 77 | ) 78 | 79 | def test_compiled_unpack_dict(self): 80 | self.generic("compiled unpack_dict", "dbs.unpack(data)", "cdbs.unpack(data)") 81 | 82 | def test_compiled_unpack_from_dict(self): 83 | self.generic( 84 | "compiled unpack_from_dict", 85 | "dbs.unpack_from(data, 3)", 86 | "cdbs.unpack_from(data, 3)", 87 | ) 88 | 89 | def test_pack(self): 90 | self.generic( 91 | "pack", "bitstruct.pack(fmt, *values)", "cbitstruct.pack(fmt, *values)" 92 | ) 93 | 94 | def test_pack_into(self): 95 | self.generic( 96 | "pack_into", 97 | "bitstruct.pack_into(fmt, dst, 3, *values)", 98 | "cbitstruct.pack_into(fmt, dst, 3, *values)", 99 | ) 100 | 101 | def test_pack_dict(self): 102 | self.generic( 103 | "pack_dict", 104 | "bitstruct.pack_dict(fmt, names, values_dict)", 105 | "cbitstruct.pack_dict(fmt, names, values_dict)", 106 | ) 107 | 108 | def test_pack_into_dict(self): 109 | self.generic( 110 | "pack_into_dict", 111 | "bitstruct.pack_into_dict(fmt, names, dst, 3, values_dict)", 112 | "cbitstruct.pack_into_dict(fmt, names, dst, 3, values_dict)", 113 | ) 114 | 115 | def test_compiled_pack(self): 116 | self.generic("compiled pack", "bs.pack(*values)", "cbs.pack(*values)") 117 | 118 | def test_compiled_pack_into(self): 119 | self.generic( 120 | "compiled pack_into", 121 | "bs.pack_into(dst, 3, *values)", 122 | "cbs.pack_into(dst, 3, *values)", 123 | ) 124 | 125 | def test_compiled_pack_dict(self): 126 | self.generic( 127 | "compiled pack_dict", "dbs.pack(values_dict)", "cdbs.pack(values_dict)" 128 | ) 129 | 130 | def test_compiled_pack_into_dict(self): 131 | self.generic( 132 | "compiled pack_into_dict", 133 | "dbs.pack_into(dst, 3, values_dict)", 134 | "cdbs.pack_into(dst, 3, values_dict)", 135 | ) 136 | 137 | def test_calcsize(self): 138 | self.generic("calcsize", "bitstruct.calcsize(fmt)", "cbitstruct.calcsize(fmt)") 139 | 140 | def test_byteswap_str(self): 141 | self.generic( 142 | "byteswap str", 143 | "bitstruct.byteswap('123456789', data, 0)", 144 | "cbitstruct.byteswap('123456789', data, 0)", 145 | ) 146 | 147 | def test_byteswap_integer(self): 148 | self.generic( 149 | "byteswap list of int", 150 | "bitstruct.byteswap([1,2,3,4,5,6,7,8,9], data, 0)", 151 | "cbitstruct.byteswap([1,2,3,4,5,6,7,8,9], data, 0)", 152 | ) 153 | 154 | 155 | if __name__ == "__main__": 156 | unittest.main() 157 | -------------------------------------------------------------------------------- /cbitstruct/tests/test_against_bitstruct.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import random 3 | import cbitstruct 4 | import bitstruct 5 | import string 6 | import math 7 | import copy 8 | import sys 9 | 10 | 11 | MAX_BITS = 64 12 | MAX_OFFSET = 16 13 | MAX_ELEMENTS = 100 14 | ENDIANNESS = ["", ">", "<"] 15 | BITORDER = ["", ">", "<"] 16 | FLOAT_BITS = ( 17 | [16, 32, 64] if sys.version_info[0] == 3 and sys.version_info[1] >= 6 else [32, 64] 18 | ) 19 | TEXT_CHOICES = string.ascii_letters + string.digits 20 | 21 | 22 | def random_bits(nbits): 23 | nbytes = (nbits + 7) // 8 24 | return bytes([random.randint(0, 255) for _ in range(nbytes)]) 25 | 26 | 27 | NBITS_GENERATORS = { 28 | "u": lambda: random.randint(1, MAX_BITS), 29 | "s": lambda: random.randint(1, MAX_BITS), 30 | "f": lambda: random.choice(FLOAT_BITS), 31 | # bitstruct does not handle incomplete bytes and text very consitently 32 | # and has no unit tests against it, so just don't check these cases 33 | "t": lambda: 8 * random.randint(1, MAX_BITS // 8), 34 | "r": lambda: 8 * random.randint(1, MAX_BITS // 8), 35 | "p": lambda: random.randint(1, 1000), 36 | "P": lambda: random.randint(1, 1000), 37 | } 38 | VALUE_GENERATORS = { 39 | "u": lambda n: random.randint(0, 2 ** n - 1), 40 | "s": lambda n: random.randint(-2 ** (n - 1), 2 ** (n - 1) - 1), 41 | "f": lambda n: random.uniform(-100, 100), 42 | "t": lambda n: "".join([random.choice(TEXT_CHOICES) for _ in range(n // 8)]), 43 | "r": lambda n: random_bits(n), 44 | "p": lambda n: None, 45 | "P": lambda n: None, 46 | } 47 | 48 | 49 | def generate_fmt(): 50 | nelements = random.randint(1, MAX_ELEMENTS) 51 | endianness = random.choice(ENDIANNESS) 52 | 53 | fmt = "" 54 | values = list() 55 | for _ in range(nelements): 56 | one_fmt, one_value = generate_one() 57 | fmt += one_fmt 58 | if one_value is not None: 59 | values.append(one_value) 60 | fmt += endianness 61 | return fmt, values 62 | 63 | 64 | def generate_one(): 65 | bits_order = random.choice(BITORDER) 66 | letter = random.choice(list(NBITS_GENERATORS.keys())) 67 | nbits = NBITS_GENERATORS[letter]() 68 | value = VALUE_GENERATORS[letter](nbits) 69 | return bits_order + letter + str(nbits), value 70 | 71 | 72 | class BitstructConformityTest(unittest.TestCase): 73 | def test_random_pack_unpack(self): 74 | for _ in range(1000): 75 | self.assert_pack_unpack() 76 | self.assert_pack_into_unpack_from() 77 | 78 | def test_byteswap(self): 79 | for _ in range(1000): 80 | self.assert_byteswap_str() 81 | self.assert_byteswap_integer() 82 | 83 | def assert_pack_unpack(self): 84 | fmt, values = generate_fmt() 85 | 86 | expected_data = bitstruct.pack(fmt, *values) 87 | result_data = cbitstruct.pack(fmt, *values) 88 | self.assertEqual(expected_data, result_data, "bad pack for '{}'".format(fmt)) 89 | 90 | expected_values = bitstruct.unpack(fmt, expected_data) 91 | result_values = cbitstruct.unpack(fmt, expected_data) 92 | self.assertValuesEqual( 93 | expected_values, result_values, "bad unpack for '{}'".format(fmt) 94 | ) 95 | 96 | def assert_pack_into_unpack_from(self): 97 | fmt, values = generate_fmt() 98 | offset = random.randint(0, MAX_OFFSET) 99 | fill_padding = random.choice([True, False]) 100 | 101 | dst = bytearray(random_bits(bitstruct.calcsize(fmt) + offset)) 102 | expected_data = dst.copy() 103 | result_data = dst.copy() 104 | 105 | bitstruct.pack_into( 106 | fmt, expected_data, offset, *values, fill_padding=fill_padding 107 | ) 108 | cbitstruct.pack_into( 109 | fmt, result_data, offset, *values, fill_padding=fill_padding 110 | ) 111 | self.assertEqual(expected_data, result_data, "bad pack_into for '{}'".format(fmt)) 112 | 113 | expected_values = bitstruct.unpack_from(fmt, expected_data, offset) 114 | result_values = cbitstruct.unpack_from(fmt, expected_data, offset) 115 | self.assertValuesEqual( 116 | expected_values, result_values, "bad unpack_from for '{}'".format(fmt) 117 | ) 118 | 119 | def assertValuesEqual(self, l1, l2, msg): 120 | for v1, v2 in zip(l1, l2): 121 | if ( 122 | isinstance(v1, float) 123 | and isinstance(v2, float) 124 | and math.isnan(v1) 125 | and math.isnan(v2) 126 | ): 127 | continue 128 | self.assertAlmostEqual(v1, v2, msg=msg) 129 | 130 | def assert_byteswap_str(self): 131 | MAX_BYTES = 300 132 | offset = random.randint(0, MAX_OFFSET) 133 | nbytes = random.randint(1, MAX_BYTES) 134 | data = random_bits((nbytes + offset) * 8) 135 | 136 | fmt = "" 137 | while nbytes > 0: 138 | n = random.randint(1, min(nbytes, 9)) 139 | fmt += str(n) 140 | nbytes -= n 141 | 142 | expected = bitstruct.byteswap(fmt, data, offset) 143 | result = cbitstruct.byteswap(fmt, data, offset) 144 | self.assertEqual(expected, result, "bad byteswap for '{}'".format(fmt)) 145 | 146 | def assert_byteswap_integer(self): 147 | MAX_BYTES = 300 148 | offset = random.randint(0, MAX_OFFSET) 149 | nbytes = random.randint(1, MAX_BYTES) 150 | data = random_bits((nbytes + offset) * 8) 151 | 152 | fmt = list() 153 | while nbytes > 0: 154 | n = random.randint(1, nbytes) 155 | fmt.append(n) 156 | nbytes -= n 157 | 158 | expected = bitstruct.byteswap(fmt, data, offset) 159 | result = cbitstruct.byteswap(fmt, data, offset) 160 | self.assertEqual(expected, result, "bad byteswap for '{}'".format(fmt)) 161 | 162 | 163 | if __name__ == "__main__": 164 | unittest.main() 165 | -------------------------------------------------------------------------------- /cbitstruct/tests/test_cornercase.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import cbitstruct 3 | 4 | 5 | NAMES = ["foo"] 6 | FMT = "u32" 7 | DICT = {"foo": 42} 8 | 9 | 10 | class BitstructApiTest(unittest.TestCase): 11 | def test_no_args(self): 12 | self.assertRaises(Exception, cbitstruct.CompiledFormat) 13 | self.assertRaises(Exception, cbitstruct.CompiledFormatDict) 14 | self.assertRaises(Exception, cbitstruct.compile) 15 | 16 | def test_not_unicode(self): 17 | self.assertRaises(UnicodeDecodeError, cbitstruct.unpack, "t8", b"\xff") 18 | 19 | def test_unpack_too_small(self): 20 | self.assertRaises(TypeError, cbitstruct.unpack, FMT, b"") 21 | 22 | def test_unpack_bad_args(self): 23 | self.assertRaises(TypeError, cbitstruct.unpack) 24 | self.assertRaises(TypeError, cbitstruct.unpack, None) 25 | self.assertRaises(TypeError, cbitstruct.unpack, FMT) 26 | self.assertRaises(TypeError, cbitstruct.unpack, FMT, None) 27 | self.assertRaises(TypeError, cbitstruct.unpack, None, bytes(4)) 28 | 29 | def test_unpack_dict_too_small(self): 30 | self.assertRaises(TypeError, cbitstruct.unpack_dict, FMT, NAMES, b"") 31 | 32 | def test_unpack_dict_bad_args(self): 33 | self.assertRaises(TypeError, cbitstruct.unpack_dict) 34 | self.assertRaises(TypeError, cbitstruct.unpack_dict, None) 35 | self.assertRaises(TypeError, cbitstruct.unpack_dict, FMT, None) 36 | self.assertRaises(TypeError, cbitstruct.unpack_dict, FMT, NAMES, None) 37 | self.assertRaises(TypeError, cbitstruct.unpack_dict, None, NAMES, bytes(4)) 38 | self.assertRaises( 39 | TypeError, cbitstruct.unpack_dict, FMT, NAMES + ["extra"], bytes(4) 40 | ) 41 | 42 | def test_unpack_from_too_small(self): 43 | self.assertRaises(TypeError, cbitstruct.unpack_from, FMT, b"") 44 | 45 | def test_unpack_from_bad_args(self): 46 | self.assertRaises(TypeError, cbitstruct.unpack_from) 47 | self.assertRaises(TypeError, cbitstruct.unpack_from, FMT) 48 | self.assertRaises(TypeError, cbitstruct.unpack_from, None, bytes(4)) 49 | self.assertRaises(TypeError, cbitstruct.unpack_from, FMT, None) 50 | 51 | def test_unpack_from_dict_too_small(self): 52 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, FMT, NAMES, b"") 53 | 54 | def test_unpack_from_dict_bad_args(self): 55 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict) 56 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, FMT) 57 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, FMT, None) 58 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, None, NAMES) 59 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, FMT, None, bytes(4)) 60 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, None, NAMES, bytes(4)) 61 | self.assertRaises( 62 | TypeError, cbitstruct.unpack_from_dict, "g32", NAMES, bytes(4) 63 | ) 64 | self.assertRaises( 65 | TypeError, cbitstruct.unpack_from_dict, FMT, NAMES + ["extra"], bytes(4) 66 | ) 67 | self.assertRaises(TypeError, cbitstruct.unpack_from_dict, FMT, NAMES, None) 68 | 69 | def test_pack_bad_args(self): 70 | self.assertRaises(TypeError, cbitstruct.pack) 71 | self.assertRaises(TypeError, cbitstruct.pack, 0) 72 | 73 | def test_pack_into_too_small(self): 74 | self.assertRaises(TypeError, cbitstruct.pack_into, FMT, b"", 12) 75 | 76 | def test_pack_into_bad_args(self): 77 | self.assertRaises(TypeError, cbitstruct.pack_into) 78 | self.assertRaises(TypeError, cbitstruct.pack_into, FMT) 79 | self.assertRaises(TypeError, cbitstruct.pack_into, FMT, bytes(4)) 80 | self.assertRaises(TypeError, cbitstruct.pack_into, FMT, bytes(4), 0) 81 | self.assertRaises(TypeError, cbitstruct.pack_into, None, bytes(4), 0, 12) 82 | self.assertRaises(TypeError, cbitstruct.pack_into, "g32", bytes(4), 0, 12) 83 | 84 | def test_pack_into_dict_too_small(self): 85 | self.assertRaises( 86 | TypeError, cbitstruct.pack_into_dict, FMT, NAMES, bytes(0), 0, DICT 87 | ) 88 | 89 | def test_pack_into_dict_bad_args(self): 90 | self.assertRaises(TypeError, cbitstruct.pack_into_dict) 91 | self.assertRaises(TypeError, cbitstruct.pack_into_dict, FMT) 92 | self.assertRaises(TypeError, cbitstruct.pack_into_dict, FMT, None) 93 | self.assertRaises(TypeError, cbitstruct.pack_into_dict, None, NAMES) 94 | self.assertRaises(TypeError, cbitstruct.pack_into_dict, FMT, NAMES, bytes(4), 0) 95 | self.assertRaises( 96 | TypeError, cbitstruct.pack_into_dict, FMT, None, bytes(4), 0, DICT 97 | ) 98 | self.assertRaises( 99 | TypeError, cbitstruct.pack_into_dict, None, NAMES, bytes(4), 0, DICT 100 | ) 101 | self.assertRaises( 102 | TypeError, cbitstruct.pack_into_dict, "g32", NAMES, bytes(4), 0, DICT 103 | ) 104 | 105 | def test_pack_dict_bad_args(self): 106 | self.assertRaises(TypeError, cbitstruct.pack_dict) 107 | self.assertRaises(TypeError, cbitstruct.pack_dict, FMT) 108 | self.assertRaises(TypeError, cbitstruct.pack_dict, FMT, None) 109 | self.assertRaises(TypeError, cbitstruct.pack_dict, None, NAMES) 110 | self.assertRaises(TypeError, cbitstruct.pack_dict, FMT, NAMES) 111 | self.assertRaises(TypeError, cbitstruct.pack_dict, FMT, None, DICT) 112 | self.assertRaises(TypeError, cbitstruct.pack_dict, None, NAMES, DICT) 113 | self.assertRaises(TypeError, cbitstruct.pack_dict, "g32", NAMES, DICT) 114 | 115 | def test_byteswap_bad_args(self): 116 | self.assertRaises(TypeError, cbitstruct.byteswap, "23", b"") 117 | self.assertRaises(TypeError, cbitstruct.byteswap, None, b"\xff") 118 | self.assertRaises(TypeError, cbitstruct.byteswap, "23") 119 | self.assertRaises(TypeError, cbitstruct.byteswap) 120 | self.assertRaises(ValueError, cbitstruct.byteswap, "\x02\x02", b"z") 121 | 122 | def test_calcsize_bad_args(self): 123 | self.assertRaises(TypeError, cbitstruct.calcsize, "g32") 124 | self.assertRaises(TypeError, cbitstruct.calcsize, None) 125 | self.assertRaises(TypeError, cbitstruct.calcsize) 126 | 127 | 128 | if __name__ == "__main__": 129 | unittest.main() 130 | -------------------------------------------------------------------------------- /cbitstruct/tests/test_api.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import inspect 3 | import bitstruct 4 | import cbitstruct 5 | 6 | 7 | NAMES = ["foo"] 8 | FMT = "u32" 9 | DICT = {"foo": 42} 10 | DATA = b"\x00\x00\x00\x00" 11 | BUF = bytearray(4) 12 | ARGS = [12] 13 | CF = cbitstruct.CompiledFormat(FMT) 14 | CFD = cbitstruct.CompiledFormatDict(FMT, NAMES) 15 | 16 | 17 | class BitstructApiTest(unittest.TestCase): 18 | def test_no_args(self): 19 | self.assertRaises(Exception, cbitstruct.byteswap) 20 | self.assertRaises(Exception, cbitstruct.calcsize) 21 | self.assertRaises(Exception, cbitstruct.pack) 22 | self.assertRaises(Exception, cbitstruct.pack_into) 23 | self.assertRaises(Exception, cbitstruct.pack_dict) 24 | self.assertRaises(Exception, cbitstruct.pack_into_dict) 25 | self.assertRaises(Exception, cbitstruct.unpack) 26 | self.assertRaises(Exception, cbitstruct.unpack_from) 27 | self.assertRaises(Exception, cbitstruct.unpack_dict) 28 | self.assertRaises(Exception, cbitstruct.unpack_from_dict) 29 | self.assertRaises(Exception, cbitstruct.CompiledFormat) 30 | self.assertRaises(Exception, cbitstruct.CompiledFormatDict) 31 | cf = cbitstruct.CompiledFormat(FMT) 32 | self.assertRaises(Exception, cf.pack) 33 | self.assertRaises(Exception, cf.pack_into) 34 | self.assertRaises(Exception, cf.unpack) 35 | self.assertRaises(Exception, cf.unpack_from) 36 | cfd = cbitstruct.CompiledFormatDict(FMT, NAMES) 37 | self.assertRaises(Exception, cf.pack) 38 | self.assertRaises(Exception, cf.pack_into) 39 | self.assertRaises(Exception, cf.unpack) 40 | self.assertRaises(Exception, cf.unpack_from) 41 | 42 | def test_compiled_format(self): 43 | cbitstruct.CompiledFormat(fmt=FMT) 44 | cbitstruct.CompiledFormat(FMT) 45 | 46 | def test_compiled_format_dict(self): 47 | cbitstruct.CompiledFormatDict(fmt=FMT, names=NAMES) 48 | cbitstruct.CompiledFormatDict(FMT, names=NAMES) 49 | cbitstruct.CompiledFormatDict(FMT, NAMES) 50 | 51 | def test_pack(self): 52 | cbitstruct.pack(fmt=FMT, *ARGS) 53 | cbitstruct.pack(FMT, *ARGS) 54 | 55 | def test_compiled_pack(self): 56 | CF.pack(*ARGS) 57 | 58 | def test_pack_into(self): 59 | cbitstruct.pack_into(*ARGS, fmt=FMT, buf=BUF, offset=0) 60 | cbitstruct.pack_into(FMT, *ARGS, buf=BUF, offset=0) 61 | cbitstruct.pack_into(FMT, BUF, *ARGS, offset=0) 62 | cbitstruct.pack_into(FMT, BUF, 0, *ARGS) 63 | 64 | cbitstruct.pack_into(*ARGS, fmt=FMT, buf=BUF, offset=0, fill_padding=False) 65 | cbitstruct.pack_into(FMT, *ARGS, buf=BUF, offset=0, fill_padding=False) 66 | cbitstruct.pack_into(FMT, BUF, *ARGS, offset=0, fill_padding=False) 67 | cbitstruct.pack_into(FMT, BUF, 0, *ARGS, fill_padding=False) 68 | 69 | def test_compiled_pack_into(self): 70 | CF.pack_into(*ARGS, buf=BUF, offset=0) 71 | CF.pack_into(BUF, *ARGS, offset=0) 72 | CF.pack_into(BUF, 0, *ARGS) 73 | 74 | CF.pack_into(*ARGS, buf=BUF, offset=0, fill_padding=False) 75 | CF.pack_into(BUF, *ARGS, offset=0, fill_padding=False) 76 | CF.pack_into(BUF, 0, *ARGS, fill_padding=False) 77 | 78 | def test_pack_dict(self): 79 | cbitstruct.pack_dict(fmt=FMT, names=NAMES, data=DICT) 80 | cbitstruct.pack_dict(FMT, names=NAMES, data=DICT) 81 | cbitstruct.pack_dict(FMT, NAMES, data=DICT) 82 | cbitstruct.pack_dict(FMT, NAMES, DICT) 83 | 84 | def test_compiled_pack_dict(self): 85 | CFD.pack(data=DICT) 86 | CFD.pack(DICT) 87 | 88 | def test_pack_into_dict(self): 89 | cbitstruct.pack_into_dict(fmt=FMT, names=NAMES, buf=BUF, offset=0, data=DICT) 90 | cbitstruct.pack_into_dict(FMT, names=NAMES, buf=BUF, offset=0, data=DICT) 91 | cbitstruct.pack_into_dict(FMT, NAMES, buf=BUF, offset=0, data=DICT) 92 | cbitstruct.pack_into_dict(FMT, NAMES, BUF, offset=0, data=DICT) 93 | cbitstruct.pack_into_dict(FMT, NAMES, BUF, 0, data=DICT) 94 | cbitstruct.pack_into_dict(FMT, NAMES, BUF, 0, DICT) 95 | 96 | cbitstruct.pack_into_dict( 97 | fmt=FMT, names=NAMES, buf=BUF, offset=0, data=DICT, fill_padding=False 98 | ) 99 | cbitstruct.pack_into_dict( 100 | FMT, names=NAMES, buf=BUF, offset=0, data=DICT, fill_padding=False 101 | ) 102 | cbitstruct.pack_into_dict( 103 | FMT, NAMES, buf=BUF, offset=0, data=DICT, fill_padding=False 104 | ) 105 | cbitstruct.pack_into_dict( 106 | FMT, NAMES, BUF, offset=0, data=DICT, fill_padding=False 107 | ) 108 | cbitstruct.pack_into_dict(FMT, NAMES, BUF, 0, data=DICT, fill_padding=False) 109 | cbitstruct.pack_into_dict(FMT, NAMES, BUF, 0, DICT, fill_padding=False) 110 | 111 | def test_compiled_pack_into_dict(self): 112 | CFD.pack_into(buf=BUF, offset=0, data=DICT) 113 | CFD.pack_into(BUF, offset=0, data=DICT) 114 | CFD.pack_into(BUF, 0, data=DICT) 115 | CFD.pack_into(BUF, 0, DICT) 116 | 117 | CFD.pack_into(buf=BUF, offset=0, data=DICT, fill_padding=False) 118 | CFD.pack_into(BUF, offset=0, data=DICT, fill_padding=False) 119 | CFD.pack_into(BUF, 0, data=DICT, fill_padding=False) 120 | CFD.pack_into(BUF, 0, DICT, fill_padding=False) 121 | 122 | def test_unpack(self): 123 | cbitstruct.unpack(fmt=FMT, data=DATA) 124 | cbitstruct.unpack(FMT, data=DATA) 125 | cbitstruct.unpack(FMT, DATA) 126 | 127 | def test_compiled_unpack(self): 128 | CF.unpack(data=DATA) 129 | CF.unpack(DATA) 130 | 131 | def test_unpack_from(self): 132 | cbitstruct.unpack_from(fmt=FMT, data=DATA, offset=0) 133 | cbitstruct.unpack_from(FMT, data=DATA, offset=0) 134 | cbitstruct.unpack_from(FMT, DATA, offset=0) 135 | cbitstruct.unpack_from(FMT, DATA, 0) 136 | 137 | cbitstruct.unpack_from(fmt=FMT, data=DATA) 138 | cbitstruct.unpack_from(FMT, data=DATA) 139 | cbitstruct.unpack_from(FMT, DATA) 140 | 141 | def test_compiled_unpack_from(self): 142 | CF.unpack_from(data=DATA, offset=0) 143 | CF.unpack_from(DATA, offset=0) 144 | CF.unpack_from(DATA, 0) 145 | 146 | CF.unpack_from(data=DATA) 147 | CF.unpack_from(DATA) 148 | 149 | def test_unpack_dict(self): 150 | cbitstruct.unpack_dict(fmt=FMT, names=NAMES, data=DATA) 151 | cbitstruct.unpack_dict(FMT, names=NAMES, data=DATA) 152 | cbitstruct.unpack_dict(FMT, NAMES, data=DATA) 153 | cbitstruct.unpack_dict(FMT, NAMES, DATA) 154 | 155 | def test_compiled_unpack_dict(self): 156 | CFD.unpack(data=DATA) 157 | CFD.unpack(DATA) 158 | 159 | def test_unpack_from_dict(self): 160 | cbitstruct.unpack_from_dict(fmt=FMT, names=NAMES, data=DATA, offset=0) 161 | cbitstruct.unpack_from_dict(FMT, names=NAMES, data=DATA, offset=0) 162 | cbitstruct.unpack_from_dict(FMT, NAMES, data=DATA, offset=0) 163 | cbitstruct.unpack_from_dict(FMT, NAMES, DATA, offset=0) 164 | cbitstruct.unpack_from_dict(FMT, NAMES, DATA, 0) 165 | 166 | cbitstruct.unpack_from_dict(fmt=FMT, names=NAMES, data=DATA) 167 | cbitstruct.unpack_from_dict(FMT, names=NAMES, data=DATA) 168 | cbitstruct.unpack_from_dict(FMT, NAMES, data=DATA) 169 | cbitstruct.unpack_from_dict(FMT, NAMES, DATA) 170 | 171 | def test_compiled_unpack_from_dict(self): 172 | CFD.unpack_from(data=DATA, offset=0) 173 | CFD.unpack_from(DATA, offset=0) 174 | CFD.unpack_from(DATA, 0) 175 | 176 | CFD.unpack_from(data=DATA) 177 | CFD.unpack_from(DATA) 178 | 179 | def test_compile(self): 180 | cbitstruct.compile(fmt=FMT, names=NAMES) 181 | cbitstruct.compile(FMT, names=NAMES) 182 | cbitstruct.compile(FMT, NAMES) 183 | 184 | cbitstruct.compile(fmt=FMT) 185 | cbitstruct.compile(FMT) 186 | 187 | def test_calcsize(self): 188 | cbitstruct.calcsize(fmt=FMT) 189 | cbitstruct.calcsize(FMT) 190 | 191 | def test_byteswap(self): 192 | cbitstruct.byteswap(fmt="1", data=DATA, offset=0) 193 | cbitstruct.byteswap("1", data=DATA, offset=0) 194 | cbitstruct.byteswap("1", DATA, offset=0) 195 | cbitstruct.byteswap("1", DATA, 0) 196 | 197 | cbitstruct.byteswap(fmt="1", data=DATA) 198 | cbitstruct.byteswap("1", data=DATA) 199 | cbitstruct.byteswap("1", DATA) 200 | 201 | 202 | if __name__ == "__main__": 203 | unittest.main() 204 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | ### 1. Definitions 5 | 6 | **1.1. “Contributor”** 7 | means each individual or legal entity that creates, contributes to 8 | the creation of, or owns Covered Software. 9 | 10 | **1.2. “Contributor Version”** 11 | means the combination of the Contributions of others (if any) used 12 | by a Contributor and that particular Contributor's Contribution. 13 | 14 | **1.3. “Contribution”** 15 | means Covered Software of a particular Contributor. 16 | 17 | **1.4. “Covered Software”** 18 | means Source Code Form to which the initial Contributor has attached 19 | the notice in Exhibit A, the Executable Form of such Source Code 20 | Form, and Modifications of such Source Code Form, in each case 21 | including portions thereof. 22 | 23 | **1.5. “Incompatible With Secondary Licenses”** 24 | means 25 | 26 | * **(a)** that the initial Contributor has attached the notice described 27 | in Exhibit B to the Covered Software; or 28 | * **(b)** that the Covered Software was made available under the terms of 29 | version 1.1 or earlier of the License, but not also under the 30 | terms of a Secondary License. 31 | 32 | **1.6. “Executable Form”** 33 | means any form of the work other than Source Code Form. 34 | 35 | **1.7. “Larger Work”** 36 | means a work that combines Covered Software with other material, in 37 | a separate file or files, that is not Covered Software. 38 | 39 | **1.8. “License”** 40 | means this document. 41 | 42 | **1.9. “Licensable”** 43 | means having the right to grant, to the maximum extent possible, 44 | whether at the time of the initial grant or subsequently, any and 45 | all of the rights conveyed by this License. 46 | 47 | **1.10. “Modifications”** 48 | means any of the following: 49 | 50 | * **(a)** any file in Source Code Form that results from an addition to, 51 | deletion from, or modification of the contents of Covered 52 | Software; or 53 | * **(b)** any new file in Source Code Form that contains any Covered 54 | Software. 55 | 56 | **1.11. “Patent Claims” of a Contributor** 57 | means any patent claim(s), including without limitation, method, 58 | process, and apparatus claims, in any patent Licensable by such 59 | Contributor that would be infringed, but for the grant of the 60 | License, by the making, using, selling, offering for sale, having 61 | made, import, or transfer of either its Contributions or its 62 | Contributor Version. 63 | 64 | **1.12. “Secondary License”** 65 | means either the GNU General Public License, Version 2.0, the GNU 66 | Lesser General Public License, Version 2.1, the GNU Affero General 67 | Public License, Version 3.0, or any later versions of those 68 | licenses. 69 | 70 | **1.13. “Source Code Form”** 71 | means the form of the work preferred for making modifications. 72 | 73 | **1.14. “You” (or “Your”)** 74 | means an individual or a legal entity exercising rights under this 75 | License. For legal entities, “You” includes any entity that 76 | controls, is controlled by, or is under common control with You. For 77 | purposes of this definition, “control” means **(a)** the power, direct 78 | or indirect, to cause the direction or management of such entity, 79 | whether by contract or otherwise, or **(b)** ownership of more than 80 | fifty percent (50%) of the outstanding shares or beneficial 81 | ownership of such entity. 82 | 83 | 84 | ### 2. License Grants and Conditions 85 | 86 | #### 2.1. Grants 87 | 88 | Each Contributor hereby grants You a world-wide, royalty-free, 89 | non-exclusive license: 90 | 91 | * **(a)** under intellectual property rights (other than patent or trademark) 92 | Licensable by such Contributor to use, reproduce, make available, 93 | modify, display, perform, distribute, and otherwise exploit its 94 | Contributions, either on an unmodified basis, with Modifications, or 95 | as part of a Larger Work; and 96 | * **(b)** under Patent Claims of such Contributor to make, use, sell, offer 97 | for sale, have made, import, and otherwise transfer either its 98 | Contributions or its Contributor Version. 99 | 100 | #### 2.2. Effective Date 101 | 102 | The licenses granted in Section 2.1 with respect to any Contribution 103 | become effective for each Contribution on the date the Contributor first 104 | distributes such Contribution. 105 | 106 | #### 2.3. Limitations on Grant Scope 107 | 108 | The licenses granted in this Section 2 are the only rights granted under 109 | this License. No additional rights or licenses will be implied from the 110 | distribution or licensing of Covered Software under this License. 111 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 112 | Contributor: 113 | 114 | * **(a)** for any code that a Contributor has removed from Covered Software; 115 | or 116 | * **(b)** for infringements caused by: **(i)** Your and any other third party's 117 | modifications of Covered Software, or **(ii)** the combination of its 118 | Contributions with other software (except as part of its Contributor 119 | Version); or 120 | * **(c)** under Patent Claims infringed by Covered Software in the absence of 121 | its Contributions. 122 | 123 | This License does not grant any rights in the trademarks, service marks, 124 | or logos of any Contributor (except as may be necessary to comply with 125 | the notice requirements in Section 3.4). 126 | 127 | #### 2.4. Subsequent Licenses 128 | 129 | No Contributor makes additional grants as a result of Your choice to 130 | distribute the Covered Software under a subsequent version of this 131 | License (see Section 10.2) or under the terms of a Secondary License (if 132 | permitted under the terms of Section 3.3). 133 | 134 | #### 2.5. Representation 135 | 136 | Each Contributor represents that the Contributor believes its 137 | Contributions are its original creation(s) or it has sufficient rights 138 | to grant the rights to its Contributions conveyed by this License. 139 | 140 | #### 2.6. Fair Use 141 | 142 | This License is not intended to limit any rights You have under 143 | applicable copyright doctrines of fair use, fair dealing, or other 144 | equivalents. 145 | 146 | #### 2.7. Conditions 147 | 148 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 149 | in Section 2.1. 150 | 151 | 152 | ### 3. Responsibilities 153 | 154 | #### 3.1. Distribution of Source Form 155 | 156 | All distribution of Covered Software in Source Code Form, including any 157 | Modifications that You create or to which You contribute, must be under 158 | the terms of this License. You must inform recipients that the Source 159 | Code Form of the Covered Software is governed by the terms of this 160 | License, and how they can obtain a copy of this License. You may not 161 | attempt to alter or restrict the recipients' rights in the Source Code 162 | Form. 163 | 164 | #### 3.2. Distribution of Executable Form 165 | 166 | If You distribute Covered Software in Executable Form then: 167 | 168 | * **(a)** such Covered Software must also be made available in Source Code 169 | Form, as described in Section 3.1, and You must inform recipients of 170 | the Executable Form how they can obtain a copy of such Source Code 171 | Form by reasonable means in a timely manner, at a charge no more 172 | than the cost of distribution to the recipient; and 173 | 174 | * **(b)** You may distribute such Executable Form under the terms of this 175 | License, or sublicense it under different terms, provided that the 176 | license for the Executable Form does not attempt to limit or alter 177 | the recipients' rights in the Source Code Form under this License. 178 | 179 | #### 3.3. Distribution of a Larger Work 180 | 181 | You may create and distribute a Larger Work under terms of Your choice, 182 | provided that You also comply with the requirements of this License for 183 | the Covered Software. If the Larger Work is a combination of Covered 184 | Software with a work governed by one or more Secondary Licenses, and the 185 | Covered Software is not Incompatible With Secondary Licenses, this 186 | License permits You to additionally distribute such Covered Software 187 | under the terms of such Secondary License(s), so that the recipient of 188 | the Larger Work may, at their option, further distribute the Covered 189 | Software under the terms of either this License or such Secondary 190 | License(s). 191 | 192 | #### 3.4. Notices 193 | 194 | You may not remove or alter the substance of any license notices 195 | (including copyright notices, patent notices, disclaimers of warranty, 196 | or limitations of liability) contained within the Source Code Form of 197 | the Covered Software, except that You may alter any license notices to 198 | the extent required to remedy known factual inaccuracies. 199 | 200 | #### 3.5. Application of Additional Terms 201 | 202 | You may choose to offer, and to charge a fee for, warranty, support, 203 | indemnity or liability obligations to one or more recipients of Covered 204 | Software. However, You may do so only on Your own behalf, and not on 205 | behalf of any Contributor. You must make it absolutely clear that any 206 | such warranty, support, indemnity, or liability obligation is offered by 207 | You alone, and You hereby agree to indemnify every Contributor for any 208 | liability incurred by such Contributor as a result of warranty, support, 209 | indemnity or liability terms You offer. You may include additional 210 | disclaimers of warranty and limitations of liability specific to any 211 | jurisdiction. 212 | 213 | 214 | ### 4. Inability to Comply Due to Statute or Regulation 215 | 216 | If it is impossible for You to comply with any of the terms of this 217 | License with respect to some or all of the Covered Software due to 218 | statute, judicial order, or regulation then You must: **(a)** comply with 219 | the terms of this License to the maximum extent possible; and **(b)** 220 | describe the limitations and the code they affect. Such description must 221 | be placed in a text file included with all distributions of the Covered 222 | Software under this License. Except to the extent prohibited by statute 223 | or regulation, such description must be sufficiently detailed for a 224 | recipient of ordinary skill to be able to understand it. 225 | 226 | 227 | ### 5. Termination 228 | 229 | **5.1.** The rights granted under this License will terminate automatically 230 | if You fail to comply with any of its terms. However, if You become 231 | compliant, then the rights granted under this License from a particular 232 | Contributor are reinstated **(a)** provisionally, unless and until such 233 | Contributor explicitly and finally terminates Your grants, and **(b)** on an 234 | ongoing basis, if such Contributor fails to notify You of the 235 | non-compliance by some reasonable means prior to 60 days after You have 236 | come back into compliance. Moreover, Your grants from a particular 237 | Contributor are reinstated on an ongoing basis if such Contributor 238 | notifies You of the non-compliance by some reasonable means, this is the 239 | first time You have received notice of non-compliance with this License 240 | from such Contributor, and You become compliant prior to 30 days after 241 | Your receipt of the notice. 242 | 243 | **5.2.** If You initiate litigation against any entity by asserting a patent 244 | infringement claim (excluding declaratory judgment actions, 245 | counter-claims, and cross-claims) alleging that a Contributor Version 246 | directly or indirectly infringes any patent, then the rights granted to 247 | You by any and all Contributors for the Covered Software under Section 248 | 2.1 of this License shall terminate. 249 | 250 | **5.3.** In the event of termination under Sections 5.1 or 5.2 above, all 251 | end user license agreements (excluding distributors and resellers) which 252 | have been validly granted by You or Your distributors under this License 253 | prior to termination shall survive termination. 254 | 255 | 256 | ### 6. Disclaimer of Warranty 257 | 258 | > Covered Software is provided under this License on an “as is” 259 | > basis, without warranty of any kind, either expressed, implied, or 260 | > statutory, including, without limitation, warranties that the 261 | > Covered Software is free of defects, merchantable, fit for a 262 | > particular purpose or non-infringing. The entire risk as to the 263 | > quality and performance of the Covered Software is with You. 264 | > Should any Covered Software prove defective in any respect, You 265 | > (not any Contributor) assume the cost of any necessary servicing, 266 | > repair, or correction. This disclaimer of warranty constitutes an 267 | > essential part of this License. No use of any Covered Software is 268 | > authorized under this License except under this disclaimer. 269 | 270 | ### 7. Limitation of Liability 271 | 272 | > Under no circumstances and under no legal theory, whether tort 273 | > (including negligence), contract, or otherwise, shall any 274 | > Contributor, or anyone who distributes Covered Software as 275 | > permitted above, be liable to You for any direct, indirect, 276 | > special, incidental, or consequential damages of any character 277 | > including, without limitation, damages for lost profits, loss of 278 | > goodwill, work stoppage, computer failure or malfunction, or any 279 | > and all other commercial damages or losses, even if such party 280 | > shall have been informed of the possibility of such damages. This 281 | > limitation of liability shall not apply to liability for death or 282 | > personal injury resulting from such party's negligence to the 283 | > extent applicable law prohibits such limitation. Some 284 | > jurisdictions do not allow the exclusion or limitation of 285 | > incidental or consequential damages, so this exclusion and 286 | > limitation may not apply to You. 287 | 288 | 289 | ### 8. Litigation 290 | 291 | Any litigation relating to this License may be brought only in the 292 | courts of a jurisdiction where the defendant maintains its principal 293 | place of business and such litigation shall be governed by laws of that 294 | jurisdiction, without reference to its conflict-of-law provisions. 295 | Nothing in this Section shall prevent a party's ability to bring 296 | cross-claims or counter-claims. 297 | 298 | 299 | ### 9. Miscellaneous 300 | 301 | This License represents the complete agreement concerning the subject 302 | matter hereof. If any provision of this License is held to be 303 | unenforceable, such provision shall be reformed only to the extent 304 | necessary to make it enforceable. Any law or regulation which provides 305 | that the language of a contract shall be construed against the drafter 306 | shall not be used to construe this License against a Contributor. 307 | 308 | 309 | ### 10. Versions of the License 310 | 311 | #### 10.1. New Versions 312 | 313 | Mozilla Foundation is the license steward. Except as provided in Section 314 | 10.3, no one other than the license steward has the right to modify or 315 | publish new versions of this License. Each version will be given a 316 | distinguishing version number. 317 | 318 | #### 10.2. Effect of New Versions 319 | 320 | You may distribute the Covered Software under the terms of the version 321 | of the License under which You originally received the Covered Software, 322 | or under the terms of any subsequent version published by the license 323 | steward. 324 | 325 | #### 10.3. Modified Versions 326 | 327 | If you create software not governed by this License, and you want to 328 | create a new license for such software, you may create and use a 329 | modified version of this License if you rename the license and remove 330 | any references to the name of the license steward (except to note that 331 | such modified license differs from this License). 332 | 333 | #### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 334 | 335 | If You choose to distribute Source Code Form that is Incompatible With 336 | Secondary Licenses under the terms of this version of the License, the 337 | notice described in Exhibit B of this License must be attached. 338 | 339 | ## Exhibit A - Source Code Form License Notice 340 | 341 | This Source Code Form is subject to the terms of the Mozilla Public 342 | License, v. 2.0. If a copy of the MPL was not distributed with this 343 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 344 | 345 | If it is not possible or desirable to put the notice in a particular 346 | file, then You may include the notice in a location (such as a LICENSE 347 | file in a relevant directory) where a recipient would be likely to look 348 | for such a notice. 349 | 350 | You may add additional accurate notices of copyright ownership. 351 | 352 | ## Exhibit B - “Incompatible With Secondary Licenses” Notice 353 | 354 | This Source Code Form is "Incompatible With Secondary Licenses", as 355 | defined by the Mozilla Public License, v. 2.0. 356 | -------------------------------------------------------------------------------- /cbitstruct/clinic/_cbitstruct.c.313.h: -------------------------------------------------------------------------------- 1 | /*[clinic input] 2 | preserve 3 | [clinic start generated code]*/ 4 | 5 | PyDoc_STRVAR(CompiledFormat___init____doc__, 6 | "CompiledFormat(fmt)\n" 7 | "--\n" 8 | "\n" 9 | "Create a compiled bitstruct object.\n" 10 | "\n" 11 | "Return a new CompiledFormat object which writes and reads binary data\n" 12 | "according to the format string."); 13 | 14 | static int 15 | CompiledFormat___init___impl(PyCompiledFormatObject *self, const char *fmt); 16 | 17 | static int 18 | CompiledFormat___init__(PyObject *self, PyObject *args, PyObject *kwargs) 19 | { 20 | int return_value = -1; 21 | static char *_keywords[] = {"fmt", NULL}; 22 | const char *fmt; 23 | 24 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:CompiledFormat", _keywords, 25 | &fmt)) 26 | goto exit; 27 | return_value = CompiledFormat___init___impl((PyCompiledFormatObject *)self, fmt); 28 | 29 | exit: 30 | return return_value; 31 | } 32 | 33 | PyDoc_STRVAR(CompiledFormat_calcsize__doc__, 34 | "calcsize($self, /)\n" 35 | "--\n" 36 | "\n" 37 | "Return size in bits of the bitDescribed by the format string."); 38 | 39 | #define COMPILEDFORMAT_CALCSIZE_METHODDEF \ 40 | {"calcsize", (PyCFunction)CompiledFormat_calcsize, METH_NOARGS, CompiledFormat_calcsize__doc__}, 41 | 42 | static Py_ssize_t 43 | CompiledFormat_calcsize_impl(PyCompiledFormatObject *self); 44 | 45 | static PyObject * 46 | CompiledFormat_calcsize(PyCompiledFormatObject *self, PyObject *Py_UNUSED(ignored)) 47 | { 48 | PyObject *return_value = NULL; 49 | Py_ssize_t _return_value; 50 | 51 | _return_value = CompiledFormat_calcsize_impl(self); 52 | if ((_return_value == -1) && PyErr_Occurred()) { 53 | goto exit; 54 | } 55 | return_value = PyLong_FromSsize_t(_return_value); 56 | 57 | exit: 58 | return return_value; 59 | } 60 | 61 | PyDoc_STRVAR(CompiledFormat_unpack__doc__, 62 | "unpack($self, /, data)\n" 63 | "--\n" 64 | "\n" 65 | "Return a tuple containing unpacked values."); 66 | 67 | #define COMPILEDFORMAT_UNPACK_METHODDEF \ 68 | {"unpack", (PyCFunction)(void(*)(void))CompiledFormat_unpack, METH_VARARGS|METH_KEYWORDS, CompiledFormat_unpack__doc__}, 69 | 70 | static PyObject * 71 | CompiledFormat_unpack_impl(PyCompiledFormatObject *self, Py_buffer *data); 72 | 73 | static PyObject * 74 | CompiledFormat_unpack(PyCompiledFormatObject *self, PyObject *args, PyObject *kwargs) 75 | { 76 | PyObject *return_value = NULL; 77 | static char *_keywords[] = {"data", NULL}; 78 | Py_buffer data = {NULL, NULL}; 79 | 80 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:unpack", _keywords, 81 | &data)) 82 | goto exit; 83 | return_value = CompiledFormat_unpack_impl(self, &data); 84 | 85 | exit: 86 | /* Cleanup for data */ 87 | if (data.obj) { 88 | PyBuffer_Release(&data); 89 | } 90 | 91 | return return_value; 92 | } 93 | 94 | PyDoc_STRVAR(CompiledFormat_unpack_from__doc__, 95 | "unpack_from($self, /, data, offset=0)\n" 96 | "--\n" 97 | "\n" 98 | "Return a tuple containing unpacked values starting at \'offset\' bits."); 99 | 100 | #define COMPILEDFORMAT_UNPACK_FROM_METHODDEF \ 101 | {"unpack_from", (PyCFunction)(void(*)(void))CompiledFormat_unpack_from, METH_VARARGS|METH_KEYWORDS, CompiledFormat_unpack_from__doc__}, 102 | 103 | static PyObject * 104 | CompiledFormat_unpack_from_impl(PyCompiledFormatObject *self, 105 | Py_buffer *data, Py_ssize_t offset); 106 | 107 | static PyObject * 108 | CompiledFormat_unpack_from(PyCompiledFormatObject *self, PyObject *args, PyObject *kwargs) 109 | { 110 | PyObject *return_value = NULL; 111 | static char *_keywords[] = {"data", "offset", NULL}; 112 | Py_buffer data = {NULL, NULL}; 113 | Py_ssize_t offset = 0; 114 | 115 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|n:unpack_from", _keywords, 116 | &data, &offset)) 117 | goto exit; 118 | return_value = CompiledFormat_unpack_from_impl(self, &data, offset); 119 | 120 | exit: 121 | /* Cleanup for data */ 122 | if (data.obj) { 123 | PyBuffer_Release(&data); 124 | } 125 | 126 | return return_value; 127 | } 128 | 129 | PyDoc_STRVAR(CompiledFormatDict___init____doc__, 130 | "CompiledFormatDict(fmt, names)\n" 131 | "--\n" 132 | "\n" 133 | "Create a compiled bitstruct object.\n" 134 | "\n" 135 | "Return a new CompiledFormatDict object which writes and reads binary data\n" 136 | "according to the format string. The names list \'name\' will be used\n" 137 | "as keys in data dictionaries."); 138 | 139 | static int 140 | CompiledFormatDict___init___impl(PyCompiledFormatDictObject *self, 141 | const char *fmt, PyObject *names); 142 | 143 | static int 144 | CompiledFormatDict___init__(PyObject *self, PyObject *args, PyObject *kwargs) 145 | { 146 | int return_value = -1; 147 | static char *_keywords[] = {"fmt", "names", NULL}; 148 | const char *fmt; 149 | PyObject *names; 150 | 151 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO:CompiledFormatDict", _keywords, 152 | &fmt, &names)) 153 | goto exit; 154 | return_value = CompiledFormatDict___init___impl((PyCompiledFormatDictObject *)self, fmt, names); 155 | 156 | exit: 157 | return return_value; 158 | } 159 | 160 | PyDoc_STRVAR(CompiledFormatDict_pack__doc__, 161 | "pack($self, /, data)\n" 162 | "--\n" 163 | "\n" 164 | "Pack values from a dict into a bytes object\n" 165 | "\n" 166 | "Return a tuple containing unpacked values.\n" 167 | "\'data\' is a dictionary containing values whic keys are the \'names\'\n" 168 | "used when constructing this object."); 169 | 170 | #define COMPILEDFORMATDICT_PACK_METHODDEF \ 171 | {"pack", (PyCFunction)(void(*)(void))CompiledFormatDict_pack, METH_VARARGS|METH_KEYWORDS, CompiledFormatDict_pack__doc__}, 172 | 173 | static PyObject * 174 | CompiledFormatDict_pack_impl(PyCompiledFormatDictObject *self, 175 | PyObject *data); 176 | 177 | static PyObject * 178 | CompiledFormatDict_pack(PyCompiledFormatDictObject *self, PyObject *args, PyObject *kwargs) 179 | { 180 | PyObject *return_value = NULL; 181 | static char *_keywords[] = {"data", NULL}; 182 | PyObject *data; 183 | 184 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:pack", _keywords, 185 | &data)) 186 | goto exit; 187 | return_value = CompiledFormatDict_pack_impl(self, data); 188 | 189 | exit: 190 | return return_value; 191 | } 192 | 193 | PyDoc_STRVAR(CompiledFormatDict_pack_into__doc__, 194 | "pack_into($self, /, buf, offset, data, *, fill_padding=True)\n" 195 | "--\n" 196 | "\n" 197 | "Pack data into a bytes object, starting at bit offset given by the offset argument.\n" 198 | "\n" 199 | "With fill_padding=False, passing bits in \'buf\' will not be modified."); 200 | 201 | #define COMPILEDFORMATDICT_PACK_INTO_METHODDEF \ 202 | {"pack_into", (PyCFunction)(void(*)(void))CompiledFormatDict_pack_into, METH_VARARGS|METH_KEYWORDS, CompiledFormatDict_pack_into__doc__}, 203 | 204 | static PyObject * 205 | CompiledFormatDict_pack_into_impl(PyCompiledFormatDictObject *self, 206 | Py_buffer *buf, Py_ssize_t offset, 207 | PyObject *data, int fill_padding); 208 | 209 | static PyObject * 210 | CompiledFormatDict_pack_into(PyCompiledFormatDictObject *self, PyObject *args, PyObject *kwargs) 211 | { 212 | PyObject *return_value = NULL; 213 | static char *_keywords[] = {"buf", "offset", "data", "fill_padding", NULL}; 214 | Py_buffer buf = {NULL, NULL}; 215 | Py_ssize_t offset; 216 | PyObject *data; 217 | int fill_padding = 1; 218 | 219 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*nO|$p:pack_into", _keywords, 220 | &buf, &offset, &data, &fill_padding)) 221 | goto exit; 222 | return_value = CompiledFormatDict_pack_into_impl(self, &buf, offset, data, fill_padding); 223 | 224 | exit: 225 | /* Cleanup for buf */ 226 | if (buf.obj) { 227 | PyBuffer_Release(&buf); 228 | } 229 | 230 | return return_value; 231 | } 232 | 233 | PyDoc_STRVAR(CompiledFormatDict_unpack__doc__, 234 | "unpack($self, /, data)\n" 235 | "--\n" 236 | "\n" 237 | "Unpack data into a dict which keys are the \'names\' used when constructing this object.\n" 238 | "\n" 239 | "Return a dict containing unpacked values."); 240 | 241 | #define COMPILEDFORMATDICT_UNPACK_METHODDEF \ 242 | {"unpack", (PyCFunction)(void(*)(void))CompiledFormatDict_unpack, METH_VARARGS|METH_KEYWORDS, CompiledFormatDict_unpack__doc__}, 243 | 244 | static PyObject * 245 | CompiledFormatDict_unpack_impl(PyCompiledFormatDictObject *self, 246 | Py_buffer *data); 247 | 248 | static PyObject * 249 | CompiledFormatDict_unpack(PyCompiledFormatDictObject *self, PyObject *args, PyObject *kwargs) 250 | { 251 | PyObject *return_value = NULL; 252 | static char *_keywords[] = {"data", NULL}; 253 | Py_buffer data = {NULL, NULL}; 254 | 255 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:unpack", _keywords, 256 | &data)) 257 | goto exit; 258 | return_value = CompiledFormatDict_unpack_impl(self, &data); 259 | 260 | exit: 261 | /* Cleanup for data */ 262 | if (data.obj) { 263 | PyBuffer_Release(&data); 264 | } 265 | 266 | return return_value; 267 | } 268 | 269 | PyDoc_STRVAR(CompiledFormatDict_unpack_from__doc__, 270 | "unpack_from($self, /, data, offset=0)\n" 271 | "--\n" 272 | "\n" 273 | "Unpack data into a dict starting at \'offset\' bits.\n" 274 | "\n" 275 | "Return a dict containing unpacked values."); 276 | 277 | #define COMPILEDFORMATDICT_UNPACK_FROM_METHODDEF \ 278 | {"unpack_from", (PyCFunction)(void(*)(void))CompiledFormatDict_unpack_from, METH_VARARGS|METH_KEYWORDS, CompiledFormatDict_unpack_from__doc__}, 279 | 280 | static PyObject * 281 | CompiledFormatDict_unpack_from_impl(PyCompiledFormatDictObject *self, 282 | Py_buffer *data, Py_ssize_t offset); 283 | 284 | static PyObject * 285 | CompiledFormatDict_unpack_from(PyCompiledFormatDictObject *self, PyObject *args, PyObject *kwargs) 286 | { 287 | PyObject *return_value = NULL; 288 | static char *_keywords[] = {"data", "offset", NULL}; 289 | Py_buffer data = {NULL, NULL}; 290 | Py_ssize_t offset = 0; 291 | 292 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|n:unpack_from", _keywords, 293 | &data, &offset)) 294 | goto exit; 295 | return_value = CompiledFormatDict_unpack_from_impl(self, &data, offset); 296 | 297 | exit: 298 | /* Cleanup for data */ 299 | if (data.obj) { 300 | PyBuffer_Release(&data); 301 | } 302 | 303 | return return_value; 304 | } 305 | 306 | PyDoc_STRVAR(pack_dict__doc__, 307 | "pack_dict($module, /, fmt, names, data)\n" 308 | "--\n" 309 | "\n" 310 | "Pack the dict data into a bytes object according to format.\n" 311 | "\n" 312 | "The order of value is determines by the list \'names\'."); 313 | 314 | #define PACK_DICT_METHODDEF \ 315 | {"pack_dict", (PyCFunction)(void(*)(void))pack_dict, METH_VARARGS|METH_KEYWORDS, pack_dict__doc__}, 316 | 317 | static PyObject * 318 | pack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 319 | PyObject *data); 320 | 321 | static PyObject * 322 | pack_dict(PyObject *module, PyObject *args, PyObject *kwargs) 323 | { 324 | PyObject *return_value = NULL; 325 | static char *_keywords[] = {"fmt", "names", "data", NULL}; 326 | const char *fmt; 327 | PyObject *names; 328 | PyObject *data; 329 | 330 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOO:pack_dict", _keywords, 331 | &fmt, &names, &data)) 332 | goto exit; 333 | return_value = pack_dict_impl(module, fmt, names, data); 334 | 335 | exit: 336 | return return_value; 337 | } 338 | 339 | PyDoc_STRVAR(pack_into_dict__doc__, 340 | "pack_into_dict($module, /, fmt, names, buf, offset, data, *,\n" 341 | " fill_padding=True)\n" 342 | "--\n" 343 | "\n" 344 | "Pack data into a bytes object, starting at bit offset given by the offset argument.\n" 345 | "\n" 346 | "With fill_padding=False, passing bits in \'buf\' will not be modified."); 347 | 348 | #define PACK_INTO_DICT_METHODDEF \ 349 | {"pack_into_dict", (PyCFunction)(void(*)(void))pack_into_dict, METH_VARARGS|METH_KEYWORDS, pack_into_dict__doc__}, 350 | 351 | static PyObject * 352 | pack_into_dict_impl(PyObject *module, const char *fmt, PyObject *names, 353 | Py_buffer *buf, Py_ssize_t offset, PyObject *data, 354 | int fill_padding); 355 | 356 | static PyObject * 357 | pack_into_dict(PyObject *module, PyObject *args, PyObject *kwargs) 358 | { 359 | PyObject *return_value = NULL; 360 | static char *_keywords[] = {"fmt", "names", "buf", "offset", "data", "fill_padding", NULL}; 361 | const char *fmt; 362 | PyObject *names; 363 | Py_buffer buf = {NULL, NULL}; 364 | Py_ssize_t offset; 365 | PyObject *data; 366 | int fill_padding = 1; 367 | 368 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOy*nO|$p:pack_into_dict", _keywords, 369 | &fmt, &names, &buf, &offset, &data, &fill_padding)) 370 | goto exit; 371 | return_value = pack_into_dict_impl(module, fmt, names, &buf, offset, data, fill_padding); 372 | 373 | exit: 374 | /* Cleanup for buf */ 375 | if (buf.obj) { 376 | PyBuffer_Release(&buf); 377 | } 378 | 379 | return return_value; 380 | } 381 | 382 | PyDoc_STRVAR(unpack__doc__, 383 | "unpack($module, /, fmt, data)\n" 384 | "--\n" 385 | "\n" 386 | "Unpack data according to the format \'fmt\'. Returns a tuple."); 387 | 388 | #define UNPACK_METHODDEF \ 389 | {"unpack", (PyCFunction)(void(*)(void))unpack, METH_VARARGS|METH_KEYWORDS, unpack__doc__}, 390 | 391 | static PyObject * 392 | unpack_impl(PyObject *module, const char *fmt, Py_buffer *data); 393 | 394 | static PyObject * 395 | unpack(PyObject *module, PyObject *args, PyObject *kwargs) 396 | { 397 | PyObject *return_value = NULL; 398 | static char *_keywords[] = {"fmt", "data", NULL}; 399 | const char *fmt; 400 | Py_buffer data = {NULL, NULL}; 401 | 402 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sy*:unpack", _keywords, 403 | &fmt, &data)) 404 | goto exit; 405 | return_value = unpack_impl(module, fmt, &data); 406 | 407 | exit: 408 | /* Cleanup for data */ 409 | if (data.obj) { 410 | PyBuffer_Release(&data); 411 | } 412 | 413 | return return_value; 414 | } 415 | 416 | PyDoc_STRVAR(unpack_from__doc__, 417 | "unpack_from($module, /, fmt, data, offset=0)\n" 418 | "--\n" 419 | "\n" 420 | "Unpack data according to the format \'fmt\', starting at bit offset \'offset.\n" 421 | "\n" 422 | "Returns a tuple."); 423 | 424 | #define UNPACK_FROM_METHODDEF \ 425 | {"unpack_from", (PyCFunction)(void(*)(void))unpack_from, METH_VARARGS|METH_KEYWORDS, unpack_from__doc__}, 426 | 427 | static PyObject * 428 | unpack_from_impl(PyObject *module, const char *fmt, Py_buffer *data, 429 | Py_ssize_t offset); 430 | 431 | static PyObject * 432 | unpack_from(PyObject *module, PyObject *args, PyObject *kwargs) 433 | { 434 | PyObject *return_value = NULL; 435 | static char *_keywords[] = {"fmt", "data", "offset", NULL}; 436 | const char *fmt; 437 | Py_buffer data = {NULL, NULL}; 438 | Py_ssize_t offset = 0; 439 | 440 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sy*|n:unpack_from", _keywords, 441 | &fmt, &data, &offset)) 442 | goto exit; 443 | return_value = unpack_from_impl(module, fmt, &data, offset); 444 | 445 | exit: 446 | /* Cleanup for data */ 447 | if (data.obj) { 448 | PyBuffer_Release(&data); 449 | } 450 | 451 | return return_value; 452 | } 453 | 454 | PyDoc_STRVAR(unpack_dict__doc__, 455 | "unpack_dict($module, /, fmt, names, data)\n" 456 | "--\n" 457 | "\n" 458 | "Unpack data according to \'fmt\'.\n" 459 | "\n" 460 | "Returns a dict which keys are \'names\'."); 461 | 462 | #define UNPACK_DICT_METHODDEF \ 463 | {"unpack_dict", (PyCFunction)(void(*)(void))unpack_dict, METH_VARARGS|METH_KEYWORDS, unpack_dict__doc__}, 464 | 465 | static PyObject * 466 | unpack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 467 | Py_buffer *data); 468 | 469 | static PyObject * 470 | unpack_dict(PyObject *module, PyObject *args, PyObject *kwargs) 471 | { 472 | PyObject *return_value = NULL; 473 | static char *_keywords[] = {"fmt", "names", "data", NULL}; 474 | const char *fmt; 475 | PyObject *names; 476 | Py_buffer data = {NULL, NULL}; 477 | 478 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOy*:unpack_dict", _keywords, 479 | &fmt, &names, &data)) 480 | goto exit; 481 | return_value = unpack_dict_impl(module, fmt, names, &data); 482 | 483 | exit: 484 | /* Cleanup for data */ 485 | if (data.obj) { 486 | PyBuffer_Release(&data); 487 | } 488 | 489 | return return_value; 490 | } 491 | 492 | PyDoc_STRVAR(unpack_from_dict__doc__, 493 | "unpack_from_dict($module, /, fmt, names, data, offset=0)\n" 494 | "--\n" 495 | "\n" 496 | "Unpack data according to \'fmt\' starting at bit offset \'offset\'.\n" 497 | "\n" 498 | "Returns a dict which keys are \'names\'."); 499 | 500 | #define UNPACK_FROM_DICT_METHODDEF \ 501 | {"unpack_from_dict", (PyCFunction)(void(*)(void))unpack_from_dict, METH_VARARGS|METH_KEYWORDS, unpack_from_dict__doc__}, 502 | 503 | static PyObject * 504 | unpack_from_dict_impl(PyObject *module, const char *fmt, PyObject *names, 505 | Py_buffer *data, Py_ssize_t offset); 506 | 507 | static PyObject * 508 | unpack_from_dict(PyObject *module, PyObject *args, PyObject *kwargs) 509 | { 510 | PyObject *return_value = NULL; 511 | static char *_keywords[] = {"fmt", "names", "data", "offset", NULL}; 512 | const char *fmt; 513 | PyObject *names; 514 | Py_buffer data = {NULL, NULL}; 515 | Py_ssize_t offset = 0; 516 | 517 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOy*|n:unpack_from_dict", _keywords, 518 | &fmt, &names, &data, &offset)) 519 | goto exit; 520 | return_value = unpack_from_dict_impl(module, fmt, names, &data, offset); 521 | 522 | exit: 523 | /* Cleanup for data */ 524 | if (data.obj) { 525 | PyBuffer_Release(&data); 526 | } 527 | 528 | return return_value; 529 | } 530 | 531 | PyDoc_STRVAR(compile__doc__, 532 | "compile($module, /, fmt, names=None)\n" 533 | "--\n" 534 | "\n" 535 | "Returns a compiled object for the format \'fmt\'."); 536 | 537 | #define COMPILE_METHODDEF \ 538 | {"compile", (PyCFunction)(void(*)(void))compile, METH_VARARGS|METH_KEYWORDS, compile__doc__}, 539 | 540 | static PyObject * 541 | compile_impl(PyObject *module, const char *fmt, PyObject *names); 542 | 543 | static PyObject * 544 | compile(PyObject *module, PyObject *args, PyObject *kwargs) 545 | { 546 | PyObject *return_value = NULL; 547 | static char *_keywords[] = {"fmt", "names", NULL}; 548 | const char *fmt; 549 | PyObject *names = Py_None; 550 | 551 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O:compile", _keywords, 552 | &fmt, &names)) 553 | goto exit; 554 | return_value = compile_impl(module, fmt, names); 555 | 556 | exit: 557 | return return_value; 558 | } 559 | 560 | PyDoc_STRVAR(calcsize__doc__, 561 | "calcsize($module, /, fmt)\n" 562 | "--\n" 563 | "\n" 564 | "Return size in bits of the bit described by the format string."); 565 | 566 | #define CALCSIZE_METHODDEF \ 567 | {"calcsize", (PyCFunction)(void(*)(void))calcsize, METH_VARARGS|METH_KEYWORDS, calcsize__doc__}, 568 | 569 | static Py_ssize_t 570 | calcsize_impl(PyObject *module, const char *fmt); 571 | 572 | static PyObject * 573 | calcsize(PyObject *module, PyObject *args, PyObject *kwargs) 574 | { 575 | PyObject *return_value = NULL; 576 | static char *_keywords[] = {"fmt", NULL}; 577 | const char *fmt; 578 | Py_ssize_t _return_value; 579 | 580 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:calcsize", _keywords, 581 | &fmt)) 582 | goto exit; 583 | _return_value = calcsize_impl(module, fmt); 584 | if ((_return_value == -1) && PyErr_Occurred()) { 585 | goto exit; 586 | } 587 | return_value = PyLong_FromSsize_t(_return_value); 588 | 589 | exit: 590 | return return_value; 591 | } 592 | 593 | PyDoc_STRVAR(byteswap__doc__, 594 | "byteswap($module, /, fmt, data, offset=0)\n" 595 | "--\n" 596 | "\n" 597 | "Swap bytes in `data` according to `fmt`, starting at byte `offset` and return the result.\n" 598 | "\n" 599 | "`fmt` must be an iterable, iterating over\n" 600 | "number of bytes to swap. For example, the format string ``\'24\'``\n" 601 | "applied to the bytes ``b\'\\x00\\x11\\x22\\x33\\x44\\x55\'`` will\n" 602 | "produce the result ``b\'\\x11\\x00\\x55\\x44\\x33\\x22\'``."); 603 | 604 | #define BYTESWAP_METHODDEF \ 605 | {"byteswap", (PyCFunction)(void(*)(void))byteswap, METH_VARARGS|METH_KEYWORDS, byteswap__doc__}, 606 | 607 | static PyObject * 608 | byteswap_impl(PyObject *module, PyObject *fmt, Py_buffer *data, 609 | Py_ssize_t offset); 610 | 611 | static PyObject * 612 | byteswap(PyObject *module, PyObject *args, PyObject *kwargs) 613 | { 614 | PyObject *return_value = NULL; 615 | static char *_keywords[] = {"fmt", "data", "offset", NULL}; 616 | PyObject *fmt; 617 | Py_buffer data = {NULL, NULL}; 618 | Py_ssize_t offset = 0; 619 | 620 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oy*|n:byteswap", _keywords, 621 | &fmt, &data, &offset)) 622 | goto exit; 623 | return_value = byteswap_impl(module, fmt, &data, offset); 624 | 625 | exit: 626 | /* Cleanup for data */ 627 | if (data.obj) { 628 | PyBuffer_Release(&data); 629 | } 630 | 631 | return return_value; 632 | } 633 | /*[clinic end generated code: output=5066999e92716c32 input=a9049054013a1b77]*/ 634 | -------------------------------------------------------------------------------- /cbitstruct/tests/test_bitstruct.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import timeit 3 | import sys 4 | from cbitstruct import * 5 | import cbitstruct 6 | 7 | Error = TypeError 8 | 9 | 10 | class BitstructTest(unittest.TestCase): 11 | 12 | def test_pack(self): 13 | """Pack values. 14 | 15 | """ 16 | 17 | packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) 18 | self.assertEqual(packed, b'\x3e\x82\x16') 19 | 20 | packed = pack('u1', 1) 21 | self.assertEqual(packed, b'\x80') 22 | 23 | packed = pack('p1u1s6u7u9', 0, -2, 65, 22) 24 | self.assertEqual(packed, b'\x3e\x82\x16') 25 | 26 | packed = pack('P1u1s6u7u9', 0, -2, 65, 22) 27 | self.assertEqual(packed, b'\xbe\x82\x16') 28 | 29 | packed = pack('p1u1s6p7u9', 0, -2, 22) 30 | self.assertEqual(packed, b'\x3e\x00\x16') 31 | 32 | packed = pack('P1u1s6p7u9', 0, -2, 22) 33 | self.assertEqual(packed, b'\xbe\x00\x16') 34 | 35 | packed = pack('u1s6f32r43', 0, -2, 3.75, b'\x00\xff\x00\xff\x00\xff') 36 | self.assertEqual(packed, b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0') 37 | 38 | packed = pack('b1', True) 39 | self.assertEqual(packed, b'\x80') 40 | 41 | packed = pack('b1p6b1', True, True) 42 | self.assertEqual(packed, b'\x81') 43 | 44 | packed = pack('b1P6b1', True, True) 45 | self.assertEqual(packed, b'\xff') 46 | 47 | packed = pack('u5b2u1', 31, False, 1) 48 | self.assertEqual(packed, b'\xf9') 49 | 50 | packed = pack('b1t24', False, u'Hi!') 51 | self.assertEqual(packed, b'$4\x90\x80') 52 | 53 | packed = pack('b1t24', False, 'Hi!') 54 | self.assertEqual(packed, b'$4\x90\x80') 55 | 56 | # Too many values to pack. 57 | with self.assertRaises(Error) as cm: 58 | pack('b1t24', False) 59 | 60 | # Cannot convert argument to integer. 61 | with self.assertRaises(Error) as cm: 62 | pack('u1', 'foo') 63 | 64 | # Cannot convert argument to float. 65 | with self.assertRaises(Error) as cm: 66 | pack('f32', 'foo') 67 | 68 | # Cannot convert argument to bytearray. 69 | with self.assertRaises(Error) as cm: 70 | pack('r5', 1.0) 71 | 72 | # Cannot encode argument as utf-8. 73 | with self.assertRaises(Error) as cm: 74 | pack('t8', 1.0) 75 | 76 | def test_unpack(self): 77 | """Unpack values. 78 | 79 | """ 80 | 81 | unpacked = unpack('u1u1s6u7u9', b'\x3e\x82\x16') 82 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 83 | 84 | unpacked = unpack('u1', bytearray(b'\x80')) 85 | self.assertEqual(unpacked, (1, )) 86 | 87 | packed = b'\xbe\x82\x16' 88 | unpacked = unpack('P1u1s6u7u9', packed) 89 | self.assertEqual(unpacked, (0, -2, 65, 22)) 90 | 91 | packed = b'\x3e\x82\x16' 92 | unpacked = unpack('P1u1s6u7u9', packed) 93 | self.assertEqual(unpacked, (0, -2, 65, 22)) 94 | 95 | packed = b'\xbe\x82\x16' 96 | unpacked = unpack('p1u1s6u7u9', packed) 97 | self.assertEqual(unpacked, (0, -2, 65, 22)) 98 | 99 | packed = b'\x3e\x82\x16' 100 | unpacked = unpack('p1u1s6u7u9', packed) 101 | self.assertEqual(unpacked, (0, -2, 65, 22)) 102 | 103 | packed = b'\x3e\x82\x16' 104 | unpacked = unpack('p1u1s6p7u9', packed) 105 | self.assertEqual(unpacked, (0, -2, 22)) 106 | 107 | packed = b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0' 108 | unpacked = unpack('u1s6f32r43', packed) 109 | self.assertEqual(unpacked, (0, -2, 3.75, b'\x00\xff\x00\xff\x00\xe0')) 110 | 111 | packed = bytearray(b'\x80') 112 | unpacked = unpack('b1', packed) 113 | self.assertEqual(unpacked, (True, )) 114 | 115 | packed = b'\x80' 116 | unpacked = unpack('b1p6b1', packed) 117 | self.assertEqual(unpacked, (True, False)) 118 | 119 | packed = b'\x06' 120 | unpacked = unpack('u5b2u1', packed) 121 | self.assertEqual(unpacked, (0, True, 0)) 122 | 123 | packed = b'\x04' 124 | unpacked = unpack('u5b2u1', packed) 125 | self.assertEqual(unpacked, (0, True, 0)) 126 | 127 | packed = b'$4\x90\x80' 128 | unpacked = unpack('b1t24', packed) 129 | self.assertEqual(unpacked, (False, u'Hi!')) 130 | 131 | # Bad float size. 132 | with self.assertRaises(Error) as cm: 133 | unpack('f33', b'\x00\x00\x00\x00\x00') 134 | 135 | # Too many bits to unpack. 136 | with self.assertRaises(Error) as cm: 137 | unpack('u9', b'\x00') 138 | 139 | # gcc packed struct with bitfields 140 | # 141 | # struct foo_t { 142 | # int a; 143 | # char b; 144 | # uint32_t c : 7; 145 | # uint32_t d : 25; 146 | # } foo; 147 | # 148 | # foo.a = 1; 149 | # foo.b = 1; 150 | # foo.c = 0x67; 151 | # foo.d = 0x12345; 152 | unpacked = unpack('s32s8u25u7', 153 | byteswap('414', 154 | b'\x01\x00\x00\x00\x01\xe7\xa2\x91\x00')) 155 | self.assertEqual(unpacked, (1, 1, 0x12345, 0x67)) 156 | 157 | def test_pack_unpack(self): 158 | """Pack and unpack values. 159 | 160 | """ 161 | 162 | packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) 163 | unpacked = unpack('u1u1s6u7u9', packed) 164 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 165 | 166 | packed = pack('f64', 1.0) 167 | unpacked = unpack('f64', packed) 168 | self.assertEqual(unpacked, (1.0, )) 169 | 170 | if sys.version_info >= (3, 6): 171 | packed = pack('f16', 1.0) 172 | unpacked = unpack('f16', packed) 173 | self.assertEqual(unpacked, (1.0, )) 174 | 175 | def test_calcsize(self): 176 | """Calculate size. 177 | 178 | """ 179 | 180 | size = calcsize('u1u1s6u7u9') 181 | self.assertEqual(size, 24) 182 | 183 | size = calcsize('u1') 184 | self.assertEqual(size, 1) 185 | 186 | size = calcsize('u1s6u7u9') 187 | self.assertEqual(size, 23) 188 | 189 | size = calcsize('b1s6u7u9p1t8') 190 | self.assertEqual(size, 32) 191 | 192 | size = calcsize('b1s6u7u9P1t8') 193 | self.assertEqual(size, 32) 194 | 195 | def test_byteswap(self): 196 | """Byte swap. 197 | 198 | """ 199 | 200 | res = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a' 201 | ref = b'\x01\x03\x02\x04\x08\x07\x06\x05\x0a\x09' 202 | self.assertEqual(byteswap('12142', ref), res) 203 | 204 | packed = pack('u1u5u2u16', 1, 2, 3, 4) 205 | unpacked = unpack('u1u5u2u16', byteswap('12', packed)) 206 | self.assertEqual(unpacked, (1, 2, 3, 1024)) 207 | 208 | def test_endianness(self): 209 | """Test pack/unpack with endianness information in the format string. 210 | 211 | """ 212 | 213 | # Big endian. 214 | ref = b'\x02\x46\x9a\xfe\x00\x00\x00' 215 | packed = pack('>u19s3f32', 0x1234, -2, -1.0) 216 | self.assertEqual(packed, ref) 217 | unpacked = unpack('>u19s3f32', packed) 218 | self.assertEqual(unpacked, (0x1234, -2, -1.0)) 219 | 220 | # Little endian. 221 | ref = b'\x2c\x48\x0c\x00\x00\x07\xf4' 222 | packed = pack('u19f64r3p4', 1, -2, 1.0, b'\x80') 230 | self.assertEqual(packed, ref) 231 | unpacked = unpack('>u19f64r3p4', packed) 232 | self.assertEqual(unpacked, (1, -2, 1.0, b'\x80')) 233 | 234 | # Opposite endianness of the 'mixed endianness' test. 235 | ref = b'\x80\x00\x1e\x00\x00\x00\x00\x00\x00\x0f\xfc\x20' 236 | packed = pack('s5s5', 0x1234, -2, -1.0) 256 | self.assertEqual(packed, ref) 257 | unpacked = unpack('u19s3f32>', packed) 258 | self.assertEqual(unpacked, (0x1234, -2, -1.0)) 259 | 260 | # Least significant byte first. 261 | ref = b'\x34\x12\x18\x00\x00\xe0\xbc' 262 | packed = pack('u19s3f32<', 0x1234, -2, -1.0) 263 | self.assertEqual(packed, ref) 264 | unpacked = unpack('u19s3f32<', packed) 265 | self.assertEqual(unpacked, (0x1234, -2, -1.0)) 266 | 267 | # Least significant byte first. 268 | ref = b'\x34\x12' 269 | packed = pack('u8s8<', 0x34, 0x12) 270 | self.assertEqual(packed, ref) 271 | unpacked = unpack('u8s8<', packed) 272 | self.assertEqual(unpacked, (0x34, 0x12)) 273 | 274 | # Least significant byte first. 275 | ref = b'\x34\x22' 276 | packed = pack('u3u12<', 1, 0x234) 277 | self.assertEqual(packed, ref) 278 | unpacked = unpack('u3s12<', packed) 279 | self.assertEqual(unpacked, (1, 0x234)) 280 | 281 | # Least significant byte first. 282 | ref = b'\x34\x11\x00' 283 | packed = pack('u3u17<', 1, 0x234) 284 | self.assertEqual(packed, ref) 285 | unpacked = unpack('u3s17<', packed) 286 | self.assertEqual(unpacked, (1, 0x234)) 287 | 288 | # Least significant byte first. 289 | ref = b'\x80' 290 | packed = pack('u1<', 1) 291 | self.assertEqual(packed, ref) 292 | unpacked = unpack('u1<', packed) 293 | self.assertEqual(unpacked, (1, )) 294 | 295 | # Least significant byte first. 296 | ref = b'\x45\x23\x25\x82' 297 | packed = pack('u19u5u1u7<', 0x12345, 5, 1, 2) 298 | self.assertEqual(packed, ref) 299 | unpacked = unpack('u19u5u1u7<', packed) 300 | self.assertEqual(unpacked, (0x12345, 5, 1, 2)) 301 | 302 | # Least significant byte first does not affect raw and text. 303 | ref = b'123abc' 304 | packed = pack('r24t24<', b'123', 'abc') 305 | self.assertEqual(packed, ref) 306 | unpacked = unpack('r24t24<', packed) 307 | self.assertEqual(unpacked, (b'123', 'abc')) 308 | 309 | def test_compile(self): 310 | cf = cbitstruct.compile('u1u1s6u7u9') 311 | 312 | packed = cf.pack(0, 0, -2, 65, 22) 313 | self.assertEqual(packed, b'\x3e\x82\x16') 314 | 315 | unpacked = cf.unpack(b'\x3e\x82\x16') 316 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 317 | 318 | def test_signed_integer(self): 319 | """Pack and unpack signed integer values. 320 | 321 | """ 322 | 323 | datas = [ 324 | ('s2', 0x01, b'\x40'), 325 | ('s3', 0x03, b'\x60'), 326 | ('s4', 0x07, b'\x70'), 327 | ('s5', 0x0f, b'\x78'), 328 | ('s6', 0x1f, b'\x7c'), 329 | ('s7', 0x3f, b'\x7e'), 330 | ('s8', 0x7f, b'\x7f'), 331 | ('s9', 0xff, b'\x7f\x80'), 332 | ('s1', -1, b'\x80'), 333 | ('s2', -1, b'\xc0') 334 | ] 335 | 336 | for fmt, value, packed in datas: 337 | self.assertEqual(pack(fmt, value), packed) 338 | self.assertEqual(unpack(fmt, packed), (value, )) 339 | 340 | def test_unsigned_integer(self): 341 | """Pack and unpack unsigned integer values. 342 | 343 | """ 344 | 345 | datas = [ 346 | ('u1', 0x001, b'\x80'), 347 | ('u2', 0x003, b'\xc0'), 348 | ('u3', 0x007, b'\xe0'), 349 | ('u4', 0x00f, b'\xf0'), 350 | ('u5', 0x01f, b'\xf8'), 351 | ('u6', 0x03f, b'\xfc'), 352 | ('u7', 0x07f, b'\xfe'), 353 | ('u8', 0x0ff, b'\xff'), 354 | ('u9', 0x1ff, b'\xff\x80') 355 | ] 356 | 357 | for fmt, value, packed in datas: 358 | self.assertEqual(pack(fmt, value), packed) 359 | self.assertEqual(unpack(fmt, packed), (value, )) 360 | 361 | def test_bad_float_size(self): 362 | """Test of bad float size. 363 | 364 | """ 365 | 366 | with self.assertRaises(Error) as cm: 367 | pack('f31', 1.0) 368 | 369 | with self.assertRaises(Error) as cm: 370 | unpack('f33', 8 * b'\x00') 371 | 372 | def test_bad_format(self): 373 | """Test of bad format. 374 | 375 | """ 376 | 377 | formats = [ 378 | ('g1', "bad char 'g' in format"), 379 | ('s1u1f32b1t8r8G13', "bad char 'G' in format"), 380 | ('s1u1f32b1t8r8G13S3', "bad char 'G' in format"), 381 | ('s', "bad format 's'"), 382 | ('1', "bad format '1'"), 383 | ('ss1', "bad format 'ss1'"), 384 | ('1s', "bad format '1s'"), 385 | ('foo', "bad format 'foo'"), 386 | ('s>1>', "bad format 's>1>'"), 387 | ('s0', "bad format 's0'") 388 | ] 389 | 390 | for fmt, expected_error in formats: 391 | with self.assertRaises(Error) as cm: 392 | cbitstruct.compile(fmt) 393 | 394 | def test_empty_format(self): 395 | """Test of empty format type. 396 | 397 | """ 398 | 399 | cf = cbitstruct.compile('') 400 | 401 | self.assertEqual(cf.pack(), b'') 402 | self.assertEqual(cf.pack(1), b'') 403 | 404 | self.assertEqual(cf.unpack(b''), ()) 405 | self.assertEqual(cf.unpack(b'\x00'), ()) 406 | 407 | def test_byte_order_format(self): 408 | """Test of a format with only byte order information. 409 | 410 | """ 411 | 412 | cf = cbitstruct.compile('>') 413 | 414 | self.assertEqual(cf.pack(), b'') 415 | self.assertEqual(cf.pack(1), b'') 416 | 417 | self.assertEqual(cf.unpack(b''), ()) 418 | self.assertEqual(cf.unpack(b'\x00'), ()) 419 | 420 | def test_pack_into(self): 421 | """Pack values into a buffer. 422 | 423 | """ 424 | 425 | packed = bytearray(3) 426 | pack_into('u1u1s6u7u9', packed, 0, 0, 0, -2, 65, 22) 427 | self.assertEqual(packed, b'\x3e\x82\x16') 428 | 429 | datas = [ 430 | (0, b'\x80\x00'), 431 | (1, b'\x40\x00'), 432 | (7, b'\x01\x00'), 433 | (15, b'\x00\x01') 434 | ] 435 | 436 | for offset, expected in datas: 437 | packed = bytearray(2) 438 | pack_into('u1', packed, offset, 1) 439 | self.assertEqual(packed, expected) 440 | 441 | packed = bytearray(b'\xff\xff\xff') 442 | pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=False) 443 | self.assertEqual(packed, b'\xf1\xf2\xf3') 444 | 445 | packed = bytearray(b'\xff\xff\xff') 446 | pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=True) 447 | self.assertEqual(packed, b'\x01\x02\x03') 448 | 449 | packed = bytearray(2) 450 | 451 | with self.assertRaises(Error) as cm: 452 | pack_into('u17', packed, 0, 1) 453 | 454 | packed = bytearray(b'\x00') 455 | pack_into('P4u4', packed, 0, 1) 456 | self.assertEqual(packed, b'\xf1') 457 | 458 | # Too many values to pack. 459 | with self.assertRaises(Error) as cm: 460 | packed = bytearray(b'\x00') 461 | pack_into('b1t24', packed, 0, False) 462 | 463 | def test_unpack_from(self): 464 | """Unpack values at given bit offset. 465 | 466 | """ 467 | 468 | unpacked = unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b\x00', 1) 469 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 470 | 471 | with self.assertRaises(Error) as cm: 472 | unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b', 1) 473 | 474 | def test_pack_integers_value_checks(self): 475 | """Pack integer values range checks. 476 | 477 | """ 478 | 479 | # Formats with minimum and maximum allowed values. 480 | datas = [ 481 | ('s1', -1, 0), 482 | ('s2', -2, 1), 483 | ('s3', -4, 3), 484 | ('u1', 0, 1), 485 | ('u2', 0, 3), 486 | ('u3', 0, 7) 487 | ] 488 | 489 | for fmt, minimum, maximum in datas: 490 | # No exception should be raised for numbers in range. 491 | pack(fmt, minimum) 492 | pack(fmt, maximum) 493 | 494 | # Numbers out of range. 495 | for number in [minimum - 1, maximum + 1]: 496 | with self.assertRaises(Error) as cm: 497 | pack(fmt, number) 498 | 499 | def test_pack_unpack_raw(self): 500 | """Pack and unpack raw values. 501 | 502 | """ 503 | 504 | packed = pack('r24', b'') 505 | self.assertEqual(packed, b'\x00\x00\x00') 506 | packed = pack('r24', b'12') 507 | self.assertEqual(packed, b'12\x00') 508 | packed = pack('r24', b'123') 509 | self.assertEqual(packed, b'123') 510 | packed = pack('r24', b'1234') 511 | self.assertEqual(packed, b'123') 512 | 513 | unpacked = unpack('r24', b'\x00\x00\x00')[0] 514 | self.assertEqual(unpacked, b'\x00\x00\x00') 515 | unpacked = unpack('r24', b'12\x00')[0] 516 | self.assertEqual(unpacked, b'12\x00') 517 | unpacked = unpack('r24', b'123')[0] 518 | self.assertEqual(unpacked, b'123') 519 | unpacked = unpack('r24', b'1234')[0] 520 | self.assertEqual(unpacked, b'123') 521 | 522 | def test_pack_unpack_text(self): 523 | """Pack and unpack text values. 524 | 525 | """ 526 | 527 | packed = pack('t24', '') 528 | self.assertEqual(packed, b'\x00\x00\x00') 529 | packed = pack('t24', '12') 530 | self.assertEqual(packed, b'12\x00') 531 | packed = pack('t24', '123') 532 | self.assertEqual(packed, b'123') 533 | packed = pack('t24', '1234') 534 | self.assertEqual(packed, b'123') 535 | 536 | unpacked = unpack('t24', b'\x00\x00\x00')[0] 537 | self.assertEqual(unpacked, '\x00\x00\x00') 538 | unpacked = unpack('t24', b'12\x00')[0] 539 | self.assertEqual(unpacked, '12\x00') 540 | unpacked = unpack('t24', b'123')[0] 541 | self.assertEqual(unpacked, '123') 542 | unpacked = unpack('t24', b'1234')[0] 543 | self.assertEqual(unpacked, '123') 544 | 545 | def test_pack_unpack_dict(self): 546 | unpacked = { 547 | 'foo': 0, 548 | 'bar': 0, 549 | 'fie': -2, 550 | 'fum': 65, 551 | 'fam': 22 552 | } 553 | packed = b'\x3e\x82\x16' 554 | fmt = 'u1u1s6u7u9' 555 | names = ['foo', 'bar', 'fie', 'fum', 'fam'] 556 | 557 | self.assertEqual(pack_dict(fmt, names, unpacked), packed) 558 | self.assertEqual(unpack_dict(fmt, names, packed), unpacked) 559 | 560 | def test_pack_into_unpack_from_dict(self): 561 | unpacked = { 562 | 'foo': 0, 563 | 'bar': 0, 564 | 'fie': -2, 565 | 'fum': 65, 566 | 'fam': 22 567 | } 568 | packed = b'\x3e\x82\x16' 569 | fmt = 'u1u1s6u7u9' 570 | names = ['foo', 'bar', 'fie', 'fum', 'fam'] 571 | 572 | actual = bytearray(3) 573 | pack_into_dict(fmt, names, actual, 0, unpacked) 574 | self.assertEqual(actual, packed) 575 | self.assertEqual(unpack_from_dict(fmt, names, packed), unpacked) 576 | 577 | def test_pack_dict_missing_key(self): 578 | unpacked = { 579 | 'foo': 0, 580 | 'bar': 0, 581 | 'fie': -2, 582 | 'fum': 65 583 | } 584 | fmt = 'u1u1s6u7u9' 585 | names = ['foo', 'bar', 'fie', 'fum', 'fam'] 586 | 587 | with self.assertRaises(KeyError) as cm: 588 | pack_dict(fmt, names, unpacked) 589 | 590 | with self.assertRaises(KeyError) as cm: 591 | data = bytearray(3) 592 | pack_into_dict(fmt, names, data, 0, unpacked) 593 | 594 | def test_compile_pack_unpack_formats(self): 595 | fmts = [ 596 | ('u1s2p3', None, (1, -1)), 597 | ('u1 s2 p3', None, (1, -1)), 598 | ('u1s2p3', ['a', 'b'], {'a': 1, 'b': -1}) 599 | ] 600 | 601 | for fmt, names, decoded in fmts: 602 | if names is None: 603 | cf = cbitstruct.compile(fmt) 604 | packed_1 = cf.pack(*decoded) 605 | packed_2 = pack(fmt, *decoded) 606 | else: 607 | cf = cbitstruct.compile(fmt, names) 608 | packed_1 = cf.pack(decoded) 609 | packed_2 = pack_dict(fmt, names, decoded) 610 | 611 | self.assertEqual(packed_1, b'\xe0') 612 | self.assertEqual(packed_2, b'\xe0') 613 | 614 | def test_compile_formats(self): 615 | cbitstruct.compile('p1u1') 616 | cbitstruct.compile('p1u1', ['a']) 617 | 618 | 619 | if __name__ == '__main__': 620 | unittest.main() 621 | -------------------------------------------------------------------------------- /cbitstruct/clinic/_cbitstruct.c.36.h: -------------------------------------------------------------------------------- 1 | /*[clinic input] 2 | preserve 3 | [clinic start generated code]*/ 4 | 5 | PyDoc_STRVAR(CompiledFormat___init____doc__, 6 | "CompiledFormat(fmt)\n" 7 | "--\n" 8 | "\n" 9 | "Create a compiled bitstruct object.\n" 10 | "\n" 11 | "Return a new CompiledFormat object which writes and reads binary data\n" 12 | "according to the format string."); 13 | 14 | static int 15 | CompiledFormat___init___impl(PyCompiledFormatObject *self, const char *fmt); 16 | 17 | static int 18 | CompiledFormat___init__(PyObject *self, PyObject *args, PyObject *kwargs) 19 | { 20 | int return_value = -1; 21 | static const char * const _keywords[] = {"fmt", NULL}; 22 | static _PyArg_Parser _parser = {"s:CompiledFormat", _keywords, 0}; 23 | const char *fmt; 24 | 25 | if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, 26 | &fmt)) { 27 | goto exit; 28 | } 29 | return_value = CompiledFormat___init___impl((PyCompiledFormatObject *)self, fmt); 30 | 31 | exit: 32 | return return_value; 33 | } 34 | 35 | PyDoc_STRVAR(CompiledFormat_calcsize__doc__, 36 | "calcsize($self, /)\n" 37 | "--\n" 38 | "\n" 39 | "Return size in bits of the bitDescribed by the format string."); 40 | 41 | #define COMPILEDFORMAT_CALCSIZE_METHODDEF \ 42 | {"calcsize", (PyCFunction)CompiledFormat_calcsize, METH_NOARGS, CompiledFormat_calcsize__doc__}, 43 | 44 | static Py_ssize_t 45 | CompiledFormat_calcsize_impl(PyCompiledFormatObject *self); 46 | 47 | static PyObject * 48 | CompiledFormat_calcsize(PyCompiledFormatObject *self, PyObject *Py_UNUSED(ignored)) 49 | { 50 | PyObject *return_value = NULL; 51 | Py_ssize_t _return_value; 52 | 53 | _return_value = CompiledFormat_calcsize_impl(self); 54 | if ((_return_value == -1) && PyErr_Occurred()) { 55 | goto exit; 56 | } 57 | return_value = PyLong_FromSsize_t(_return_value); 58 | 59 | exit: 60 | return return_value; 61 | } 62 | 63 | PyDoc_STRVAR(CompiledFormat_unpack__doc__, 64 | "unpack($self, /, data)\n" 65 | "--\n" 66 | "\n" 67 | "Return a tuple containing unpacked values."); 68 | 69 | #define COMPILEDFORMAT_UNPACK_METHODDEF \ 70 | {"unpack", (PyCFunction)CompiledFormat_unpack, METH_FASTCALL, CompiledFormat_unpack__doc__}, 71 | 72 | static PyObject * 73 | CompiledFormat_unpack_impl(PyCompiledFormatObject *self, Py_buffer *data); 74 | 75 | static PyObject * 76 | CompiledFormat_unpack(PyCompiledFormatObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 77 | { 78 | PyObject *return_value = NULL; 79 | static const char * const _keywords[] = {"data", NULL}; 80 | static _PyArg_Parser _parser = {"y*:unpack", _keywords, 0}; 81 | Py_buffer data = {NULL, NULL}; 82 | 83 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 84 | &data)) { 85 | goto exit; 86 | } 87 | return_value = CompiledFormat_unpack_impl(self, &data); 88 | 89 | exit: 90 | /* Cleanup for data */ 91 | if (data.obj) { 92 | PyBuffer_Release(&data); 93 | } 94 | 95 | return return_value; 96 | } 97 | 98 | PyDoc_STRVAR(CompiledFormat_unpack_from__doc__, 99 | "unpack_from($self, /, data, offset=0)\n" 100 | "--\n" 101 | "\n" 102 | "Return a tuple containing unpacked values starting at \'offset\' bits."); 103 | 104 | #define COMPILEDFORMAT_UNPACK_FROM_METHODDEF \ 105 | {"unpack_from", (PyCFunction)CompiledFormat_unpack_from, METH_FASTCALL, CompiledFormat_unpack_from__doc__}, 106 | 107 | static PyObject * 108 | CompiledFormat_unpack_from_impl(PyCompiledFormatObject *self, 109 | Py_buffer *data, Py_ssize_t offset); 110 | 111 | static PyObject * 112 | CompiledFormat_unpack_from(PyCompiledFormatObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 113 | { 114 | PyObject *return_value = NULL; 115 | static const char * const _keywords[] = {"data", "offset", NULL}; 116 | static _PyArg_Parser _parser = {"y*|n:unpack_from", _keywords, 0}; 117 | Py_buffer data = {NULL, NULL}; 118 | Py_ssize_t offset = 0; 119 | 120 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 121 | &data, &offset)) { 122 | goto exit; 123 | } 124 | return_value = CompiledFormat_unpack_from_impl(self, &data, offset); 125 | 126 | exit: 127 | /* Cleanup for data */ 128 | if (data.obj) { 129 | PyBuffer_Release(&data); 130 | } 131 | 132 | return return_value; 133 | } 134 | 135 | PyDoc_STRVAR(CompiledFormatDict___init____doc__, 136 | "CompiledFormatDict(fmt, names)\n" 137 | "--\n" 138 | "\n" 139 | "Create a compiled bitstruct object.\n" 140 | "\n" 141 | "Return a new CompiledFormatDict object which writes and reads binary data\n" 142 | "according to the format string. The names list \'name\' will be used\n" 143 | "as keys in data dictionaries."); 144 | 145 | static int 146 | CompiledFormatDict___init___impl(PyCompiledFormatDictObject *self, 147 | const char *fmt, PyObject *names); 148 | 149 | static int 150 | CompiledFormatDict___init__(PyObject *self, PyObject *args, PyObject *kwargs) 151 | { 152 | int return_value = -1; 153 | static const char * const _keywords[] = {"fmt", "names", NULL}; 154 | static _PyArg_Parser _parser = {"sO:CompiledFormatDict", _keywords, 0}; 155 | const char *fmt; 156 | PyObject *names; 157 | 158 | if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, 159 | &fmt, &names)) { 160 | goto exit; 161 | } 162 | return_value = CompiledFormatDict___init___impl((PyCompiledFormatDictObject *)self, fmt, names); 163 | 164 | exit: 165 | return return_value; 166 | } 167 | 168 | PyDoc_STRVAR(CompiledFormatDict_pack__doc__, 169 | "pack($self, /, data)\n" 170 | "--\n" 171 | "\n" 172 | "Pack values from a dict into a bytes object\n" 173 | "\n" 174 | "Return a tuple containing unpacked values.\n" 175 | "\'data\' is a dictionary containing values whic keys are the \'names\'\n" 176 | "used when constructing this object."); 177 | 178 | #define COMPILEDFORMATDICT_PACK_METHODDEF \ 179 | {"pack", (PyCFunction)CompiledFormatDict_pack, METH_FASTCALL, CompiledFormatDict_pack__doc__}, 180 | 181 | static PyObject * 182 | CompiledFormatDict_pack_impl(PyCompiledFormatDictObject *self, 183 | PyObject *data); 184 | 185 | static PyObject * 186 | CompiledFormatDict_pack(PyCompiledFormatDictObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 187 | { 188 | PyObject *return_value = NULL; 189 | static const char * const _keywords[] = {"data", NULL}; 190 | static _PyArg_Parser _parser = {"O:pack", _keywords, 0}; 191 | PyObject *data; 192 | 193 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 194 | &data)) { 195 | goto exit; 196 | } 197 | return_value = CompiledFormatDict_pack_impl(self, data); 198 | 199 | exit: 200 | return return_value; 201 | } 202 | 203 | PyDoc_STRVAR(CompiledFormatDict_pack_into__doc__, 204 | "pack_into($self, /, buf, offset, data, *, fill_padding=True)\n" 205 | "--\n" 206 | "\n" 207 | "Pack data into a bytes object, starting at bit offset given by the offset argument.\n" 208 | "\n" 209 | "With fill_padding=False, passing bits in \'buf\' will not be modified."); 210 | 211 | #define COMPILEDFORMATDICT_PACK_INTO_METHODDEF \ 212 | {"pack_into", (PyCFunction)CompiledFormatDict_pack_into, METH_FASTCALL, CompiledFormatDict_pack_into__doc__}, 213 | 214 | static PyObject * 215 | CompiledFormatDict_pack_into_impl(PyCompiledFormatDictObject *self, 216 | Py_buffer *buf, Py_ssize_t offset, 217 | PyObject *data, int fill_padding); 218 | 219 | static PyObject * 220 | CompiledFormatDict_pack_into(PyCompiledFormatDictObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 221 | { 222 | PyObject *return_value = NULL; 223 | static const char * const _keywords[] = {"buf", "offset", "data", "fill_padding", NULL}; 224 | static _PyArg_Parser _parser = {"y*nO|$p:pack_into", _keywords, 0}; 225 | Py_buffer buf = {NULL, NULL}; 226 | Py_ssize_t offset; 227 | PyObject *data; 228 | int fill_padding = 1; 229 | 230 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 231 | &buf, &offset, &data, &fill_padding)) { 232 | goto exit; 233 | } 234 | return_value = CompiledFormatDict_pack_into_impl(self, &buf, offset, data, fill_padding); 235 | 236 | exit: 237 | /* Cleanup for buf */ 238 | if (buf.obj) { 239 | PyBuffer_Release(&buf); 240 | } 241 | 242 | return return_value; 243 | } 244 | 245 | PyDoc_STRVAR(CompiledFormatDict_unpack__doc__, 246 | "unpack($self, /, data)\n" 247 | "--\n" 248 | "\n" 249 | "Unpack data into a dict which keys are the \'names\' used when constructing this object.\n" 250 | "\n" 251 | "Return a dict containing unpacked values."); 252 | 253 | #define COMPILEDFORMATDICT_UNPACK_METHODDEF \ 254 | {"unpack", (PyCFunction)CompiledFormatDict_unpack, METH_FASTCALL, CompiledFormatDict_unpack__doc__}, 255 | 256 | static PyObject * 257 | CompiledFormatDict_unpack_impl(PyCompiledFormatDictObject *self, 258 | Py_buffer *data); 259 | 260 | static PyObject * 261 | CompiledFormatDict_unpack(PyCompiledFormatDictObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 262 | { 263 | PyObject *return_value = NULL; 264 | static const char * const _keywords[] = {"data", NULL}; 265 | static _PyArg_Parser _parser = {"y*:unpack", _keywords, 0}; 266 | Py_buffer data = {NULL, NULL}; 267 | 268 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 269 | &data)) { 270 | goto exit; 271 | } 272 | return_value = CompiledFormatDict_unpack_impl(self, &data); 273 | 274 | exit: 275 | /* Cleanup for data */ 276 | if (data.obj) { 277 | PyBuffer_Release(&data); 278 | } 279 | 280 | return return_value; 281 | } 282 | 283 | PyDoc_STRVAR(CompiledFormatDict_unpack_from__doc__, 284 | "unpack_from($self, /, data, offset=0)\n" 285 | "--\n" 286 | "\n" 287 | "Unpack data into a dict starting at \'offset\' bits.\n" 288 | "\n" 289 | "Return a dict containing unpacked values."); 290 | 291 | #define COMPILEDFORMATDICT_UNPACK_FROM_METHODDEF \ 292 | {"unpack_from", (PyCFunction)CompiledFormatDict_unpack_from, METH_FASTCALL, CompiledFormatDict_unpack_from__doc__}, 293 | 294 | static PyObject * 295 | CompiledFormatDict_unpack_from_impl(PyCompiledFormatDictObject *self, 296 | Py_buffer *data, Py_ssize_t offset); 297 | 298 | static PyObject * 299 | CompiledFormatDict_unpack_from(PyCompiledFormatDictObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 300 | { 301 | PyObject *return_value = NULL; 302 | static const char * const _keywords[] = {"data", "offset", NULL}; 303 | static _PyArg_Parser _parser = {"y*|n:unpack_from", _keywords, 0}; 304 | Py_buffer data = {NULL, NULL}; 305 | Py_ssize_t offset = 0; 306 | 307 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 308 | &data, &offset)) { 309 | goto exit; 310 | } 311 | return_value = CompiledFormatDict_unpack_from_impl(self, &data, offset); 312 | 313 | exit: 314 | /* Cleanup for data */ 315 | if (data.obj) { 316 | PyBuffer_Release(&data); 317 | } 318 | 319 | return return_value; 320 | } 321 | 322 | PyDoc_STRVAR(pack_dict__doc__, 323 | "pack_dict($module, /, fmt, names, data)\n" 324 | "--\n" 325 | "\n" 326 | "Pack the dict data into a bytes object according to format.\n" 327 | "\n" 328 | "The order of value is determines by the list \'names\'."); 329 | 330 | #define PACK_DICT_METHODDEF \ 331 | {"pack_dict", (PyCFunction)pack_dict, METH_FASTCALL, pack_dict__doc__}, 332 | 333 | static PyObject * 334 | pack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 335 | PyObject *data); 336 | 337 | static PyObject * 338 | pack_dict(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 339 | { 340 | PyObject *return_value = NULL; 341 | static const char * const _keywords[] = {"fmt", "names", "data", NULL}; 342 | static _PyArg_Parser _parser = {"sOO:pack_dict", _keywords, 0}; 343 | const char *fmt; 344 | PyObject *names; 345 | PyObject *data; 346 | 347 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 348 | &fmt, &names, &data)) { 349 | goto exit; 350 | } 351 | return_value = pack_dict_impl(module, fmt, names, data); 352 | 353 | exit: 354 | return return_value; 355 | } 356 | 357 | PyDoc_STRVAR(pack_into_dict__doc__, 358 | "pack_into_dict($module, /, fmt, names, buf, offset, data, *,\n" 359 | " fill_padding=True)\n" 360 | "--\n" 361 | "\n" 362 | "Pack data into a bytes object, starting at bit offset given by the offset argument.\n" 363 | "\n" 364 | "With fill_padding=False, passing bits in \'buf\' will not be modified."); 365 | 366 | #define PACK_INTO_DICT_METHODDEF \ 367 | {"pack_into_dict", (PyCFunction)pack_into_dict, METH_FASTCALL, pack_into_dict__doc__}, 368 | 369 | static PyObject * 370 | pack_into_dict_impl(PyObject *module, const char *fmt, PyObject *names, 371 | Py_buffer *buf, Py_ssize_t offset, PyObject *data, 372 | int fill_padding); 373 | 374 | static PyObject * 375 | pack_into_dict(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 376 | { 377 | PyObject *return_value = NULL; 378 | static const char * const _keywords[] = {"fmt", "names", "buf", "offset", "data", "fill_padding", NULL}; 379 | static _PyArg_Parser _parser = {"sOy*nO|$p:pack_into_dict", _keywords, 0}; 380 | const char *fmt; 381 | PyObject *names; 382 | Py_buffer buf = {NULL, NULL}; 383 | Py_ssize_t offset; 384 | PyObject *data; 385 | int fill_padding = 1; 386 | 387 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 388 | &fmt, &names, &buf, &offset, &data, &fill_padding)) { 389 | goto exit; 390 | } 391 | return_value = pack_into_dict_impl(module, fmt, names, &buf, offset, data, fill_padding); 392 | 393 | exit: 394 | /* Cleanup for buf */ 395 | if (buf.obj) { 396 | PyBuffer_Release(&buf); 397 | } 398 | 399 | return return_value; 400 | } 401 | 402 | PyDoc_STRVAR(unpack__doc__, 403 | "unpack($module, /, fmt, data)\n" 404 | "--\n" 405 | "\n" 406 | "Unpack data according to the format \'fmt\'. Returns a tuple."); 407 | 408 | #define UNPACK_METHODDEF \ 409 | {"unpack", (PyCFunction)unpack, METH_FASTCALL, unpack__doc__}, 410 | 411 | static PyObject * 412 | unpack_impl(PyObject *module, const char *fmt, Py_buffer *data); 413 | 414 | static PyObject * 415 | unpack(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 416 | { 417 | PyObject *return_value = NULL; 418 | static const char * const _keywords[] = {"fmt", "data", NULL}; 419 | static _PyArg_Parser _parser = {"sy*:unpack", _keywords, 0}; 420 | const char *fmt; 421 | Py_buffer data = {NULL, NULL}; 422 | 423 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 424 | &fmt, &data)) { 425 | goto exit; 426 | } 427 | return_value = unpack_impl(module, fmt, &data); 428 | 429 | exit: 430 | /* Cleanup for data */ 431 | if (data.obj) { 432 | PyBuffer_Release(&data); 433 | } 434 | 435 | return return_value; 436 | } 437 | 438 | PyDoc_STRVAR(unpack_from__doc__, 439 | "unpack_from($module, /, fmt, data, offset=0)\n" 440 | "--\n" 441 | "\n" 442 | "Unpack data according to the format \'fmt\', starting at bit offset \'offset.\n" 443 | "\n" 444 | "Returns a tuple."); 445 | 446 | #define UNPACK_FROM_METHODDEF \ 447 | {"unpack_from", (PyCFunction)unpack_from, METH_FASTCALL, unpack_from__doc__}, 448 | 449 | static PyObject * 450 | unpack_from_impl(PyObject *module, const char *fmt, Py_buffer *data, 451 | Py_ssize_t offset); 452 | 453 | static PyObject * 454 | unpack_from(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 455 | { 456 | PyObject *return_value = NULL; 457 | static const char * const _keywords[] = {"fmt", "data", "offset", NULL}; 458 | static _PyArg_Parser _parser = {"sy*|n:unpack_from", _keywords, 0}; 459 | const char *fmt; 460 | Py_buffer data = {NULL, NULL}; 461 | Py_ssize_t offset = 0; 462 | 463 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 464 | &fmt, &data, &offset)) { 465 | goto exit; 466 | } 467 | return_value = unpack_from_impl(module, fmt, &data, offset); 468 | 469 | exit: 470 | /* Cleanup for data */ 471 | if (data.obj) { 472 | PyBuffer_Release(&data); 473 | } 474 | 475 | return return_value; 476 | } 477 | 478 | PyDoc_STRVAR(unpack_dict__doc__, 479 | "unpack_dict($module, /, fmt, names, data)\n" 480 | "--\n" 481 | "\n" 482 | "Unpack data according to \'fmt\'.\n" 483 | "\n" 484 | "Returns a dict which keys are \'names\'."); 485 | 486 | #define UNPACK_DICT_METHODDEF \ 487 | {"unpack_dict", (PyCFunction)unpack_dict, METH_FASTCALL, unpack_dict__doc__}, 488 | 489 | static PyObject * 490 | unpack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 491 | Py_buffer *data); 492 | 493 | static PyObject * 494 | unpack_dict(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 495 | { 496 | PyObject *return_value = NULL; 497 | static const char * const _keywords[] = {"fmt", "names", "data", NULL}; 498 | static _PyArg_Parser _parser = {"sOy*:unpack_dict", _keywords, 0}; 499 | const char *fmt; 500 | PyObject *names; 501 | Py_buffer data = {NULL, NULL}; 502 | 503 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 504 | &fmt, &names, &data)) { 505 | goto exit; 506 | } 507 | return_value = unpack_dict_impl(module, fmt, names, &data); 508 | 509 | exit: 510 | /* Cleanup for data */ 511 | if (data.obj) { 512 | PyBuffer_Release(&data); 513 | } 514 | 515 | return return_value; 516 | } 517 | 518 | PyDoc_STRVAR(unpack_from_dict__doc__, 519 | "unpack_from_dict($module, /, fmt, names, data, offset=0)\n" 520 | "--\n" 521 | "\n" 522 | "Unpack data according to \'fmt\' starting at bit offset \'offset\'.\n" 523 | "\n" 524 | "Returns a dict which keys are \'names\'."); 525 | 526 | #define UNPACK_FROM_DICT_METHODDEF \ 527 | {"unpack_from_dict", (PyCFunction)unpack_from_dict, METH_FASTCALL, unpack_from_dict__doc__}, 528 | 529 | static PyObject * 530 | unpack_from_dict_impl(PyObject *module, const char *fmt, PyObject *names, 531 | Py_buffer *data, Py_ssize_t offset); 532 | 533 | static PyObject * 534 | unpack_from_dict(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 535 | { 536 | PyObject *return_value = NULL; 537 | static const char * const _keywords[] = {"fmt", "names", "data", "offset", NULL}; 538 | static _PyArg_Parser _parser = {"sOy*|n:unpack_from_dict", _keywords, 0}; 539 | const char *fmt; 540 | PyObject *names; 541 | Py_buffer data = {NULL, NULL}; 542 | Py_ssize_t offset = 0; 543 | 544 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 545 | &fmt, &names, &data, &offset)) { 546 | goto exit; 547 | } 548 | return_value = unpack_from_dict_impl(module, fmt, names, &data, offset); 549 | 550 | exit: 551 | /* Cleanup for data */ 552 | if (data.obj) { 553 | PyBuffer_Release(&data); 554 | } 555 | 556 | return return_value; 557 | } 558 | 559 | PyDoc_STRVAR(compile__doc__, 560 | "compile($module, /, fmt, names=None)\n" 561 | "--\n" 562 | "\n" 563 | "Returns a compiled object for the format \'fmt\'."); 564 | 565 | #define COMPILE_METHODDEF \ 566 | {"compile", (PyCFunction)compile, METH_FASTCALL, compile__doc__}, 567 | 568 | static PyObject * 569 | compile_impl(PyObject *module, const char *fmt, PyObject *names); 570 | 571 | static PyObject * 572 | compile(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 573 | { 574 | PyObject *return_value = NULL; 575 | static const char * const _keywords[] = {"fmt", "names", NULL}; 576 | static _PyArg_Parser _parser = {"s|O:compile", _keywords, 0}; 577 | const char *fmt; 578 | PyObject *names = Py_None; 579 | 580 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 581 | &fmt, &names)) { 582 | goto exit; 583 | } 584 | return_value = compile_impl(module, fmt, names); 585 | 586 | exit: 587 | return return_value; 588 | } 589 | 590 | PyDoc_STRVAR(calcsize__doc__, 591 | "calcsize($module, /, fmt)\n" 592 | "--\n" 593 | "\n" 594 | "Return size in bits of the bit described by the format string."); 595 | 596 | #define CALCSIZE_METHODDEF \ 597 | {"calcsize", (PyCFunction)calcsize, METH_FASTCALL, calcsize__doc__}, 598 | 599 | static Py_ssize_t 600 | calcsize_impl(PyObject *module, const char *fmt); 601 | 602 | static PyObject * 603 | calcsize(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 604 | { 605 | PyObject *return_value = NULL; 606 | static const char * const _keywords[] = {"fmt", NULL}; 607 | static _PyArg_Parser _parser = {"s:calcsize", _keywords, 0}; 608 | const char *fmt; 609 | Py_ssize_t _return_value; 610 | 611 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 612 | &fmt)) { 613 | goto exit; 614 | } 615 | _return_value = calcsize_impl(module, fmt); 616 | if ((_return_value == -1) && PyErr_Occurred()) { 617 | goto exit; 618 | } 619 | return_value = PyLong_FromSsize_t(_return_value); 620 | 621 | exit: 622 | return return_value; 623 | } 624 | 625 | PyDoc_STRVAR(byteswap__doc__, 626 | "byteswap($module, /, fmt, data, offset=0)\n" 627 | "--\n" 628 | "\n" 629 | "Swap bytes in `data` according to `fmt`, starting at byte `offset` and return the result.\n" 630 | "\n" 631 | "`fmt` must be an iterable, iterating over\n" 632 | "number of bytes to swap. For example, the format string ``\'24\'``\n" 633 | "applied to the bytes ``b\'\\x00\\x11\\x22\\x33\\x44\\x55\'`` will\n" 634 | "produce the result ``b\'\\x11\\x00\\x55\\x44\\x33\\x22\'``."); 635 | 636 | #define BYTESWAP_METHODDEF \ 637 | {"byteswap", (PyCFunction)byteswap, METH_FASTCALL, byteswap__doc__}, 638 | 639 | static PyObject * 640 | byteswap_impl(PyObject *module, PyObject *fmt, Py_buffer *data, 641 | Py_ssize_t offset); 642 | 643 | static PyObject * 644 | byteswap(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) 645 | { 646 | PyObject *return_value = NULL; 647 | static const char * const _keywords[] = {"fmt", "data", "offset", NULL}; 648 | static _PyArg_Parser _parser = {"Oy*|n:byteswap", _keywords, 0}; 649 | PyObject *fmt; 650 | Py_buffer data = {NULL, NULL}; 651 | Py_ssize_t offset = 0; 652 | 653 | if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser, 654 | &fmt, &data, &offset)) { 655 | goto exit; 656 | } 657 | return_value = byteswap_impl(module, fmt, &data, offset); 658 | 659 | exit: 660 | /* Cleanup for data */ 661 | if (data.obj) { 662 | PyBuffer_Release(&data); 663 | } 664 | 665 | return return_value; 666 | } 667 | /*[clinic end generated code: output=93b815cb6fb17f58 input=a9049054013a1b77]*/ 668 | -------------------------------------------------------------------------------- /cbitstruct/clinic/_cbitstruct.c.37.h: -------------------------------------------------------------------------------- 1 | /*[clinic input] 2 | preserve 3 | [clinic start generated code]*/ 4 | 5 | PyDoc_STRVAR(CompiledFormat___init____doc__, 6 | "CompiledFormat(fmt)\n" 7 | "--\n" 8 | "\n" 9 | "Create a compiled bitstruct object.\n" 10 | "\n" 11 | "Return a new CompiledFormat object which writes and reads binary data\n" 12 | "according to the format string."); 13 | 14 | static int 15 | CompiledFormat___init___impl(PyCompiledFormatObject *self, const char *fmt); 16 | 17 | static int 18 | CompiledFormat___init__(PyObject *self, PyObject *args, PyObject *kwargs) 19 | { 20 | int return_value = -1; 21 | static const char * const _keywords[] = {"fmt", NULL}; 22 | static _PyArg_Parser _parser = {.format="s:CompiledFormat", .keywords=_keywords, .fname=0}; 23 | const char *fmt; 24 | 25 | if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, 26 | &fmt)) { 27 | goto exit; 28 | } 29 | return_value = CompiledFormat___init___impl((PyCompiledFormatObject *)self, fmt); 30 | 31 | exit: 32 | return return_value; 33 | } 34 | 35 | PyDoc_STRVAR(CompiledFormat_calcsize__doc__, 36 | "calcsize($self, /)\n" 37 | "--\n" 38 | "\n" 39 | "Return size in bits of the bitDescribed by the format string."); 40 | 41 | #define COMPILEDFORMAT_CALCSIZE_METHODDEF \ 42 | {"calcsize", (PyCFunction)CompiledFormat_calcsize, METH_NOARGS, CompiledFormat_calcsize__doc__}, 43 | 44 | static Py_ssize_t 45 | CompiledFormat_calcsize_impl(PyCompiledFormatObject *self); 46 | 47 | static PyObject * 48 | CompiledFormat_calcsize(PyCompiledFormatObject *self, PyObject *Py_UNUSED(ignored)) 49 | { 50 | PyObject *return_value = NULL; 51 | Py_ssize_t _return_value; 52 | 53 | _return_value = CompiledFormat_calcsize_impl(self); 54 | if ((_return_value == -1) && PyErr_Occurred()) { 55 | goto exit; 56 | } 57 | return_value = PyLong_FromSsize_t(_return_value); 58 | 59 | exit: 60 | return return_value; 61 | } 62 | 63 | PyDoc_STRVAR(CompiledFormat_unpack__doc__, 64 | "unpack($self, /, data)\n" 65 | "--\n" 66 | "\n" 67 | "Return a tuple containing unpacked values."); 68 | 69 | #define COMPILEDFORMAT_UNPACK_METHODDEF \ 70 | {"unpack", (PyCFunction)CompiledFormat_unpack, METH_FASTCALL|METH_KEYWORDS, CompiledFormat_unpack__doc__}, 71 | 72 | static PyObject * 73 | CompiledFormat_unpack_impl(PyCompiledFormatObject *self, Py_buffer *data); 74 | 75 | static PyObject * 76 | CompiledFormat_unpack(PyCompiledFormatObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 77 | { 78 | PyObject *return_value = NULL; 79 | static const char * const _keywords[] = {"data", NULL}; 80 | static _PyArg_Parser _parser = {.format="y*:unpack", .keywords=_keywords, .fname=0}; 81 | Py_buffer data = {NULL, NULL}; 82 | 83 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 84 | &data)) { 85 | goto exit; 86 | } 87 | return_value = CompiledFormat_unpack_impl(self, &data); 88 | 89 | exit: 90 | /* Cleanup for data */ 91 | if (data.obj) { 92 | PyBuffer_Release(&data); 93 | } 94 | 95 | return return_value; 96 | } 97 | 98 | PyDoc_STRVAR(CompiledFormat_unpack_from__doc__, 99 | "unpack_from($self, /, data, offset=0)\n" 100 | "--\n" 101 | "\n" 102 | "Return a tuple containing unpacked values starting at \'offset\' bits."); 103 | 104 | #define COMPILEDFORMAT_UNPACK_FROM_METHODDEF \ 105 | {"unpack_from", (PyCFunction)CompiledFormat_unpack_from, METH_FASTCALL|METH_KEYWORDS, CompiledFormat_unpack_from__doc__}, 106 | 107 | static PyObject * 108 | CompiledFormat_unpack_from_impl(PyCompiledFormatObject *self, 109 | Py_buffer *data, Py_ssize_t offset); 110 | 111 | static PyObject * 112 | CompiledFormat_unpack_from(PyCompiledFormatObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 113 | { 114 | PyObject *return_value = NULL; 115 | static const char * const _keywords[] = {"data", "offset", NULL}; 116 | static _PyArg_Parser _parser = {.format="y*|n:unpack_from", .keywords=_keywords, .fname=0}; 117 | Py_buffer data = {NULL, NULL}; 118 | Py_ssize_t offset = 0; 119 | 120 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 121 | &data, &offset)) { 122 | goto exit; 123 | } 124 | return_value = CompiledFormat_unpack_from_impl(self, &data, offset); 125 | 126 | exit: 127 | /* Cleanup for data */ 128 | if (data.obj) { 129 | PyBuffer_Release(&data); 130 | } 131 | 132 | return return_value; 133 | } 134 | 135 | PyDoc_STRVAR(CompiledFormatDict___init____doc__, 136 | "CompiledFormatDict(fmt, names)\n" 137 | "--\n" 138 | "\n" 139 | "Create a compiled bitstruct object.\n" 140 | "\n" 141 | "Return a new CompiledFormatDict object which writes and reads binary data\n" 142 | "according to the format string. The names list \'name\' will be used\n" 143 | "as keys in data dictionaries."); 144 | 145 | static int 146 | CompiledFormatDict___init___impl(PyCompiledFormatDictObject *self, 147 | const char *fmt, PyObject *names); 148 | 149 | static int 150 | CompiledFormatDict___init__(PyObject *self, PyObject *args, PyObject *kwargs) 151 | { 152 | int return_value = -1; 153 | static const char * const _keywords[] = {"fmt", "names", NULL}; 154 | static _PyArg_Parser _parser = {.format="sO:CompiledFormatDict", .keywords=_keywords, .fname=0}; 155 | const char *fmt; 156 | PyObject *names; 157 | 158 | if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, 159 | &fmt, &names)) { 160 | goto exit; 161 | } 162 | return_value = CompiledFormatDict___init___impl((PyCompiledFormatDictObject *)self, fmt, names); 163 | 164 | exit: 165 | return return_value; 166 | } 167 | 168 | PyDoc_STRVAR(CompiledFormatDict_pack__doc__, 169 | "pack($self, /, data)\n" 170 | "--\n" 171 | "\n" 172 | "Pack values from a dict into a bytes object\n" 173 | "\n" 174 | "Return a tuple containing unpacked values.\n" 175 | "\'data\' is a dictionary containing values whic keys are the \'names\'\n" 176 | "used when constructing this object."); 177 | 178 | #define COMPILEDFORMATDICT_PACK_METHODDEF \ 179 | {"pack", (PyCFunction)CompiledFormatDict_pack, METH_FASTCALL|METH_KEYWORDS, CompiledFormatDict_pack__doc__}, 180 | 181 | static PyObject * 182 | CompiledFormatDict_pack_impl(PyCompiledFormatDictObject *self, 183 | PyObject *data); 184 | 185 | static PyObject * 186 | CompiledFormatDict_pack(PyCompiledFormatDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 187 | { 188 | PyObject *return_value = NULL; 189 | static const char * const _keywords[] = {"data", NULL}; 190 | static _PyArg_Parser _parser = {.format="O:pack", .keywords=_keywords, .fname=0}; 191 | PyObject *data; 192 | 193 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 194 | &data)) { 195 | goto exit; 196 | } 197 | return_value = CompiledFormatDict_pack_impl(self, data); 198 | 199 | exit: 200 | return return_value; 201 | } 202 | 203 | PyDoc_STRVAR(CompiledFormatDict_pack_into__doc__, 204 | "pack_into($self, /, buf, offset, data, *, fill_padding=True)\n" 205 | "--\n" 206 | "\n" 207 | "Pack data into a bytes object, starting at bit offset given by the offset argument.\n" 208 | "\n" 209 | "With fill_padding=False, passing bits in \'buf\' will not be modified."); 210 | 211 | #define COMPILEDFORMATDICT_PACK_INTO_METHODDEF \ 212 | {"pack_into", (PyCFunction)CompiledFormatDict_pack_into, METH_FASTCALL|METH_KEYWORDS, CompiledFormatDict_pack_into__doc__}, 213 | 214 | static PyObject * 215 | CompiledFormatDict_pack_into_impl(PyCompiledFormatDictObject *self, 216 | Py_buffer *buf, Py_ssize_t offset, 217 | PyObject *data, int fill_padding); 218 | 219 | static PyObject * 220 | CompiledFormatDict_pack_into(PyCompiledFormatDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 221 | { 222 | PyObject *return_value = NULL; 223 | static const char * const _keywords[] = {"buf", "offset", "data", "fill_padding", NULL}; 224 | static _PyArg_Parser _parser = {.format="y*nO|$p:pack_into", .keywords=_keywords, .fname=0}; 225 | Py_buffer buf = {NULL, NULL}; 226 | Py_ssize_t offset; 227 | PyObject *data; 228 | int fill_padding = 1; 229 | 230 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 231 | &buf, &offset, &data, &fill_padding)) { 232 | goto exit; 233 | } 234 | return_value = CompiledFormatDict_pack_into_impl(self, &buf, offset, data, fill_padding); 235 | 236 | exit: 237 | /* Cleanup for buf */ 238 | if (buf.obj) { 239 | PyBuffer_Release(&buf); 240 | } 241 | 242 | return return_value; 243 | } 244 | 245 | PyDoc_STRVAR(CompiledFormatDict_unpack__doc__, 246 | "unpack($self, /, data)\n" 247 | "--\n" 248 | "\n" 249 | "Unpack data into a dict which keys are the \'names\' used when constructing this object.\n" 250 | "\n" 251 | "Return a dict containing unpacked values."); 252 | 253 | #define COMPILEDFORMATDICT_UNPACK_METHODDEF \ 254 | {"unpack", (PyCFunction)CompiledFormatDict_unpack, METH_FASTCALL|METH_KEYWORDS, CompiledFormatDict_unpack__doc__}, 255 | 256 | static PyObject * 257 | CompiledFormatDict_unpack_impl(PyCompiledFormatDictObject *self, 258 | Py_buffer *data); 259 | 260 | static PyObject * 261 | CompiledFormatDict_unpack(PyCompiledFormatDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 262 | { 263 | PyObject *return_value = NULL; 264 | static const char * const _keywords[] = {"data", NULL}; 265 | static _PyArg_Parser _parser = {.format="y*:unpack", .keywords=_keywords, .fname=0}; 266 | Py_buffer data = {NULL, NULL}; 267 | 268 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 269 | &data)) { 270 | goto exit; 271 | } 272 | return_value = CompiledFormatDict_unpack_impl(self, &data); 273 | 274 | exit: 275 | /* Cleanup for data */ 276 | if (data.obj) { 277 | PyBuffer_Release(&data); 278 | } 279 | 280 | return return_value; 281 | } 282 | 283 | PyDoc_STRVAR(CompiledFormatDict_unpack_from__doc__, 284 | "unpack_from($self, /, data, offset=0)\n" 285 | "--\n" 286 | "\n" 287 | "Unpack data into a dict starting at \'offset\' bits.\n" 288 | "\n" 289 | "Return a dict containing unpacked values."); 290 | 291 | #define COMPILEDFORMATDICT_UNPACK_FROM_METHODDEF \ 292 | {"unpack_from", (PyCFunction)CompiledFormatDict_unpack_from, METH_FASTCALL|METH_KEYWORDS, CompiledFormatDict_unpack_from__doc__}, 293 | 294 | static PyObject * 295 | CompiledFormatDict_unpack_from_impl(PyCompiledFormatDictObject *self, 296 | Py_buffer *data, Py_ssize_t offset); 297 | 298 | static PyObject * 299 | CompiledFormatDict_unpack_from(PyCompiledFormatDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 300 | { 301 | PyObject *return_value = NULL; 302 | static const char * const _keywords[] = {"data", "offset", NULL}; 303 | static _PyArg_Parser _parser = {.format="y*|n:unpack_from", .keywords=_keywords, .fname=0}; 304 | Py_buffer data = {NULL, NULL}; 305 | Py_ssize_t offset = 0; 306 | 307 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 308 | &data, &offset)) { 309 | goto exit; 310 | } 311 | return_value = CompiledFormatDict_unpack_from_impl(self, &data, offset); 312 | 313 | exit: 314 | /* Cleanup for data */ 315 | if (data.obj) { 316 | PyBuffer_Release(&data); 317 | } 318 | 319 | return return_value; 320 | } 321 | 322 | PyDoc_STRVAR(pack_dict__doc__, 323 | "pack_dict($module, /, fmt, names, data)\n" 324 | "--\n" 325 | "\n" 326 | "Pack the dict data into a bytes object according to format.\n" 327 | "\n" 328 | "The order of value is determines by the list \'names\'."); 329 | 330 | #define PACK_DICT_METHODDEF \ 331 | {"pack_dict", (PyCFunction)pack_dict, METH_FASTCALL|METH_KEYWORDS, pack_dict__doc__}, 332 | 333 | static PyObject * 334 | pack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 335 | PyObject *data); 336 | 337 | static PyObject * 338 | pack_dict(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 339 | { 340 | PyObject *return_value = NULL; 341 | static const char * const _keywords[] = {"fmt", "names", "data", NULL}; 342 | static _PyArg_Parser _parser = {.format="sOO:pack_dict", .keywords=_keywords, .fname=0}; 343 | const char *fmt; 344 | PyObject *names; 345 | PyObject *data; 346 | 347 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 348 | &fmt, &names, &data)) { 349 | goto exit; 350 | } 351 | return_value = pack_dict_impl(module, fmt, names, data); 352 | 353 | exit: 354 | return return_value; 355 | } 356 | 357 | PyDoc_STRVAR(pack_into_dict__doc__, 358 | "pack_into_dict($module, /, fmt, names, buf, offset, data, *,\n" 359 | " fill_padding=True)\n" 360 | "--\n" 361 | "\n" 362 | "Pack data into a bytes object, starting at bit offset given by the offset argument.\n" 363 | "\n" 364 | "With fill_padding=False, passing bits in \'buf\' will not be modified."); 365 | 366 | #define PACK_INTO_DICT_METHODDEF \ 367 | {"pack_into_dict", (PyCFunction)pack_into_dict, METH_FASTCALL|METH_KEYWORDS, pack_into_dict__doc__}, 368 | 369 | static PyObject * 370 | pack_into_dict_impl(PyObject *module, const char *fmt, PyObject *names, 371 | Py_buffer *buf, Py_ssize_t offset, PyObject *data, 372 | int fill_padding); 373 | 374 | static PyObject * 375 | pack_into_dict(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 376 | { 377 | PyObject *return_value = NULL; 378 | static const char * const _keywords[] = {"fmt", "names", "buf", "offset", "data", "fill_padding", NULL}; 379 | static _PyArg_Parser _parser = {.format="sOy*nO|$p:pack_into_dict", .keywords=_keywords, .fname=0}; 380 | const char *fmt; 381 | PyObject *names; 382 | Py_buffer buf = {NULL, NULL}; 383 | Py_ssize_t offset; 384 | PyObject *data; 385 | int fill_padding = 1; 386 | 387 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 388 | &fmt, &names, &buf, &offset, &data, &fill_padding)) { 389 | goto exit; 390 | } 391 | return_value = pack_into_dict_impl(module, fmt, names, &buf, offset, data, fill_padding); 392 | 393 | exit: 394 | /* Cleanup for buf */ 395 | if (buf.obj) { 396 | PyBuffer_Release(&buf); 397 | } 398 | 399 | return return_value; 400 | } 401 | 402 | PyDoc_STRVAR(unpack__doc__, 403 | "unpack($module, /, fmt, data)\n" 404 | "--\n" 405 | "\n" 406 | "Unpack data according to the format \'fmt\'. Returns a tuple."); 407 | 408 | #define UNPACK_METHODDEF \ 409 | {"unpack", (PyCFunction)unpack, METH_FASTCALL|METH_KEYWORDS, unpack__doc__}, 410 | 411 | static PyObject * 412 | unpack_impl(PyObject *module, const char *fmt, Py_buffer *data); 413 | 414 | static PyObject * 415 | unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 416 | { 417 | PyObject *return_value = NULL; 418 | static const char * const _keywords[] = {"fmt", "data", NULL}; 419 | static _PyArg_Parser _parser = {.format="sy*:unpack", .keywords=_keywords, .fname=0}; 420 | const char *fmt; 421 | Py_buffer data = {NULL, NULL}; 422 | 423 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 424 | &fmt, &data)) { 425 | goto exit; 426 | } 427 | return_value = unpack_impl(module, fmt, &data); 428 | 429 | exit: 430 | /* Cleanup for data */ 431 | if (data.obj) { 432 | PyBuffer_Release(&data); 433 | } 434 | 435 | return return_value; 436 | } 437 | 438 | PyDoc_STRVAR(unpack_from__doc__, 439 | "unpack_from($module, /, fmt, data, offset=0)\n" 440 | "--\n" 441 | "\n" 442 | "Unpack data according to the format \'fmt\', starting at bit offset \'offset.\n" 443 | "\n" 444 | "Returns a tuple."); 445 | 446 | #define UNPACK_FROM_METHODDEF \ 447 | {"unpack_from", (PyCFunction)unpack_from, METH_FASTCALL|METH_KEYWORDS, unpack_from__doc__}, 448 | 449 | static PyObject * 450 | unpack_from_impl(PyObject *module, const char *fmt, Py_buffer *data, 451 | Py_ssize_t offset); 452 | 453 | static PyObject * 454 | unpack_from(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 455 | { 456 | PyObject *return_value = NULL; 457 | static const char * const _keywords[] = {"fmt", "data", "offset", NULL}; 458 | static _PyArg_Parser _parser = {.format="sy*|n:unpack_from", .keywords=_keywords, .fname=0}; 459 | const char *fmt; 460 | Py_buffer data = {NULL, NULL}; 461 | Py_ssize_t offset = 0; 462 | 463 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 464 | &fmt, &data, &offset)) { 465 | goto exit; 466 | } 467 | return_value = unpack_from_impl(module, fmt, &data, offset); 468 | 469 | exit: 470 | /* Cleanup for data */ 471 | if (data.obj) { 472 | PyBuffer_Release(&data); 473 | } 474 | 475 | return return_value; 476 | } 477 | 478 | PyDoc_STRVAR(unpack_dict__doc__, 479 | "unpack_dict($module, /, fmt, names, data)\n" 480 | "--\n" 481 | "\n" 482 | "Unpack data according to \'fmt\'.\n" 483 | "\n" 484 | "Returns a dict which keys are \'names\'."); 485 | 486 | #define UNPACK_DICT_METHODDEF \ 487 | {"unpack_dict", (PyCFunction)unpack_dict, METH_FASTCALL|METH_KEYWORDS, unpack_dict__doc__}, 488 | 489 | static PyObject * 490 | unpack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 491 | Py_buffer *data); 492 | 493 | static PyObject * 494 | unpack_dict(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 495 | { 496 | PyObject *return_value = NULL; 497 | static const char * const _keywords[] = {"fmt", "names", "data", NULL}; 498 | static _PyArg_Parser _parser = {.format="sOy*:unpack_dict", .keywords=_keywords, .fname=0}; 499 | const char *fmt; 500 | PyObject *names; 501 | Py_buffer data = {NULL, NULL}; 502 | 503 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 504 | &fmt, &names, &data)) { 505 | goto exit; 506 | } 507 | return_value = unpack_dict_impl(module, fmt, names, &data); 508 | 509 | exit: 510 | /* Cleanup for data */ 511 | if (data.obj) { 512 | PyBuffer_Release(&data); 513 | } 514 | 515 | return return_value; 516 | } 517 | 518 | PyDoc_STRVAR(unpack_from_dict__doc__, 519 | "unpack_from_dict($module, /, fmt, names, data, offset=0)\n" 520 | "--\n" 521 | "\n" 522 | "Unpack data according to \'fmt\' starting at bit offset \'offset\'.\n" 523 | "\n" 524 | "Returns a dict which keys are \'names\'."); 525 | 526 | #define UNPACK_FROM_DICT_METHODDEF \ 527 | {"unpack_from_dict", (PyCFunction)unpack_from_dict, METH_FASTCALL|METH_KEYWORDS, unpack_from_dict__doc__}, 528 | 529 | static PyObject * 530 | unpack_from_dict_impl(PyObject *module, const char *fmt, PyObject *names, 531 | Py_buffer *data, Py_ssize_t offset); 532 | 533 | static PyObject * 534 | unpack_from_dict(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 535 | { 536 | PyObject *return_value = NULL; 537 | static const char * const _keywords[] = {"fmt", "names", "data", "offset", NULL}; 538 | static _PyArg_Parser _parser = {.format="sOy*|n:unpack_from_dict", .keywords=_keywords, .fname=0}; 539 | const char *fmt; 540 | PyObject *names; 541 | Py_buffer data = {NULL, NULL}; 542 | Py_ssize_t offset = 0; 543 | 544 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 545 | &fmt, &names, &data, &offset)) { 546 | goto exit; 547 | } 548 | return_value = unpack_from_dict_impl(module, fmt, names, &data, offset); 549 | 550 | exit: 551 | /* Cleanup for data */ 552 | if (data.obj) { 553 | PyBuffer_Release(&data); 554 | } 555 | 556 | return return_value; 557 | } 558 | 559 | PyDoc_STRVAR(compile__doc__, 560 | "compile($module, /, fmt, names=None)\n" 561 | "--\n" 562 | "\n" 563 | "Returns a compiled object for the format \'fmt\'."); 564 | 565 | #define COMPILE_METHODDEF \ 566 | {"compile", (PyCFunction)compile, METH_FASTCALL|METH_KEYWORDS, compile__doc__}, 567 | 568 | static PyObject * 569 | compile_impl(PyObject *module, const char *fmt, PyObject *names); 570 | 571 | static PyObject * 572 | compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 573 | { 574 | PyObject *return_value = NULL; 575 | static const char * const _keywords[] = {"fmt", "names", NULL}; 576 | static _PyArg_Parser _parser = {.format="s|O:compile", .keywords=_keywords, .fname=0}; 577 | const char *fmt; 578 | PyObject *names = Py_None; 579 | 580 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 581 | &fmt, &names)) { 582 | goto exit; 583 | } 584 | return_value = compile_impl(module, fmt, names); 585 | 586 | exit: 587 | return return_value; 588 | } 589 | 590 | PyDoc_STRVAR(calcsize__doc__, 591 | "calcsize($module, /, fmt)\n" 592 | "--\n" 593 | "\n" 594 | "Return size in bits of the bit described by the format string."); 595 | 596 | #define CALCSIZE_METHODDEF \ 597 | {"calcsize", (PyCFunction)calcsize, METH_FASTCALL|METH_KEYWORDS, calcsize__doc__}, 598 | 599 | static Py_ssize_t 600 | calcsize_impl(PyObject *module, const char *fmt); 601 | 602 | static PyObject * 603 | calcsize(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 604 | { 605 | PyObject *return_value = NULL; 606 | static const char * const _keywords[] = {"fmt", NULL}; 607 | static _PyArg_Parser _parser = {.format="s:calcsize", .keywords=_keywords, .fname=0}; 608 | const char *fmt; 609 | Py_ssize_t _return_value; 610 | 611 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 612 | &fmt)) { 613 | goto exit; 614 | } 615 | _return_value = calcsize_impl(module, fmt); 616 | if ((_return_value == -1) && PyErr_Occurred()) { 617 | goto exit; 618 | } 619 | return_value = PyLong_FromSsize_t(_return_value); 620 | 621 | exit: 622 | return return_value; 623 | } 624 | 625 | PyDoc_STRVAR(byteswap__doc__, 626 | "byteswap($module, /, fmt, data, offset=0)\n" 627 | "--\n" 628 | "\n" 629 | "Swap bytes in `data` according to `fmt`, starting at byte `offset` and return the result.\n" 630 | "\n" 631 | "`fmt` must be an iterable, iterating over\n" 632 | "number of bytes to swap. For example, the format string ``\'24\'``\n" 633 | "applied to the bytes ``b\'\\x00\\x11\\x22\\x33\\x44\\x55\'`` will\n" 634 | "produce the result ``b\'\\x11\\x00\\x55\\x44\\x33\\x22\'``."); 635 | 636 | #define BYTESWAP_METHODDEF \ 637 | {"byteswap", (PyCFunction)byteswap, METH_FASTCALL|METH_KEYWORDS, byteswap__doc__}, 638 | 639 | static PyObject * 640 | byteswap_impl(PyObject *module, PyObject *fmt, Py_buffer *data, 641 | Py_ssize_t offset); 642 | 643 | static PyObject * 644 | byteswap(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) 645 | { 646 | PyObject *return_value = NULL; 647 | static const char * const _keywords[] = {"fmt", "data", "offset", NULL}; 648 | static _PyArg_Parser _parser = {.format="Oy*|n:byteswap", .keywords=_keywords, .fname=0}; 649 | PyObject *fmt; 650 | Py_buffer data = {NULL, NULL}; 651 | Py_ssize_t offset = 0; 652 | 653 | if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 654 | &fmt, &data, &offset)) { 655 | goto exit; 656 | } 657 | return_value = byteswap_impl(module, fmt, &data, offset); 658 | 659 | exit: 660 | /* Cleanup for data */ 661 | if (data.obj) { 662 | PyBuffer_Release(&data); 663 | } 664 | 665 | return return_value; 666 | } 667 | /*[clinic end generated code: output=20c77328e33739fc input=a9049054013a1b77]*/ 668 | -------------------------------------------------------------------------------- /cbitstruct/tests/original/test_bitstruct.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import timeit 3 | import sys 4 | from bitstruct import * 5 | import bitstruct 6 | 7 | class BitStructTest(unittest.TestCase): 8 | 9 | def test_pack(self): 10 | """Pack values. 11 | 12 | """ 13 | 14 | packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) 15 | self.assertEqual(packed, b'\x3e\x82\x16') 16 | 17 | packed = pack('u1', 1) 18 | self.assertEqual(packed, b'\x80') 19 | 20 | packed = pack('u77', 0x100000000001000000) 21 | ref = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00' 22 | self.assertEqual(packed, ref) 23 | 24 | packed = pack('u8000', int(8000 * '1', 2)) 25 | ref = 1000 * b'\xff' 26 | self.assertEqual(packed, ref) 27 | 28 | packed = pack('s4000', int(8000 * '0', 2)) 29 | ref = 500 * b'\x00' 30 | self.assertEqual(packed, ref) 31 | 32 | packed = pack('p1u1s6u7u9', 0, -2, 65, 22) 33 | self.assertEqual(packed, b'\x3e\x82\x16') 34 | 35 | packed = pack('P1u1s6u7u9', 0, -2, 65, 22) 36 | self.assertEqual(packed, b'\xbe\x82\x16') 37 | 38 | packed = pack('p1u1s6p7u9', 0, -2, 22) 39 | self.assertEqual(packed, b'\x3e\x00\x16') 40 | 41 | packed = pack('P1u1s6p7u9', 0, -2, 22) 42 | self.assertEqual(packed, b'\xbe\x00\x16') 43 | 44 | packed = pack('u1s6f32r43', 0, -2, 3.75, b'\x00\xff\x00\xff\x00\xff') 45 | self.assertEqual(packed, b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0') 46 | 47 | packed = pack('b1', True) 48 | self.assertEqual(packed, b'\x80') 49 | 50 | packed = pack('b1p6b1', True, True) 51 | self.assertEqual(packed, b'\x81') 52 | 53 | packed = pack('b1P6b1', True, True) 54 | self.assertEqual(packed, b'\xff') 55 | 56 | packed = pack('u5b2u1', 31, False, 1) 57 | self.assertEqual(packed, b'\xf9') 58 | 59 | packed = pack('b1t24', False, u'Hi!') 60 | self.assertEqual(packed, b'$4\x90\x80') 61 | 62 | packed = pack('b1t24', False, 'Hi!') 63 | self.assertEqual(packed, b'$4\x90\x80') 64 | 65 | packed = pack('t8000', 1000 * '7') 66 | self.assertEqual(packed, 1000 * b'\x37') 67 | 68 | # Too many values to pack. 69 | with self.assertRaises(Error) as cm: 70 | pack('b1t24', False) 71 | 72 | self.assertEqual(str(cm.exception), 73 | 'pack expected 2 item(s) for packing (got 1)') 74 | 75 | # Cannot convert argument to integer. 76 | with self.assertRaises(ValueError) as cm: 77 | pack('u1', 'foo') 78 | 79 | self.assertEqual(str(cm.exception), 80 | "invalid literal for int() with base 10: 'foo'") 81 | 82 | # Cannot convert argument to float. 83 | with self.assertRaises(ValueError) as cm: 84 | pack('f32', 'foo') 85 | 86 | if sys.version_info[0] < 3: 87 | self.assertEqual(str(cm.exception), 88 | 'could not convert string to float: foo') 89 | else: 90 | self.assertEqual(str(cm.exception), 91 | "could not convert string to float: 'foo'") 92 | 93 | # Cannot convert argument to bytearray. 94 | with self.assertRaises(TypeError) as cm: 95 | pack('r5', 1.0) 96 | 97 | self.assertEqual(str(cm.exception), 98 | "object of type 'float' has no len()") 99 | 100 | # Cannot encode argument as utf-8. 101 | with self.assertRaises(AttributeError) as cm: 102 | pack('t8', 1.0) 103 | 104 | self.assertEqual(str(cm.exception), 105 | "'float' object has no attribute 'encode'") 106 | 107 | def test_unpack(self): 108 | """Unpack values. 109 | 110 | """ 111 | 112 | unpacked = unpack('u1u1s6u7u9', b'\x3e\x82\x16') 113 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 114 | 115 | unpacked = unpack('u1', bytearray(b'\x80')) 116 | self.assertEqual(unpacked, (1, )) 117 | 118 | packed = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00' 119 | unpacked = unpack('u77', packed) 120 | self.assertEqual(unpacked, (0x100000000001000000,)) 121 | 122 | packed = 1000 * b'\xff' 123 | unpacked = unpack('u8000', packed) 124 | self.assertEqual(unpacked, (int(8000 * '1', 2), )) 125 | 126 | packed = 500 * b'\x00' 127 | unpacked = unpack('s4000', packed) 128 | self.assertEqual(unpacked, (0, )) 129 | 130 | packed = b'\xbe\x82\x16' 131 | unpacked = unpack('P1u1s6u7u9', packed) 132 | self.assertEqual(unpacked, (0, -2, 65, 22)) 133 | 134 | packed = b'\x3e\x82\x16' 135 | unpacked = unpack('P1u1s6u7u9', packed) 136 | self.assertEqual(unpacked, (0, -2, 65, 22)) 137 | 138 | packed = b'\xbe\x82\x16' 139 | unpacked = unpack('p1u1s6u7u9', packed) 140 | self.assertEqual(unpacked, (0, -2, 65, 22)) 141 | 142 | packed = b'\x3e\x82\x16' 143 | unpacked = unpack('p1u1s6u7u9', packed) 144 | self.assertEqual(unpacked, (0, -2, 65, 22)) 145 | 146 | packed = b'\x3e\x82\x16' 147 | unpacked = unpack('p1u1s6p7u9', packed) 148 | self.assertEqual(unpacked, (0, -2, 22)) 149 | 150 | packed = b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0' 151 | unpacked = unpack('u1s6f32r43', packed) 152 | self.assertEqual(unpacked, (0, -2, 3.75, b'\x00\xff\x00\xff\x00\xe0')) 153 | 154 | packed = bytearray(b'\x80') 155 | unpacked = unpack('b1', packed) 156 | self.assertEqual(unpacked, (True, )) 157 | 158 | packed = b'\x80' 159 | unpacked = unpack('b1p6b1', packed) 160 | self.assertEqual(unpacked, (True, False)) 161 | 162 | packed = b'\x06' 163 | unpacked = unpack('u5b2u1', packed) 164 | self.assertEqual(unpacked, (0, True, 0)) 165 | 166 | packed = b'\x04' 167 | unpacked = unpack('u5b2u1', packed) 168 | self.assertEqual(unpacked, (0, True, 0)) 169 | 170 | packed = b'$4\x90\x80' 171 | unpacked = unpack('b1t24', packed) 172 | self.assertEqual(unpacked, (False, u'Hi!')) 173 | 174 | packed = 1000 * b'7' 175 | unpacked = unpack('t8000', packed) 176 | self.assertEqual(packed, 1000 * b'\x37') 177 | 178 | # Bad float size. 179 | with self.assertRaises(Error) as cm: 180 | unpack('f33', b'\x00\x00\x00\x00\x00') 181 | 182 | self.assertEqual(str(cm.exception), 183 | 'expected float size of 16, 32, or 64 bits (got 33)') 184 | 185 | # Too many bits to unpack. 186 | with self.assertRaises(Error) as cm: 187 | unpack('u9', b'\x00') 188 | 189 | self.assertEqual(str(cm.exception), 190 | 'unpack requires at least 9 bits to unpack (got 8)') 191 | 192 | # gcc packed struct with bitfields 193 | # 194 | # struct foo_t { 195 | # int a; 196 | # char b; 197 | # uint32_t c : 7; 198 | # uint32_t d : 25; 199 | # } foo; 200 | # 201 | # foo.a = 1; 202 | # foo.b = 1; 203 | # foo.c = 0x67; 204 | # foo.d = 0x12345; 205 | unpacked = unpack('s32s8u25u7', 206 | byteswap('414', 207 | b'\x01\x00\x00\x00\x01\xe7\xa2\x91\x00')) 208 | self.assertEqual(unpacked, (1, 1, 0x12345, 0x67)) 209 | 210 | def test_pack_unpack(self): 211 | """Pack and unpack values. 212 | 213 | """ 214 | 215 | packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22) 216 | unpacked = unpack('u1u1s6u7u9', packed) 217 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 218 | 219 | packed = pack('f64', 1.0) 220 | unpacked = unpack('f64', packed) 221 | self.assertEqual(unpacked, (1.0, )) 222 | 223 | if sys.version_info >= (3, 6): 224 | packed = pack('f16', 1.0) 225 | unpacked = unpack('f16', packed) 226 | self.assertEqual(unpacked, (1.0, )) 227 | 228 | def test_calcsize(self): 229 | """Calculate size. 230 | 231 | """ 232 | 233 | size = calcsize('u1u1s6u7u9') 234 | self.assertEqual(size, 24) 235 | 236 | size = calcsize('u1') 237 | self.assertEqual(size, 1) 238 | 239 | size = calcsize('u77') 240 | self.assertEqual(size, 77) 241 | 242 | size = calcsize('u1s6u7u9') 243 | self.assertEqual(size, 23) 244 | 245 | size = calcsize('b1s6u7u9p1t8') 246 | self.assertEqual(size, 32) 247 | 248 | size = calcsize('b1s6u7u9P1t8') 249 | self.assertEqual(size, 32) 250 | 251 | def test_byteswap(self): 252 | """Byte swap. 253 | 254 | """ 255 | 256 | res = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a' 257 | ref = b'\x01\x03\x02\x04\x08\x07\x06\x05\x0a\x09' 258 | self.assertEqual(byteswap('12142', ref), res) 259 | 260 | packed = pack('u1u5u2u16', 1, 2, 3, 4) 261 | unpacked = unpack('u1u5u2u16', byteswap('12', packed)) 262 | self.assertEqual(unpacked, (1, 2, 3, 1024)) 263 | 264 | def test_endianness(self): 265 | """Test pack/unpack with endianness information in the format string. 266 | 267 | """ 268 | 269 | # Big endian. 270 | ref = b'\x02\x46\x9a\xfe\x00\x00\x00' 271 | packed = pack('>u19s3f32', 0x1234, -2, -1.0) 272 | self.assertEqual(packed, ref) 273 | unpacked = unpack('>u19s3f32', packed) 274 | self.assertEqual(unpacked, (0x1234, -2, -1.0)) 275 | 276 | # Little endian. 277 | ref = b'\x2c\x48\x0c\x00\x00\x07\xf4' 278 | packed = pack('u19f64r3p4', 1, -2, 1.0, b'\x80') 286 | self.assertEqual(packed, ref) 287 | unpacked = unpack('>u19f64r3p4', packed) 288 | self.assertEqual(unpacked, (1, -2, 1.0, b'\x80')) 289 | 290 | # Opposite endianness of the 'mixed endianness' test. 291 | ref = b'\x80\x00\x1e\x00\x00\x00\x00\x00\x00\x0f\xfc\x20' 292 | packed = pack('s5s5', 0x1234, -2, -1.0) 312 | self.assertEqual(packed, ref) 313 | unpacked = unpack('u19s3f32>', packed) 314 | self.assertEqual(unpacked, (0x1234, -2, -1.0)) 315 | 316 | # Least significant byte first. 317 | ref = b'\x34\x12\x18\x00\x00\xe0\xbc' 318 | packed = pack('u19s3f32<', 0x1234, -2, -1.0) 319 | self.assertEqual(packed, ref) 320 | unpacked = unpack('u19s3f32<', packed) 321 | self.assertEqual(unpacked, (0x1234, -2, -1.0)) 322 | 323 | # Least significant byte first. 324 | ref = b'\x34\x12' 325 | packed = pack('u8s8<', 0x34, 0x12) 326 | self.assertEqual(packed, ref) 327 | unpacked = unpack('u8s8<', packed) 328 | self.assertEqual(unpacked, (0x34, 0x12)) 329 | 330 | # Least significant byte first. 331 | ref = b'\x34\x22' 332 | packed = pack('u3u12<', 1, 0x234) 333 | self.assertEqual(packed, ref) 334 | unpacked = unpack('u3s12<', packed) 335 | self.assertEqual(unpacked, (1, 0x234)) 336 | 337 | # Least significant byte first. 338 | ref = b'\x34\x11\x00' 339 | packed = pack('u3u17<', 1, 0x234) 340 | self.assertEqual(packed, ref) 341 | unpacked = unpack('u3s17<', packed) 342 | self.assertEqual(unpacked, (1, 0x234)) 343 | 344 | # Least significant byte first. 345 | ref = b'\x80' 346 | packed = pack('u1<', 1) 347 | self.assertEqual(packed, ref) 348 | unpacked = unpack('u1<', packed) 349 | self.assertEqual(unpacked, (1, )) 350 | 351 | # Least significant byte first. 352 | ref = b'\x45\x23\x25\x82' 353 | packed = pack('u19u5u1u7<', 0x12345, 5, 1, 2) 354 | self.assertEqual(packed, ref) 355 | unpacked = unpack('u19u5u1u7<', packed) 356 | self.assertEqual(unpacked, (0x12345, 5, 1, 2)) 357 | 358 | # Least significant byte first does not affect raw and text. 359 | ref = b'123abc' 360 | packed = pack('r24t24<', b'123', 'abc') 361 | self.assertEqual(packed, ref) 362 | unpacked = unpack('r24t24<', packed) 363 | self.assertEqual(unpacked, (b'123', 'abc')) 364 | 365 | def test_performance_mixed_types(self): 366 | """Test pack/unpack performance with mixed types. 367 | 368 | """ 369 | 370 | print() 371 | 372 | time = timeit.timeit("pack('s6u7r40b1t152', " 373 | "-2, 22, b'\x01\x01\x03\x04\x05', " 374 | "True, u'foo fie bar gom gum')", 375 | setup="from bitstruct import pack", 376 | number=50000) 377 | print("pack time: {} s ({} s/pack)".format(time, time / 50000)) 378 | 379 | time = timeit.timeit( 380 | "fmt.pack(-2, 22, b'\x01\x01\x03\x04\x05', " 381 | "True, u'foo fie bar gom gum')", 382 | setup="import bitstruct ; fmt = bitstruct.compile('s6u7r40b1t152')", 383 | number=50000) 384 | print("pack time compiled: {} s ({} s/pack)".format(time, time / 50000)) 385 | 386 | time = timeit.timeit("unpack('s6u7r40b1t152', " 387 | "b'\\xf8\\xb0\\x08\\x08\\x18 " 388 | "-\\x99\\xbd\\xbc\\x81\\x99" 389 | "\\xa5\\x94\\x81\\x89\\x85" 390 | "\\xc8\\x81\\x9d\\xbd\\xb4" 391 | "\\x81\\x9d\\xd5\\xb4')", 392 | setup="from bitstruct import unpack", 393 | number=50000) 394 | print("unpack time: {} s ({} s/unpack)".format(time, time / 50000)) 395 | 396 | time = timeit.timeit( 397 | "fmt.unpack(b'\\xf8\\xb0\\x08\\x08\\x18 " 398 | "-\\x99\\xbd\\xbc\\x81\\x99" 399 | "\\xa5\\x94\\x81\\x89\\x85" 400 | "\\xc8\\x81\\x9d\\xbd\\xb4" 401 | "\\x81\\x9d\\xd5\\xb4')", 402 | setup="import bitstruct ; fmt = bitstruct.compile('s6u7r40b1t152')", 403 | number=50000) 404 | print("unpack time compiled: {} s ({} s/unpack)".format(time, time / 50000)) 405 | 406 | def test_performance_integers(self): 407 | """Test pack/unpack performance with integers. 408 | 409 | """ 410 | 411 | print() 412 | 413 | time = timeit.timeit("pack('s13u7u35u1s9', " 414 | "-2, 22, 44567233, 0, 33)", 415 | setup="from bitstruct import pack", 416 | number=50000) 417 | print("pack time: {} s ({} s/pack)".format(time, time / 50000)) 418 | 419 | time = timeit.timeit( 420 | "fmt.pack(-2, 22, 44567233, 0, 33)", 421 | setup="import bitstruct ; fmt = bitstruct.compile('s13u7u35u1s9')", 422 | number=50000) 423 | print("pack time compiled: {} s ({} s/pack)".format(time, time / 50000)) 424 | 425 | time = timeit.timeit("unpack('s13u7u35u1s9', " 426 | "b'\\xff\\xf1`\\x05P\\x15\\x82\\x10\\x80')", 427 | setup="from bitstruct import unpack", 428 | number=50000) 429 | print("unpack time: {} s ({} s/unpack)".format(time, time / 50000)) 430 | 431 | time = timeit.timeit( 432 | "fmt.unpack(b'\\xff\\xf1`\\x05P\\x15\\x82\\x10\\x80')", 433 | setup="import bitstruct ; fmt = bitstruct.compile('s13u7u35u1s9')", 434 | number=50000) 435 | print("unpack time compiled: {} s ({} s/unpack)".format(time, time / 50000)) 436 | 437 | def test_compile(self): 438 | cf = bitstruct.compile('u1u1s6u7u9') 439 | 440 | packed = cf.pack(0, 0, -2, 65, 22) 441 | self.assertEqual(packed, b'\x3e\x82\x16') 442 | 443 | unpacked = cf.unpack(b'\x3e\x82\x16') 444 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 445 | 446 | def test_signed_integer(self): 447 | """Pack and unpack signed integer values. 448 | 449 | """ 450 | 451 | datas = [ 452 | ('s2', 0x01, b'\x40'), 453 | ('s3', 0x03, b'\x60'), 454 | ('s4', 0x07, b'\x70'), 455 | ('s5', 0x0f, b'\x78'), 456 | ('s6', 0x1f, b'\x7c'), 457 | ('s7', 0x3f, b'\x7e'), 458 | ('s8', 0x7f, b'\x7f'), 459 | ('s9', 0xff, b'\x7f\x80'), 460 | ('s1', -1, b'\x80'), 461 | ('s2', -1, b'\xc0') 462 | ] 463 | 464 | for fmt, value, packed in datas: 465 | self.assertEqual(pack(fmt, value), packed) 466 | self.assertEqual(unpack(fmt, packed), (value, )) 467 | 468 | def test_unsigned_integer(self): 469 | """Pack and unpack unsigned integer values. 470 | 471 | """ 472 | 473 | datas = [ 474 | ('u1', 0x001, b'\x80'), 475 | ('u2', 0x003, b'\xc0'), 476 | ('u3', 0x007, b'\xe0'), 477 | ('u4', 0x00f, b'\xf0'), 478 | ('u5', 0x01f, b'\xf8'), 479 | ('u6', 0x03f, b'\xfc'), 480 | ('u7', 0x07f, b'\xfe'), 481 | ('u8', 0x0ff, b'\xff'), 482 | ('u9', 0x1ff, b'\xff\x80') 483 | ] 484 | 485 | for fmt, value, packed in datas: 486 | self.assertEqual(pack(fmt, value), packed) 487 | self.assertEqual(unpack(fmt, packed), (value, )) 488 | 489 | def test_bad_float_size(self): 490 | """Test of bad float size. 491 | 492 | """ 493 | 494 | with self.assertRaises(Error) as cm: 495 | pack('f31', 1.0) 496 | 497 | self.assertEqual(str(cm.exception), 498 | 'expected float size of 16, 32, or 64 bits (got 31)') 499 | 500 | with self.assertRaises(Error) as cm: 501 | unpack('f33', 8 * b'\x00') 502 | 503 | self.assertEqual(str(cm.exception), 504 | 'expected float size of 16, 32, or 64 bits (got 33)') 505 | 506 | def test_bad_format(self): 507 | """Test of bad format. 508 | 509 | """ 510 | 511 | formats = [ 512 | ('g1', "bad char 'g' in format"), 513 | ('s1u1f32b1t8r8G13', "bad char 'G' in format"), 514 | ('s1u1f32b1t8r8G13S3', "bad char 'G' in format"), 515 | ('s', "bad format 's'"), 516 | ('1', "bad format '1'"), 517 | ('ss1', "bad format 'ss1'"), 518 | ('1s', "bad format '1s'"), 519 | ('foo', "bad format 'foo'"), 520 | ('s>1>', "bad format 's>1>'"), 521 | ('s0', "bad format 's0'") 522 | ] 523 | 524 | for fmt, expected_error in formats: 525 | with self.assertRaises(Error) as cm: 526 | bitstruct.compile(fmt) 527 | 528 | self.assertEqual(str(cm.exception), expected_error) 529 | 530 | def test_empty_format(self): 531 | """Test of empty format type. 532 | 533 | """ 534 | 535 | cf = bitstruct.compile('') 536 | 537 | self.assertEqual(cf.pack(), b'') 538 | self.assertEqual(cf.pack(1), b'') 539 | 540 | self.assertEqual(cf.unpack(b''), ()) 541 | self.assertEqual(cf.unpack(b'\x00'), ()) 542 | 543 | def test_byte_order_format(self): 544 | """Test of a format with only byte order information. 545 | 546 | """ 547 | 548 | cf = bitstruct.compile('>') 549 | 550 | self.assertEqual(cf.pack(), b'') 551 | self.assertEqual(cf.pack(1), b'') 552 | 553 | self.assertEqual(cf.unpack(b''), ()) 554 | self.assertEqual(cf.unpack(b'\x00'), ()) 555 | 556 | def test_pack_into(self): 557 | """Pack values into a buffer. 558 | 559 | """ 560 | 561 | packed = bytearray(3) 562 | pack_into('u1u1s6u7u9', packed, 0, 0, 0, -2, 65, 22) 563 | self.assertEqual(packed, b'\x3e\x82\x16') 564 | 565 | datas = [ 566 | (0, b'\x80\x00'), 567 | (1, b'\x40\x00'), 568 | (7, b'\x01\x00'), 569 | (15, b'\x00\x01') 570 | ] 571 | 572 | for offset, expected in datas: 573 | packed = bytearray(2) 574 | pack_into('u1', packed, offset, 1) 575 | self.assertEqual(packed, expected) 576 | 577 | packed = bytearray(b'\xff\xff\xff') 578 | pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=False) 579 | self.assertEqual(packed, b'\xf1\xf2\xf3') 580 | 581 | packed = bytearray(b'\xff\xff\xff') 582 | pack_into('p4u4p4u4p4u4', packed, 0, 1, 2, 3, fill_padding=True) 583 | self.assertEqual(packed, b'\x01\x02\x03') 584 | 585 | packed = bytearray(2) 586 | 587 | with self.assertRaises(Error) as cm: 588 | pack_into('u17', packed, 0, 1) 589 | 590 | self.assertEqual(str(cm.exception), 591 | 'pack requires a buffer of at least 17 bits') 592 | 593 | packed = bytearray(b'\x00') 594 | pack_into('P4u4', packed, 0, 1) 595 | self.assertEqual(packed, b'\xf1') 596 | 597 | # Too many values to pack. 598 | with self.assertRaises(Error) as cm: 599 | packed = bytearray(b'\x00') 600 | pack_into('b1t24', packed, 0, False) 601 | 602 | self.assertEqual(str(cm.exception), 603 | 'pack expected 2 item(s) for packing (got 1)') 604 | 605 | def test_unpack_from(self): 606 | """Unpack values at given bit offset. 607 | 608 | """ 609 | 610 | unpacked = unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b\x00', 1) 611 | self.assertEqual(unpacked, (0, 0, -2, 65, 22)) 612 | 613 | with self.assertRaises(Error) as cm: 614 | unpack_from('u1u1s6u7u9', b'\x1f\x41\x0b', 1) 615 | 616 | self.assertEqual(str(cm.exception), 617 | 'unpack requires at least 24 bits to unpack ' 618 | '(got 23)') 619 | 620 | def test_pack_integers_value_checks(self): 621 | """Pack integer values range checks. 622 | 623 | """ 624 | 625 | # Formats with minimum and maximum allowed values. 626 | datas = [ 627 | ('s1', -1, 0), 628 | ('s2', -2, 1), 629 | ('s3', -4, 3), 630 | ('u1', 0, 1), 631 | ('u2', 0, 3), 632 | ('u3', 0, 7) 633 | ] 634 | 635 | for fmt, minimum, maximum in datas: 636 | # No exception should be raised for numbers in range. 637 | pack(fmt, minimum) 638 | pack(fmt, maximum) 639 | 640 | # Numbers out of range. 641 | for number in [minimum - 1, maximum + 1]: 642 | with self.assertRaises(Error) as cm: 643 | pack(fmt, number) 644 | 645 | self.assertEqual( 646 | str(cm.exception), 647 | '"{}" requires {} <= integer <= {} (got {})'.format( 648 | fmt, 649 | minimum, 650 | maximum, 651 | number)) 652 | 653 | def test_pack_unpack_raw(self): 654 | """Pack and unpack raw values. 655 | 656 | """ 657 | 658 | packed = pack('r24', b'') 659 | self.assertEqual(packed, b'\x00\x00\x00') 660 | packed = pack('r24', b'12') 661 | self.assertEqual(packed, b'12\x00') 662 | packed = pack('r24', b'123') 663 | self.assertEqual(packed, b'123') 664 | packed = pack('r24', b'1234') 665 | self.assertEqual(packed, b'123') 666 | 667 | unpacked = unpack('r24', b'\x00\x00\x00')[0] 668 | self.assertEqual(unpacked, b'\x00\x00\x00') 669 | unpacked = unpack('r24', b'12\x00')[0] 670 | self.assertEqual(unpacked, b'12\x00') 671 | unpacked = unpack('r24', b'123')[0] 672 | self.assertEqual(unpacked, b'123') 673 | unpacked = unpack('r24', b'1234')[0] 674 | self.assertEqual(unpacked, b'123') 675 | 676 | def test_pack_unpack_text(self): 677 | """Pack and unpack text values. 678 | 679 | """ 680 | 681 | packed = pack('t24', '') 682 | self.assertEqual(packed, b'\x00\x00\x00') 683 | packed = pack('t24', '12') 684 | self.assertEqual(packed, b'12\x00') 685 | packed = pack('t24', '123') 686 | self.assertEqual(packed, b'123') 687 | packed = pack('t24', '1234') 688 | self.assertEqual(packed, b'123') 689 | 690 | unpacked = unpack('t24', b'\x00\x00\x00')[0] 691 | self.assertEqual(unpacked, '\x00\x00\x00') 692 | unpacked = unpack('t24', b'12\x00')[0] 693 | self.assertEqual(unpacked, '12\x00') 694 | unpacked = unpack('t24', b'123')[0] 695 | self.assertEqual(unpacked, '123') 696 | unpacked = unpack('t24', b'1234')[0] 697 | self.assertEqual(unpacked, '123') 698 | 699 | def test_pack_unpack_dict(self): 700 | unpacked = { 701 | 'foo': 0, 702 | 'bar': 0, 703 | 'fie': -2, 704 | 'fum': 65, 705 | 'fam': 22 706 | } 707 | packed = b'\x3e\x82\x16' 708 | fmt = 'u1u1s6u7u9' 709 | names = ['foo', 'bar', 'fie', 'fum', 'fam'] 710 | 711 | self.assertEqual(pack_dict(fmt, names, unpacked), packed) 712 | self.assertEqual(unpack_dict(fmt, names, packed), unpacked) 713 | 714 | def test_pack_into_unpack_from_dict(self): 715 | unpacked = { 716 | 'foo': 0, 717 | 'bar': 0, 718 | 'fie': -2, 719 | 'fum': 65, 720 | 'fam': 22 721 | } 722 | packed = b'\x3e\x82\x16' 723 | fmt = 'u1u1s6u7u9' 724 | names = ['foo', 'bar', 'fie', 'fum', 'fam'] 725 | 726 | actual = bytearray(3) 727 | pack_into_dict(fmt, names, actual, 0, unpacked) 728 | self.assertEqual(actual, packed) 729 | self.assertEqual(unpack_from_dict(fmt, names, packed), unpacked) 730 | 731 | def test_pack_dict_missing_key(self): 732 | unpacked = { 733 | 'foo': 0, 734 | 'bar': 0, 735 | 'fie': -2, 736 | 'fum': 65 737 | } 738 | fmt = 'u1u1s6u7u9' 739 | names = ['foo', 'bar', 'fie', 'fum', 'fam'] 740 | 741 | with self.assertRaises(Error) as cm: 742 | pack_dict(fmt, names, unpacked) 743 | 744 | self.assertEqual(str(cm.exception), 745 | "'fam' not found in data dictionary") 746 | 747 | with self.assertRaises(Error) as cm: 748 | data = bytearray(3) 749 | pack_into_dict(fmt, names, data, 0, unpacked) 750 | 751 | self.assertEqual(str(cm.exception), 752 | "'fam' not found in data dictionary") 753 | 754 | def test_compile_pack_unpack_formats(self): 755 | fmts = [ 756 | ('u1s2p3', None, (1, -1)), 757 | ('u1 s2 p3', None, (1, -1)), 758 | ('u1s2p3', ['a', 'b'], {'a': 1, 'b': -1}) 759 | ] 760 | 761 | for fmt, names, decoded in fmts: 762 | if names is None: 763 | cf = bitstruct.compile(fmt) 764 | packed_1 = cf.pack(*decoded) 765 | packed_2 = pack(fmt, *decoded) 766 | else: 767 | cf = bitstruct.compile(fmt, names) 768 | packed_1 = cf.pack(decoded) 769 | packed_2 = pack_dict(fmt, names, decoded) 770 | 771 | self.assertEqual(packed_1, b'\xe0') 772 | self.assertEqual(packed_2, b'\xe0') 773 | 774 | def test_compile_formats(self): 775 | bitstruct.compile('p1u1') 776 | bitstruct.compile('p1u1', ['a']) 777 | 778 | 779 | if __name__ == '__main__': 780 | unittest.main() 781 | -------------------------------------------------------------------------------- /cbitstruct/_cbitstruct.c: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 | 5 | #define PY_SSIZE_T_CLEAN 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define BIT_MASK(__TYPE__, __ONE_COUNT__) \ 14 | (((__TYPE__)(-((__ONE_COUNT__) != 0))) \ 15 | & (((__TYPE__)-1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))) 16 | 17 | #define BIT_SET(__TYPE__, __ONE_POS__) ((__TYPE__)(1) << (__ONE_POS__)) 18 | 19 | #define BAD_FORMAT ('\0') 20 | #define FILL_PADDING ("fill_padding") 21 | #define SMALL_FORMAT_OPTIMIZATION (16) 22 | 23 | // #define PRINT_DEBUG 24 | #ifdef PRINT_DEBUG 25 | #define DEBUG(...) (printf(__VA_ARGS__)) 26 | #else 27 | #define DEBUG(...) 28 | #endif 29 | 30 | typedef struct { 31 | int bits; 32 | char type; 33 | bool msb_first; 34 | } Desc; 35 | 36 | typedef struct { 37 | Desc* descs; 38 | int ndescs; 39 | int npadding; 40 | int nbits; 41 | bool be; 42 | bool valid; 43 | } CompiledFormat; 44 | 45 | typedef union { 46 | uint8_t raw[8]; 47 | uint64_t uint64; 48 | int64_t int64; 49 | float fp32; 50 | double fp64; 51 | } ParsedElement; 52 | 53 | static int fast_strtoi(const char* str, const char** end) 54 | { 55 | int val = 0; 56 | 57 | while (*str && *str >= '0' && *str <= '9') { 58 | val = val * 10 + (*str++ - '0'); 59 | } 60 | 61 | *end = str; 62 | return val; 63 | } 64 | 65 | static const char* c_find_next(const char* fmt) 66 | { 67 | while (*fmt == ' ') { 68 | ++fmt; 69 | } 70 | return fmt; 71 | } 72 | 73 | static Desc c_parse_one(const char* fmt, const char** end, Desc* previous) 74 | { 75 | Desc desc; 76 | if (*fmt == '<') { 77 | desc.msb_first = false; 78 | ++fmt; 79 | } 80 | else if (*fmt == '>') { 81 | desc.msb_first = true; 82 | ++fmt; 83 | } 84 | else if (previous) { 85 | desc.msb_first = previous->msb_first; 86 | } 87 | else { 88 | desc.msb_first = true; 89 | } 90 | desc.type = *fmt++; 91 | desc.bits = fast_strtoi(fmt, end); 92 | 93 | switch (desc.type) { 94 | case 'u': 95 | case 's': 96 | case 'b': 97 | case 't': 98 | case 'r': 99 | if (desc.bits < 1 || desc.bits > 64) { 100 | // bits > 64 is only supported for padding 101 | desc.type = BAD_FORMAT; 102 | } 103 | break; 104 | case 'p': 105 | case 'P': 106 | break; 107 | case 'f': 108 | if (desc.bits != 16 && desc.bits != 32 && desc.bits != 64) { 109 | desc.type = BAD_FORMAT; 110 | } 111 | break; 112 | default: 113 | desc.type = BAD_FORMAT; 114 | break; 115 | } 116 | return desc; 117 | } 118 | 119 | static int c_count_elements(const char* fmt) 120 | { 121 | int nr = 0; 122 | while (*fmt != '\0') { 123 | if (*fmt >= 'A' && *fmt <= 'z') { 124 | // Assume this is an element 125 | ++nr; 126 | } 127 | ++fmt; 128 | } 129 | return nr; 130 | } 131 | 132 | static CompiledFormat c_compile_format(const char* fmt) 133 | { 134 | CompiledFormat compiled = {NULL, 0, 0, 0, true, true}; 135 | 136 | const int n = c_count_elements(fmt); 137 | if (n <= 0 && *fmt != '>' && *fmt != '<' && *fmt != '\0') { 138 | compiled.valid = false; 139 | goto invalid; 140 | } 141 | 142 | compiled.descs = PyMem_RawMalloc(n * sizeof(Desc)); 143 | if (!compiled.descs) { 144 | PyErr_NoMemory(); 145 | goto invalid; 146 | } 147 | compiled.ndescs = n; 148 | 149 | for (int i = 0; i < n; ++i) { 150 | fmt = c_find_next(fmt); 151 | const char* end; 152 | compiled.descs[i] = 153 | c_parse_one(fmt, &end, i > 0 ? &compiled.descs[i - 1] : NULL); 154 | if (compiled.descs[i].type == BAD_FORMAT) { 155 | goto invalid; 156 | } 157 | if (compiled.descs[i].type == 'p' || compiled.descs[i].type == 'P') { 158 | compiled.npadding++; 159 | } 160 | compiled.nbits += compiled.descs[i].bits; 161 | fmt = end; 162 | } 163 | 164 | fmt = c_find_next(fmt); 165 | if (*fmt == '<') { 166 | compiled.be = false; 167 | } 168 | 169 | return compiled; 170 | 171 | invalid: 172 | compiled.valid = false; 173 | return compiled; 174 | } 175 | 176 | static void c_release_compiled_format(CompiledFormat* compiled) 177 | { 178 | if (compiled->descs) { 179 | PyMem_RawFree(compiled->descs); 180 | compiled->descs = NULL; 181 | compiled->ndescs = 0; 182 | } 183 | } 184 | 185 | static uint64_t c_bitswitch(uint64_t data, int nbits) 186 | { 187 | uint64_t out = 0; 188 | for (int i = 0; i < nbits; ++i) { 189 | int bit = data & 1; 190 | out <<= 1; 191 | out |= bit; 192 | data >>= 1; 193 | } 194 | return out; 195 | } 196 | 197 | static uint64_t c_partial_c_byteswitch(uint64_t data, int nbits, int bits_in_lsb) 198 | { 199 | uint64_t out = 0; 200 | int bits = bits_in_lsb; 201 | 202 | while (nbits > 0) { 203 | int byte = data & BIT_MASK(uint8_t, bits); 204 | out <<= bits; 205 | out |= byte; 206 | data >>= bits; 207 | nbits -= bits; 208 | bits = nbits > 8 ? 8 : nbits; 209 | } 210 | 211 | return out; 212 | } 213 | 214 | static void c_byteswitch(uint8_t* data, int nbytes) 215 | { 216 | uint8_t* end = data + nbytes - 1; 217 | for (int i = 0; i < nbytes / 2; ++i) { 218 | uint8_t tmp = end[-i]; 219 | end[-i] = data[i]; 220 | data[i] = tmp; 221 | } 222 | } 223 | 224 | static void c_bitset(uint8_t* dst, int dst_bit_offset, int nbits) 225 | { 226 | dst += dst_bit_offset / 8; 227 | dst_bit_offset %= 8; 228 | int first_byte_bits = 8 - dst_bit_offset; 229 | int n_full_bytes = (nbits - first_byte_bits) / 8; 230 | int last_byte_bits = (nbits - first_byte_bits) % 8; 231 | 232 | uint8_t first_byte = BIT_MASK(uint8_t, first_byte_bits); 233 | if (last_byte_bits < 0) { 234 | // when the elements fits on a single byte and does not 235 | // fill all bits, "-last_byte_bits" is the right padding 236 | first_byte &= ~BIT_MASK(uint8_t, -last_byte_bits); 237 | } 238 | *dst++ |= first_byte; 239 | 240 | for (int n = 0; n < n_full_bytes; ++n) { 241 | *dst++ = 0xff; 242 | } 243 | 244 | if (last_byte_bits > 0) { 245 | uint8_t last_byte = BIT_MASK(uint8_t, last_byte_bits); 246 | last_byte <<= 8 - last_byte_bits; 247 | *dst |= last_byte; 248 | } 249 | } 250 | 251 | static void c_bitcpy(uint8_t* dst, int dst_bit_offset, uint64_t data, int nbits) 252 | { 253 | dst += dst_bit_offset / 8; 254 | dst_bit_offset %= 8; 255 | int first_byte_bits = 8 - dst_bit_offset; 256 | int n_full_bytes = (nbits - first_byte_bits) / 8; 257 | int last_byte_bits = (nbits - first_byte_bits) % 8; 258 | 259 | dst += n_full_bytes + (last_byte_bits > 0 ? 1 : 0); 260 | 261 | if (last_byte_bits > 0) { 262 | uint8_t last_byte_mask = BIT_MASK(uint8_t, last_byte_bits); 263 | uint8_t last_byte = data & last_byte_mask; 264 | 265 | last_byte <<= 8 - last_byte_bits; 266 | last_byte_mask <<= 8 - last_byte_bits; 267 | *dst &= ~last_byte_mask; 268 | *dst |= last_byte & last_byte_mask; 269 | data >>= last_byte_bits; 270 | --dst; 271 | } 272 | 273 | for (int n = 0; n < n_full_bytes; ++n) { 274 | uint8_t byte = data & 0xff; 275 | *dst-- = byte; 276 | data >>= 8; 277 | } 278 | 279 | uint8_t first_byte_mask = BIT_MASK(uint8_t, first_byte_bits); 280 | uint8_t first_byte = data & first_byte_mask; 281 | 282 | if (last_byte_bits < 0) { 283 | // when the elements fits on a single byte and does not 284 | // fill all bits, "-last_byte_bits" is the right padding 285 | first_byte_mask &= ~BIT_MASK(uint8_t, -last_byte_bits); 286 | first_byte <<= -last_byte_bits; 287 | } 288 | 289 | *dst &= ~first_byte_mask; 290 | *dst |= first_byte & first_byte_mask; 291 | } 292 | 293 | static void c_pack( 294 | uint8_t* out, 295 | const ParsedElement* elements, 296 | CompiledFormat fmt, 297 | int offset, 298 | bool fill_padding) 299 | { 300 | int bit = offset; 301 | bool wrong_endianness = fmt.be ^ PY_LITTLE_ENDIAN; 302 | 303 | for (int n = 0; n < fmt.ndescs; ++n) { 304 | const ParsedElement* el = elements + n; 305 | const Desc* desc = fmt.descs + n; 306 | const int bitoff = bit; 307 | uint64_t data = el->uint64; 308 | bit += desc->bits; 309 | 310 | if (desc->type == 'p') { 311 | if (fill_padding) { 312 | c_bitcpy(out, bitoff, 0, desc->bits); 313 | } 314 | continue; 315 | } 316 | else if (desc->type == 'P') { 317 | if (fill_padding) { 318 | c_bitset(out, bitoff, desc->bits); 319 | } 320 | continue; 321 | } 322 | 323 | if (desc->type == 'r' || desc->type == 't') { 324 | int nbytes = (desc->bits + 7) / 8; 325 | int padding = nbytes * 8 - desc->bits; 326 | #if PY_LITTLE_ENDIAN 327 | assert(nbytes <= (int)sizeof(data)); 328 | c_byteswitch((uint8_t*)&data, nbytes); 329 | #endif 330 | data >>= padding; 331 | } 332 | 333 | // Switch bits if necessary 334 | if (!desc->msb_first) { 335 | data = c_bitswitch(data, desc->bits); 336 | } 337 | 338 | // Correct source data endianness, except for raw and text 339 | // which are special cases, handled later. 340 | if (desc->type != 'r' && desc->type != 't' && wrong_endianness) { 341 | // bs is the first byte 342 | int bits_in_lsb = 8 - (bitoff % 8); 343 | data = c_partial_c_byteswitch(data, desc->bits, bits_in_lsb); 344 | } 345 | 346 | c_bitcpy(out, bitoff, data, desc->bits); 347 | } 348 | } 349 | 350 | static void c_unpack( 351 | ParsedElement* out, 352 | const uint8_t* bytes, 353 | CompiledFormat fmt, 354 | int offset) 355 | { 356 | int bit = offset; 357 | bool wrong_endianness = fmt.be ^ PY_LITTLE_ENDIAN; 358 | 359 | for (int n = 0; n < fmt.ndescs; ++n) { 360 | const Desc* desc = fmt.descs + n; 361 | 362 | const uint8_t* src = bytes + bit / 8; 363 | int first_byte_bits = 8 - (bit % 8); 364 | int n_full_bytes = (desc->bits - first_byte_bits) / 8; 365 | int last_byte_bits = (desc->bits - first_byte_bits) % 8; 366 | bit += desc->bits; 367 | 368 | if (desc->type == 'p' || desc->type == 'P') { 369 | // padding, ignore 370 | continue; 371 | } 372 | 373 | uint64_t data = 0; 374 | { 375 | uint8_t byte = *src++ & BIT_MASK(uint8_t, first_byte_bits); 376 | data |= byte; 377 | } 378 | 379 | for (int i = 0; i < n_full_bytes; ++i) { 380 | data <<= 8; 381 | data |= *src++; 382 | } 383 | 384 | if (last_byte_bits > 0) { 385 | uint8_t byte = *src; 386 | byte >>= 8 - last_byte_bits; 387 | data <<= last_byte_bits; 388 | data |= byte; 389 | } 390 | else { 391 | data >>= -last_byte_bits; 392 | } 393 | 394 | // Correct source data endianness, except for raw and text 395 | // which are special cases, handled later. 396 | if (desc->type != 'r' && desc->type != 't' && wrong_endianness) { 397 | // lsb is the "last_byte", the lsb of the host system 398 | // +16 because last_byte_bits can go down to -7, and -(offset%8) as well 399 | int bits_in_lsb = (last_byte_bits + 16 - (offset % 8)) % 8; 400 | data = c_partial_c_byteswitch(data, desc->bits, bits_in_lsb); 401 | } 402 | 403 | // Switch bits if necessary 404 | if (!desc->msb_first) { 405 | data = c_bitswitch(data, desc->bits); 406 | } 407 | 408 | // For raw and text, add right padding (as bitstruct) and 409 | // correct endianness. These types are not influenced 410 | // by the source data endianness, only the host system 411 | if (desc->type == 'r' || desc->type == 't') { 412 | int nbytes = (desc->bits + 7) / 8; 413 | int padding = nbytes * 8 - desc->bits; 414 | data <<= padding; 415 | #if PY_LITTLE_ENDIAN 416 | assert(nbytes <= (int)sizeof(data)); 417 | c_byteswitch((uint8_t*)&data, nbytes); 418 | #endif 419 | } 420 | 421 | if (desc->type == 's' && desc->bits < 64) { 422 | uint64_t sign_bit = BIT_SET(uint64_t, desc->bits - 1); 423 | if (data & sign_bit) { 424 | // two's complement: replace high bits by ones 425 | data |= BIT_MASK(uint64_t, 64) << desc->bits; 426 | } 427 | } 428 | 429 | out[n].uint64 = data; 430 | } 431 | } 432 | 433 | /* Python conversions */ 434 | 435 | static bool unsigned_in_range(uint64_t n, int bits) 436 | { 437 | if (bits == 64) { 438 | return true; 439 | } 440 | return n < (1ull << bits); 441 | } 442 | 443 | static bool signed_in_range(int64_t n, int bits) 444 | { 445 | if (bits == 64) { 446 | return true; 447 | } 448 | if (n > 0) { 449 | return n < (1ll << (bits - 1)); 450 | } 451 | else { 452 | return -n <= (1ll << (bits - 1)); 453 | } 454 | } 455 | 456 | static bool python_to_parsed_elements( 457 | ParsedElement* elements, 458 | PyObject** data, 459 | Py_ssize_t data_size, 460 | CompiledFormat fmt) 461 | { 462 | assert(data_size >= fmt.ndescs - fmt.npadding); 463 | 464 | int n = 0; 465 | for (int i = 0; i < fmt.ndescs; ++i) { 466 | Desc* desc = fmt.descs + i; 467 | ParsedElement* el = elements + i; 468 | // zero all bits 469 | el->uint64 = 0; 470 | 471 | if (desc->type == 'p' || desc->type == 'P') { 472 | continue; 473 | } 474 | 475 | PyObject* v = data[n++]; 476 | 477 | switch (desc->type) { 478 | case 'u': 479 | #if SIZEOF_LONG >= 8 480 | el->uint64 = PyLong_AsUnsignedLong(v); 481 | #else 482 | el->uint64 = PyLong_AsUnsignedLongLong(v); 483 | #endif // SIZEOF_LONG >= 8 484 | if (!unsigned_in_range(el->uint64, desc->bits)) { 485 | PyErr_SetString(PyExc_TypeError, "integer is out of range"); 486 | } 487 | break; 488 | case 's': 489 | #if SIZEOF_LONG >= 8 490 | el->int64 = PyLong_AsLong(v); 491 | #else 492 | el->int64 = PyLong_AsLongLong(v); 493 | #endif // SIZEOF_LONG >= 8 494 | if (!signed_in_range(el->int64, desc->bits)) { 495 | PyErr_SetString(PyExc_TypeError, "integer is out of range"); 496 | } 497 | break; 498 | case 'f': 499 | #if PY_VERSION_HEX >= 0x030B00A7 500 | if (desc->bits == 16) { 501 | double cv = PyFloat_AsDouble(v); 502 | PyFloat_Pack2(cv, (char*)el->raw, PY_LITTLE_ENDIAN); 503 | } 504 | else 505 | #elif PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 506 | if (desc->bits == 16) { 507 | double cv = PyFloat_AsDouble(v); 508 | _PyFloat_Pack2(cv, el->raw, PY_LITTLE_ENDIAN); 509 | } 510 | else 511 | #endif // PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 512 | if (desc->bits == 32) { 513 | el->fp32 = PyFloat_AsDouble(v); 514 | } 515 | else if (desc->bits == 64) { 516 | el->fp64 = PyFloat_AsDouble(v); 517 | } 518 | break; 519 | case 'b': 520 | el->uint64 = PyObject_IsTrue(v); 521 | break; 522 | case 't': { 523 | Py_ssize_t size; 524 | const char* data = PyUnicode_AsUTF8AndSize(v, &size); 525 | if (data) { 526 | int max_size = sizeof(el->raw); 527 | int cpysize = size < max_size ? size : max_size; 528 | memcpy(el->raw, data, cpysize); 529 | } 530 | } break; 531 | case 'r': { 532 | Py_ssize_t size = PyBytes_Size(v); 533 | char* data = PyBytes_AsString(v); 534 | if (data) { 535 | int max_size = sizeof(el->raw); 536 | int cpysize = size < max_size ? size : max_size; 537 | memcpy(el->raw, data, cpysize); 538 | } 539 | } break; 540 | default: 541 | return false; 542 | }; 543 | 544 | if (PyErr_Occurred()) { 545 | return false; 546 | } 547 | } 548 | 549 | return true; 550 | } 551 | 552 | static PyObject* parsed_elements_to_python(ParsedElement* elements, CompiledFormat fmt) 553 | { 554 | PyObject* result = PyTuple_New(fmt.ndescs - fmt.npadding); 555 | if (result == NULL) { 556 | PyErr_NoMemory(); 557 | return NULL; 558 | } 559 | 560 | int j = 0; 561 | for (int i = 0; i < fmt.ndescs; ++i) { 562 | PyObject* v = NULL; 563 | ParsedElement* el = elements + i; 564 | Desc* desc = fmt.descs + i; 565 | 566 | switch (desc->type) { 567 | case 'u': 568 | #if SIZEOF_LONG >= 8 569 | v = PyLong_FromUnsignedLong(el->uint64); 570 | #else 571 | v = PyLong_FromUnsignedLongLong(el->uint64); 572 | #endif // SIZEOF_LONG >= 8 573 | break; 574 | case 's': 575 | #if SIZEOF_LONG >= 8 576 | v = PyLong_FromLong(el->int64); 577 | #else 578 | v = PyLong_FromLongLong(el->int64); 579 | #endif // SIZEOF_LONG >= 8 580 | break; 581 | case 'f': 582 | #if PY_VERSION_HEX >= 0x030B00A7 583 | if (desc->bits == 16) { 584 | double cv = PyFloat_Unpack2((const char*)el->raw, PY_LITTLE_ENDIAN); 585 | if (cv == -1.0 && PyErr_Occurred()) { 586 | break; 587 | } 588 | v = PyFloat_FromDouble(cv); 589 | } 590 | else 591 | #elif PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 592 | if (desc->bits == 16) { 593 | double cv = _PyFloat_Unpack2(el->raw, PY_LITTLE_ENDIAN); 594 | if (cv == -1.0 && PyErr_Occurred()) { 595 | break; 596 | } 597 | v = PyFloat_FromDouble(cv); 598 | } 599 | else 600 | #endif // PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 6 601 | if (desc->bits == 32) { 602 | v = PyFloat_FromDouble(el->fp32); 603 | } 604 | else if (desc->bits == 64) { 605 | v = PyFloat_FromDouble(el->fp64); 606 | } 607 | break; 608 | case 'b': 609 | v = PyBool_FromLong(!!el->uint64); 610 | break; 611 | case 't': 612 | v = PyUnicode_FromStringAndSize( 613 | (const char*)el->raw, (desc->bits + 7) / 8); 614 | break; 615 | case 'r': 616 | v = PyBytes_FromStringAndSize( 617 | (const char*)el->raw, (desc->bits + 7) / 8); 618 | break; 619 | case 'p': 620 | case 'P': 621 | // Padding, ignore 622 | continue; 623 | }; 624 | 625 | if (!v) { 626 | // An exception has been set 627 | Py_DECREF(result); 628 | return NULL; 629 | } 630 | 631 | PyTuple_SET_ITEM(result, j++, v); 632 | } 633 | 634 | return result; 635 | } 636 | 637 | // Modified version of PyArg_ParseTupleAndKeywords 638 | // to parse 'n' arguments from args or kwargs and return 639 | // the number of arguments parsed from 'args' 640 | static Py_ssize_t PyArg_ParseTupleAndKeywordsFirstN( 641 | PyObject* args, 642 | PyObject* kwargs, 643 | const char* format, 644 | char* keywords[], 645 | int n, 646 | ...) 647 | { 648 | va_list varargs; 649 | va_start(varargs, n); 650 | 651 | Py_ssize_t return_value = -1; 652 | Py_ssize_t nkwargs = kwargs ? PyObject_Length(kwargs) : 0; 653 | Py_ssize_t n_actual_args = n - nkwargs; 654 | 655 | if (PyTuple_GET_SIZE(args) < n_actual_args) { 656 | PyErr_SetString(PyExc_TypeError, "Not enough arguments"); 657 | goto exit; 658 | } 659 | 660 | PyObject* actual_args = PyTuple_GetSlice(args, 0, n_actual_args); 661 | if (!actual_args) { 662 | PyErr_NoMemory(); 663 | goto exit; 664 | } 665 | 666 | if (PyArg_VaParseTupleAndKeywords( 667 | actual_args, kwargs, format, keywords, varargs)) { 668 | return_value = n_actual_args; 669 | } 670 | Py_DECREF(actual_args); 671 | 672 | exit: 673 | va_end(varargs); 674 | return return_value; 675 | } 676 | 677 | static bool PopFillPadding(PyObject* kwargs) 678 | { 679 | // get the fill_padding value and remove 680 | // it from kwargs as it makes parsing painful 681 | bool fill_padding = true; 682 | if (kwargs) { 683 | PyObject* py_fill_padding = PyDict_GetItemString(kwargs, FILL_PADDING); 684 | if (py_fill_padding) { 685 | fill_padding = PyObject_IsTrue(py_fill_padding); 686 | PyDict_DelItemString(kwargs, FILL_PADDING); 687 | } 688 | } 689 | return fill_padding; 690 | } 691 | 692 | static PyObject* CompiledFormat_pack_raw( 693 | CompiledFormat compiled_fmt, 694 | PyObject** data, 695 | Py_ssize_t n_data) 696 | { 697 | ParsedElement elements_stack[SMALL_FORMAT_OPTIMIZATION]; 698 | ParsedElement* elements = elements_stack; 699 | bool use_stack = compiled_fmt.ndescs <= SMALL_FORMAT_OPTIMIZATION; 700 | PyObject* bytes = NULL; 701 | 702 | int expected_size = compiled_fmt.ndescs - compiled_fmt.npadding; 703 | if (n_data < expected_size) { 704 | PyErr_Format( 705 | PyExc_TypeError, 706 | "pack() expected %d arguments (got %ld)", 707 | expected_size, 708 | n_data); 709 | return NULL; 710 | } 711 | 712 | if (!use_stack) { 713 | elements = PyMem_RawMalloc(compiled_fmt.ndescs * sizeof(ParsedElement)); 714 | if (!elements) { 715 | PyErr_NoMemory(); 716 | return NULL; 717 | } 718 | } 719 | 720 | if (!python_to_parsed_elements(elements, data, n_data, compiled_fmt)) { 721 | PyErr_SetString(PyExc_TypeError, "failed to parse arguments"); 722 | goto exit; 723 | } 724 | 725 | int nbytes = (compiled_fmt.nbits + 7) / 8; 726 | bytes = PyBytes_FromStringAndSize(NULL, nbytes); 727 | if (!bytes) { 728 | PyErr_NoMemory(); 729 | goto exit; 730 | } 731 | 732 | PyBytes_AS_STRING(bytes)[nbytes - 1] = 0; 733 | c_pack((uint8_t*)PyBytes_AS_STRING(bytes), elements, compiled_fmt, 0, true); 734 | 735 | exit: 736 | if (!use_stack) { 737 | PyMem_RawFree(elements); 738 | } 739 | 740 | return bytes; 741 | } 742 | 743 | static PyObject* CompiledFormat_pack_into_raw( 744 | CompiledFormat compiled_fmt, 745 | Py_buffer* buffer, 746 | Py_ssize_t offset, 747 | PyObject** data_args, 748 | Py_ssize_t n_data_args, 749 | bool fill_padding) 750 | { 751 | ParsedElement elements_stack[SMALL_FORMAT_OPTIMIZATION]; 752 | ParsedElement* elements = elements_stack; 753 | bool use_stack = compiled_fmt.ndescs <= SMALL_FORMAT_OPTIMIZATION; 754 | PyObject* return_value = NULL; 755 | 756 | int expected_size = compiled_fmt.ndescs - compiled_fmt.npadding; 757 | if (n_data_args < expected_size) { 758 | PyErr_Format( 759 | PyExc_TypeError, 760 | "expected %d data arguments (got %ld)", 761 | expected_size, 762 | n_data_args); 763 | goto exit; 764 | } 765 | 766 | if (!PyBuffer_IsContiguous(buffer, 'C')) { 767 | PyErr_Format(PyExc_TypeError, "required a contiguous buffer"); 768 | goto exit; 769 | } 770 | 771 | int nbytes = (compiled_fmt.nbits + 7) / 8; 772 | if (buffer->len < nbytes) { 773 | PyErr_Format( 774 | PyExc_TypeError, "required a buffer of at least %d bytes", nbytes); 775 | goto exit; 776 | } 777 | 778 | if (!use_stack) { 779 | elements = PyMem_RawMalloc(compiled_fmt.ndescs * sizeof(ParsedElement)); 780 | if (!elements) { 781 | PyErr_NoMemory(); 782 | goto exit; 783 | } 784 | } 785 | 786 | if (!python_to_parsed_elements(elements, data_args, n_data_args, compiled_fmt)) { 787 | // python_to_parsed_elements should set the exception 788 | goto exit; 789 | } 790 | 791 | c_pack((uint8_t*)buffer->buf, elements, compiled_fmt, offset, fill_padding); 792 | 793 | return_value = Py_None; 794 | Py_INCREF(Py_None); 795 | 796 | exit: 797 | if (!use_stack && elements) { 798 | PyMem_RawFree(elements); 799 | } 800 | 801 | return return_value; 802 | } 803 | 804 | /* Python methods */ 805 | 806 | // clang-format off 807 | /*[clinic input] 808 | class CompiledFormat "PyCompiledFormatObject *" "&PyStructType" 809 | [clinic start generated code]*/ 810 | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=470ab77e2b50e7be]*/ 811 | // clang-format on 812 | 813 | // clang-format off 814 | typedef struct { 815 | PyObject_HEAD 816 | CompiledFormat compiled_fmt; 817 | } PyCompiledFormatObject; 818 | // clang-format on 819 | 820 | // clang-format off 821 | /*[clinic input] 822 | class CompiledFormatDict "PyCompiledFormatDictObject *" "&PyStructType" 823 | [clinic start generated code]*/ 824 | /*[clinic end generated code: output=da39a3ee5e6b4b0d input=fd49c75ad758a8d9]*/ 825 | // clang-format on 826 | 827 | typedef struct { 828 | PyCompiledFormatObject super; 829 | PyObject* names; 830 | } PyCompiledFormatDictObject; 831 | 832 | #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 13 833 | #include "clinic/_cbitstruct.c.313.h" 834 | #elif PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7 835 | #include "clinic/_cbitstruct.c.37.h" 836 | #elif PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 6 837 | #include "clinic/_cbitstruct.c.36.h" 838 | #else 839 | #error "Unsupported python version" 840 | #endif 841 | 842 | /* CompiledFormat */ 843 | 844 | // clang-format off 845 | /*[clinic input] 846 | CompiledFormat.__init__ 847 | 848 | fmt: str 849 | 850 | Create a compiled bitstruct object. 851 | 852 | Return a new CompiledFormat object which writes and reads binary data 853 | according to the format string. 854 | [clinic start generated code]*/ 855 | 856 | static int 857 | CompiledFormat___init___impl(PyCompiledFormatObject *self, const char *fmt) 858 | /*[clinic end generated code: output=cd0e3acd08d00e64 input=2beef0b1a8e9bed3]*/ 859 | // clang-format on 860 | { 861 | self->compiled_fmt = c_compile_format(fmt); 862 | if (!self->compiled_fmt.valid) { 863 | PyErr_SetString(PyExc_TypeError, "bad or unsupported format"); 864 | return -1; 865 | } 866 | 867 | return 0; 868 | } 869 | 870 | static PyObject* CompiledFormat_new(PyTypeObject* type, PyObject* args, PyObject* kwds) 871 | { 872 | return type->tp_alloc(type, 0); 873 | } 874 | 875 | static void CompiledFormat_deinit(PyCompiledFormatObject* self) 876 | { 877 | c_release_compiled_format(&self->compiled_fmt); 878 | } 879 | 880 | static void CompiledFormat_dealloc(PyCompiledFormatObject* self) 881 | { 882 | CompiledFormat_deinit(self); 883 | Py_TYPE(self)->tp_free((PyObject*)self); 884 | } 885 | 886 | // clang-format off 887 | /*[clinic input] 888 | CompiledFormat.calcsize -> Py_ssize_t 889 | 890 | Return size in bits of the bitDescribed by the format string. 891 | [clinic start generated code]*/ 892 | 893 | static Py_ssize_t 894 | CompiledFormat_calcsize_impl(PyCompiledFormatObject *self) 895 | /*[clinic end generated code: output=6370799a72a08ece input=3eb320f6268226f2]*/ 896 | // clang-format on 897 | { 898 | return self->compiled_fmt.nbits; 899 | } 900 | 901 | // clang-format off 902 | PyDoc_STRVAR(CompiledFormat_pack__doc__, 903 | "pack($self, *args)\n" 904 | "--\n" 905 | "\n" 906 | "Pack args into a bytes object."); 907 | // clang-format on 908 | static PyObject* CompiledFormat_pack(PyCompiledFormatObject* self, PyObject* args) 909 | { 910 | PyObject** data = PySequence_Fast_ITEMS(args); 911 | Py_ssize_t n_data = PyTuple_GET_SIZE(args); 912 | return CompiledFormat_pack_raw(self->compiled_fmt, data, n_data); 913 | } 914 | 915 | // clang-format off 916 | PyDoc_STRVAR(CompiledFormat_pack_into__doc__, 917 | "pack_into($self, buf, offset, *args, **kwargs)\n" 918 | "--\n" 919 | "\n" 920 | "Pack args into a bytes object, starting at bit offset given by the\n" 921 | "offset argument. An optional 'fill_padding=False' argument can be given\n" 922 | "to keep padding bits from 'buf' as-is."); 923 | // clang-format on 924 | static PyObject* CompiledFormat_pack_into( 925 | PyCompiledFormatObject* self, 926 | PyObject* args, 927 | PyObject* kwargs) 928 | { 929 | PyObject* return_value = NULL; 930 | Py_buffer buffer = {NULL, NULL}; 931 | Py_ssize_t offset = 0; 932 | 933 | bool fill_padding = PopFillPadding(kwargs); 934 | Py_ssize_t n_args = PyTuple_GET_SIZE(args); 935 | PyObject** data = PySequence_Fast_ITEMS(args); 936 | 937 | static char* _keywords[] = {"buf", "offset", NULL}; 938 | // custom (and vague) error message as all other 'pack_into' 939 | // versions are processed by this function. Using the default 940 | // error message would give bad information to the user 941 | Py_ssize_t n_args_parsed = PyArg_ParseTupleAndKeywordsFirstN( 942 | args, kwargs, "y*n:pack_into", _keywords, 2, &buffer, &offset); 943 | if (n_args_parsed < 0) { 944 | goto exit; 945 | } 946 | 947 | return_value = CompiledFormat_pack_into_raw( 948 | self->compiled_fmt, 949 | &buffer, 950 | offset, 951 | data + n_args_parsed, 952 | n_args - n_args_parsed, 953 | fill_padding); 954 | 955 | exit: 956 | if (buffer.obj) { 957 | PyBuffer_Release(&buffer); 958 | } 959 | return return_value; 960 | } 961 | 962 | // clang-format off 963 | /*[clinic input] 964 | CompiledFormat.unpack 965 | 966 | data: Py_buffer 967 | 968 | Return a tuple containing unpacked values. 969 | [clinic start generated code]*/ 970 | 971 | static PyObject * 972 | CompiledFormat_unpack_impl(PyCompiledFormatObject *self, Py_buffer *data) 973 | /*[clinic end generated code: output=489f0a43ef4c99ec input=2377fa8de58bde66]*/ 974 | // clang-format on 975 | { 976 | return CompiledFormat_unpack_from_impl(self, data, 0); 977 | } 978 | 979 | // clang-format off 980 | /*[clinic input] 981 | CompiledFormat.unpack_from 982 | 983 | data: Py_buffer 984 | offset: Py_ssize_t = 0 985 | 986 | Return a tuple containing unpacked values starting at 'offset' bits. 987 | [clinic start generated code]*/ 988 | 989 | static PyObject * 990 | CompiledFormat_unpack_from_impl(PyCompiledFormatObject *self, 991 | Py_buffer *data, Py_ssize_t offset) 992 | /*[clinic end generated code: output=b9d1446fc0990bef input=9ea43549ede42f94]*/ 993 | // clang-format on 994 | { 995 | ParsedElement elements_stack[SMALL_FORMAT_OPTIMIZATION]; 996 | ParsedElement* elements = elements_stack; 997 | bool use_stack = self->compiled_fmt.ndescs <= SMALL_FORMAT_OPTIMIZATION; 998 | 999 | if (!PyBuffer_IsContiguous(data, 'C')) { 1000 | PyErr_Format(PyExc_TypeError, "unpack() expects a contiguous buffer"); 1001 | return NULL; 1002 | } 1003 | 1004 | int nbytes = (self->compiled_fmt.nbits + offset + 7) / 8; 1005 | if (data->len < nbytes) { 1006 | PyErr_Format( 1007 | PyExc_TypeError, 1008 | "unpack() requires a buffer of at least %d bytes", 1009 | nbytes); 1010 | return NULL; 1011 | } 1012 | 1013 | if (!use_stack) { 1014 | elements = PyMem_RawMalloc( 1015 | self->compiled_fmt.ndescs * sizeof(ParsedElement)); 1016 | if (!elements) { 1017 | PyErr_NoMemory(); 1018 | return NULL; 1019 | } 1020 | } 1021 | 1022 | c_unpack(elements, data->buf, self->compiled_fmt, offset); 1023 | PyObject* return_value = 1024 | parsed_elements_to_python(elements, self->compiled_fmt); 1025 | 1026 | if (!use_stack) { 1027 | PyMem_RawFree(elements); 1028 | } 1029 | 1030 | return return_value; 1031 | } 1032 | 1033 | // clang-format off 1034 | static struct PyMethodDef CompiledFormat_methods[] = { 1035 | { 1036 | "pack", 1037 | (PyCFunction)CompiledFormat_pack, 1038 | METH_VARARGS, 1039 | CompiledFormat_pack__doc__ 1040 | }, 1041 | { 1042 | "pack_into", 1043 | (PyCFunction)CompiledFormat_pack_into, 1044 | METH_VARARGS|METH_KEYWORDS, 1045 | CompiledFormat_pack_into__doc__ 1046 | }, 1047 | COMPILEDFORMAT_UNPACK_METHODDEF 1048 | COMPILEDFORMAT_CALCSIZE_METHODDEF 1049 | COMPILEDFORMAT_UNPACK_FROM_METHODDEF 1050 | {NULL, NULL} 1051 | }; 1052 | 1053 | static PyTypeObject PyCompiledFormatType = { 1054 | PyVarObject_HEAD_INIT(NULL, 0) 1055 | .tp_name = "_cbitstruct.CompiledFormat", 1056 | .tp_doc = CompiledFormat___init____doc__, 1057 | .tp_basicsize = sizeof(PyCompiledFormatObject), 1058 | .tp_itemsize = 0, 1059 | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 1060 | .tp_init = (initproc) CompiledFormat___init__, 1061 | .tp_new = CompiledFormat_new, 1062 | .tp_dealloc = (destructor) CompiledFormat_dealloc, 1063 | .tp_methods = CompiledFormat_methods, 1064 | }; 1065 | // clang-format on 1066 | 1067 | /* CompiledFormatDict */ 1068 | 1069 | // clang-format off 1070 | /*[clinic input] 1071 | CompiledFormatDict.__init__ 1072 | 1073 | fmt: str 1074 | names: object 1075 | 1076 | Create a compiled bitstruct object. 1077 | 1078 | Return a new CompiledFormatDict object which writes and reads binary data 1079 | according to the format string. The names list 'name' will be used 1080 | as keys in data dictionaries. 1081 | [clinic start generated code]*/ 1082 | 1083 | static int 1084 | CompiledFormatDict___init___impl(PyCompiledFormatDictObject *self, 1085 | const char *fmt, PyObject *names) 1086 | /*[clinic end generated code: output=1d94d08e0ab9bdba input=9964c21a875025a8]*/ 1087 | // clang-format on 1088 | { 1089 | self->names = NULL; 1090 | 1091 | if (CompiledFormat___init___impl((PyCompiledFormatObject*)self, fmt)) { 1092 | // CompiledFormat___init___impl has set the exception 1093 | return -1; 1094 | } 1095 | 1096 | self->names = PySequence_Fast(names, "names must be a sequence"); 1097 | if (!self->names) { 1098 | // PySequence_Fast sets the exception 1099 | return -1; 1100 | } 1101 | 1102 | return 0; 1103 | } 1104 | 1105 | static void CompiledFormatDict_deinit(PyCompiledFormatDictObject* self) 1106 | { 1107 | if (self->names) { 1108 | Py_DECREF(self->names); 1109 | } 1110 | CompiledFormat_deinit((PyCompiledFormatObject*)self); 1111 | } 1112 | 1113 | static void CompiledFormatDict_dealloc(PyCompiledFormatDictObject* self) 1114 | { 1115 | CompiledFormatDict_deinit(self); 1116 | Py_TYPE(self)->tp_free((PyObject*)self); 1117 | } 1118 | 1119 | // clang-format off 1120 | /*[clinic input] 1121 | CompiledFormatDict.pack 1122 | 1123 | data: object 1124 | 1125 | Pack values from a dict into a bytes object 1126 | 1127 | Return a tuple containing unpacked values. 1128 | 'data' is a dictionary containing values whic keys are the 'names' 1129 | used when constructing this object. 1130 | [clinic start generated code]*/ 1131 | 1132 | static PyObject * 1133 | CompiledFormatDict_pack_impl(PyCompiledFormatDictObject *self, 1134 | PyObject *data) 1135 | /*[clinic end generated code: output=8c26c6e4fc16088c input=afba13ca6085dd00]*/ 1136 | // clang-format on 1137 | { 1138 | PyObject* return_value = NULL; 1139 | 1140 | Py_ssize_t nnames = PySequence_Fast_GET_SIZE(self->names); 1141 | PyObject** names = PySequence_Fast_ITEMS(self->names); 1142 | PyObject* values = PyTuple_New(nnames); 1143 | if (!values) { 1144 | PyErr_NoMemory(); 1145 | return NULL; 1146 | } 1147 | 1148 | for (int i = 0; i < nnames; ++i) { 1149 | PyObject* v = PyObject_GetItem(data, names[i]); 1150 | if (!v) { 1151 | // PyObject_GetItem sets KeyError 1152 | goto exit; 1153 | } 1154 | PyTuple_SET_ITEM(values, i, v); 1155 | } 1156 | 1157 | return_value = CompiledFormat_pack((PyCompiledFormatObject*)self, values); 1158 | 1159 | exit: 1160 | if (values) { 1161 | Py_DECREF(values); 1162 | } 1163 | 1164 | return return_value; 1165 | } 1166 | 1167 | // clang-format off 1168 | /*[clinic input] 1169 | CompiledFormatDict.pack_into 1170 | 1171 | buf: Py_buffer 1172 | offset: Py_ssize_t 1173 | data: object 1174 | * 1175 | fill_padding: bool = True 1176 | 1177 | Pack data into a bytes object, starting at bit offset given by the offset argument. 1178 | 1179 | With fill_padding=False, passing bits in 'buf' will not be modified. 1180 | [clinic start generated code]*/ 1181 | 1182 | static PyObject * 1183 | CompiledFormatDict_pack_into_impl(PyCompiledFormatDictObject *self, 1184 | Py_buffer *buf, Py_ssize_t offset, 1185 | PyObject *data, int fill_padding) 1186 | /*[clinic end generated code: output=ee246de261e9c699 input=290a9a4a3e3ed942]*/ 1187 | // clang-format on 1188 | { 1189 | PyObject* return_value = NULL; 1190 | 1191 | Py_ssize_t nnames = PySequence_Fast_GET_SIZE(self->names); 1192 | PyObject** names = PySequence_Fast_ITEMS(self->names); 1193 | 1194 | PyObject* data_tuple = PyTuple_New(nnames); 1195 | if (!data_tuple) { 1196 | PyErr_NoMemory(); 1197 | goto exit; 1198 | } 1199 | 1200 | for (int i = 0; i < nnames; ++i) { 1201 | PyObject* v = PyObject_GetItem(data, names[i]); 1202 | if (!v) { 1203 | // PyObject_GetItem sets KeyError 1204 | goto exit; 1205 | } 1206 | PyTuple_SET_ITEM(data_tuple, i, v); 1207 | } 1208 | 1209 | PyObject** data_array = PySequence_Fast_ITEMS(data_tuple); 1210 | return_value = CompiledFormat_pack_into_raw( 1211 | self->super.compiled_fmt, buf, offset, data_array, nnames, fill_padding); 1212 | 1213 | exit: 1214 | if (data_tuple) { 1215 | Py_DECREF(data_tuple); 1216 | } 1217 | 1218 | return return_value; 1219 | } 1220 | 1221 | // clang-format off 1222 | /*[clinic input] 1223 | CompiledFormatDict.unpack 1224 | 1225 | data: Py_buffer 1226 | 1227 | Unpack data into a dict which keys are the 'names' used when constructing this object. 1228 | 1229 | Return a dict containing unpacked values. 1230 | [clinic start generated code]*/ 1231 | 1232 | static PyObject * 1233 | CompiledFormatDict_unpack_impl(PyCompiledFormatDictObject *self, 1234 | Py_buffer *data) 1235 | /*[clinic end generated code: output=647eec29c90d0c63 input=40d648e22f9a7249]*/ 1236 | // clang-format on 1237 | { 1238 | return CompiledFormatDict_unpack_from_impl(self, data, 0); 1239 | } 1240 | 1241 | // clang-format off 1242 | /*[clinic input] 1243 | CompiledFormatDict.unpack_from 1244 | 1245 | data: Py_buffer 1246 | offset: Py_ssize_t = 0 1247 | 1248 | Unpack data into a dict starting at 'offset' bits. 1249 | 1250 | Return a dict containing unpacked values. 1251 | [clinic start generated code]*/ 1252 | 1253 | static PyObject * 1254 | CompiledFormatDict_unpack_from_impl(PyCompiledFormatDictObject *self, 1255 | Py_buffer *data, Py_ssize_t offset) 1256 | /*[clinic end generated code: output=5f5d4987c9da42fe input=f7e4af99e1650077]*/ 1257 | // clang-format on 1258 | { 1259 | PyObject* return_value = NULL; 1260 | PyObject* tuple_res = NULL; 1261 | PyObject* tuple_res_seq = NULL; 1262 | 1263 | Py_ssize_t len = PySequence_Fast_GET_SIZE(self->names); 1264 | tuple_res = CompiledFormat_unpack_from_impl( 1265 | (PyCompiledFormatObject*)self, data, offset); 1266 | if (!tuple_res) { 1267 | // We expect CompiledFormat_unpack_impl to set the exception 1268 | goto exit; 1269 | } 1270 | 1271 | tuple_res_seq = PySequence_Fast(tuple_res, ""); 1272 | if (!tuple_res_seq) { 1273 | // PySequence_Fast sets the exception 1274 | goto exit; 1275 | } 1276 | 1277 | if (PySequence_Fast_GET_SIZE(tuple_res_seq) != len) { 1278 | PyErr_Format( 1279 | PyExc_TypeError, 1280 | "unpacked %d values, but have %d names", 1281 | PySequence_Fast_GET_SIZE(tuple_res_seq), 1282 | len); 1283 | goto exit; 1284 | } 1285 | 1286 | return_value = PyDict_New(); 1287 | if (!return_value) { 1288 | PyErr_NoMemory(); 1289 | goto exit; 1290 | } 1291 | 1292 | PyObject** names = PySequence_Fast_ITEMS(self->names); 1293 | PyObject** values = PySequence_Fast_ITEMS(tuple_res_seq); 1294 | for (int i = 0; i < len; ++i) { 1295 | if (PyDict_SetItem(return_value, names[i], values[i])) { 1296 | // PyDict_SetItem sets TypeError 1297 | goto exit; 1298 | } 1299 | } 1300 | 1301 | exit: 1302 | if (tuple_res_seq) { 1303 | Py_DECREF(tuple_res_seq); 1304 | } 1305 | if (tuple_res) { 1306 | Py_DECREF(tuple_res); 1307 | } 1308 | 1309 | return return_value; 1310 | } 1311 | 1312 | // clang-format off 1313 | static struct PyMethodDef CompiledFormatDict_methods[] = { 1314 | COMPILEDFORMATDICT_PACK_METHODDEF 1315 | COMPILEDFORMATDICT_UNPACK_METHODDEF 1316 | COMPILEDFORMATDICT_UNPACK_FROM_METHODDEF 1317 | COMPILEDFORMATDICT_PACK_INTO_METHODDEF 1318 | {NULL, NULL} 1319 | }; 1320 | 1321 | static PyTypeObject PyCompiledFormatDictType = { 1322 | PyVarObject_HEAD_INIT(NULL, 0) 1323 | .tp_name = "_cbitstruct.CompiledFormatDict", 1324 | .tp_doc = CompiledFormatDict___init____doc__, 1325 | .tp_basicsize = sizeof(PyCompiledFormatDictObject), 1326 | .tp_itemsize = 0, 1327 | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 1328 | .tp_base = &PyCompiledFormatType, 1329 | .tp_init = (initproc) CompiledFormatDict___init__, 1330 | .tp_dealloc = (destructor) CompiledFormatDict_dealloc, 1331 | .tp_methods = CompiledFormatDict_methods, 1332 | }; 1333 | // clang-format on 1334 | 1335 | /* Functions */ 1336 | 1337 | // clang-format off 1338 | PyDoc_STRVAR(pack__doc__, 1339 | "pack(fmt, *args)\n" 1340 | "--\n" 1341 | "\n" 1342 | "Pack args into a bytes object according to fmt"); 1343 | // clang-format on 1344 | static PyObject* pack(PyObject* module, PyObject* args, PyObject* kwargs) 1345 | { 1346 | PyObject* return_value = NULL; 1347 | const char* fmt = NULL; 1348 | PyCompiledFormatObject self; 1349 | memset(&self, 0, sizeof(self)); 1350 | 1351 | static char* _keywords[] = {"fmt", NULL}; 1352 | Py_ssize_t n_args_parsed = PyArg_ParseTupleAndKeywordsFirstN( 1353 | args, kwargs, "s:pack", _keywords, 1, &fmt); 1354 | if (n_args_parsed < 0) { 1355 | goto exit; 1356 | } 1357 | 1358 | if (CompiledFormat___init___impl(&self, fmt)) { 1359 | // CompiledFormat___init___impl has set the exception 1360 | goto exit; 1361 | } 1362 | 1363 | Py_ssize_t n_args = PyTuple_GET_SIZE(args); 1364 | PyObject** data = PySequence_Fast_ITEMS(args); 1365 | return_value = CompiledFormat_pack_raw( 1366 | self.compiled_fmt, data + n_args_parsed, n_args - n_args_parsed); 1367 | 1368 | exit: 1369 | CompiledFormat_deinit(&self); 1370 | return return_value; 1371 | } 1372 | 1373 | // clang-format off 1374 | PyDoc_STRVAR(pack_into__doc__, 1375 | "pack_into(fmt, buf, offset, *args, **kwargs)\n" 1376 | "--\n" 1377 | "\n" 1378 | "Pack args into a bytes object according to fmt, starting at bit offset given by the\n" 1379 | "offset argument. An optional 'fill_padding=False' argument can be given\n" 1380 | "to keep padding bits from 'buf' as-is."); 1381 | // clang-format on 1382 | static PyObject* pack_into(PyObject* module, PyObject* args, PyObject* kwargs) 1383 | { 1384 | PyObject* return_value = NULL; 1385 | Py_buffer buffer = {NULL, NULL}; 1386 | Py_ssize_t offset = 0; 1387 | const char* fmt = NULL; 1388 | PyCompiledFormatObject self; 1389 | memset(&self, 0, sizeof(self)); 1390 | bool fill_padding = PopFillPadding(kwargs); 1391 | 1392 | static char* _keywords[] = {"fmt", "buf", "offset", NULL}; 1393 | Py_ssize_t n_args_parsed = PyArg_ParseTupleAndKeywordsFirstN( 1394 | args, kwargs, "sy*n:pack_into", _keywords, 3, &fmt, &buffer, &offset); 1395 | if (n_args_parsed < 0) { 1396 | goto exit; 1397 | } 1398 | 1399 | if (CompiledFormat___init___impl(&self, fmt)) { 1400 | // CompiledFormat___init___impl has set the exception 1401 | goto exit; 1402 | } 1403 | 1404 | Py_ssize_t n_args = PyTuple_GET_SIZE(args); 1405 | PyObject** data = PySequence_Fast_ITEMS(args); 1406 | return_value = CompiledFormat_pack_into_raw( 1407 | self.compiled_fmt, 1408 | &buffer, 1409 | offset, 1410 | data + n_args_parsed, 1411 | n_args - n_args_parsed, 1412 | fill_padding); 1413 | 1414 | exit: 1415 | CompiledFormat_deinit(&self); 1416 | return return_value; 1417 | } 1418 | 1419 | // clang-format off 1420 | /*[clinic input] 1421 | pack_dict 1422 | 1423 | fmt: str 1424 | names: object 1425 | data: object 1426 | 1427 | 1428 | Pack the dict data into a bytes object according to format. 1429 | 1430 | The order of value is determines by the list 'names'. 1431 | [clinic start generated code]*/ 1432 | 1433 | static PyObject * 1434 | pack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 1435 | PyObject *data) 1436 | /*[clinic end generated code: output=b44920272f0e4e79 input=3222bbb02f49b7e9]*/ 1437 | // clang-format on 1438 | { 1439 | PyObject* return_value = NULL; 1440 | PyCompiledFormatDictObject self; 1441 | memset(&self, 0, sizeof(self)); 1442 | 1443 | if (CompiledFormatDict___init___impl(&self, fmt, names)) { 1444 | // CompiledFormatDict___init___impl has set the exception 1445 | goto exit; 1446 | } 1447 | return_value = CompiledFormatDict_pack_impl(&self, data); 1448 | 1449 | exit: 1450 | CompiledFormatDict_deinit(&self); 1451 | return return_value; 1452 | } 1453 | 1454 | // clang-format off 1455 | /*[clinic input] 1456 | pack_into_dict 1457 | 1458 | fmt: str 1459 | names: object 1460 | buf: Py_buffer 1461 | offset: Py_ssize_t 1462 | data: object 1463 | * 1464 | fill_padding: bool = True 1465 | 1466 | Pack data into a bytes object, starting at bit offset given by the offset argument. 1467 | 1468 | With fill_padding=False, passing bits in 'buf' will not be modified. 1469 | [clinic start generated code]*/ 1470 | 1471 | static PyObject * 1472 | pack_into_dict_impl(PyObject *module, const char *fmt, PyObject *names, 1473 | Py_buffer *buf, Py_ssize_t offset, PyObject *data, 1474 | int fill_padding) 1475 | /*[clinic end generated code: output=619b415fc187011b input=e72dec46484ec66f]*/ 1476 | // clang-format on 1477 | { 1478 | PyObject* return_value = NULL; 1479 | PyCompiledFormatDictObject self; 1480 | memset(&self, 0, sizeof(self)); 1481 | 1482 | if (CompiledFormatDict___init___impl(&self, fmt, names)) { 1483 | // CompiledFormatDict___init___impl has set the exception 1484 | goto exit; 1485 | } 1486 | 1487 | return_value = 1488 | CompiledFormatDict_pack_into_impl(&self, buf, offset, data, fill_padding); 1489 | 1490 | exit: 1491 | CompiledFormatDict_deinit(&self); 1492 | return return_value; 1493 | } 1494 | 1495 | // clang-format off 1496 | /*[clinic input] 1497 | unpack 1498 | 1499 | fmt: str 1500 | data: Py_buffer 1501 | 1502 | 1503 | Unpack data according to the format 'fmt'. Returns a tuple. 1504 | [clinic start generated code]*/ 1505 | 1506 | static PyObject * 1507 | unpack_impl(PyObject *module, const char *fmt, Py_buffer *data) 1508 | /*[clinic end generated code: output=a143faad32f38aba input=50b83d898a3818c2]*/ 1509 | // clang-format on 1510 | { 1511 | return unpack_from_impl(module, fmt, data, 0); 1512 | } 1513 | 1514 | // clang-format off 1515 | /*[clinic input] 1516 | unpack_from 1517 | 1518 | fmt: str 1519 | data: Py_buffer 1520 | offset: Py_ssize_t = 0 1521 | 1522 | 1523 | Unpack data according to the format 'fmt', starting at bit offset 'offset. 1524 | 1525 | Returns a tuple. 1526 | [clinic start generated code]*/ 1527 | 1528 | static PyObject * 1529 | unpack_from_impl(PyObject *module, const char *fmt, Py_buffer *data, 1530 | Py_ssize_t offset) 1531 | /*[clinic end generated code: output=ae5caee332fca9c6 input=0b5f695460c5fc88]*/ 1532 | // clang-format on 1533 | { 1534 | PyObject* return_value = NULL; 1535 | PyCompiledFormatObject self; 1536 | memset(&self, 0, sizeof(self)); 1537 | 1538 | if (CompiledFormat___init___impl(&self, fmt)) { 1539 | // CompiledFormat___init___impl has set the exception 1540 | goto exit; 1541 | } 1542 | return_value = CompiledFormat_unpack_from_impl(&self, data, offset); 1543 | 1544 | exit: 1545 | CompiledFormat_deinit(&self); 1546 | return return_value; 1547 | } 1548 | 1549 | // clang-format off 1550 | /*[clinic input] 1551 | unpack_dict 1552 | 1553 | fmt: str 1554 | names: object 1555 | data: Py_buffer 1556 | 1557 | Unpack data according to 'fmt'. 1558 | 1559 | Returns a dict which keys are 'names'. 1560 | [clinic start generated code]*/ 1561 | 1562 | static PyObject * 1563 | unpack_dict_impl(PyObject *module, const char *fmt, PyObject *names, 1564 | Py_buffer *data) 1565 | /*[clinic end generated code: output=5ab92004f3aeb22e input=2fc9ff0adb4d3763]*/ 1566 | // clang-format on 1567 | { 1568 | return unpack_from_dict_impl(module, fmt, names, data, 0); 1569 | } 1570 | 1571 | // clang-format off 1572 | /*[clinic input] 1573 | unpack_from_dict 1574 | 1575 | fmt: str 1576 | names: object 1577 | data: Py_buffer 1578 | offset: Py_ssize_t = 0 1579 | 1580 | Unpack data according to 'fmt' starting at bit offset 'offset'. 1581 | 1582 | Returns a dict which keys are 'names'. 1583 | [clinic start generated code]*/ 1584 | 1585 | static PyObject * 1586 | unpack_from_dict_impl(PyObject *module, const char *fmt, PyObject *names, 1587 | Py_buffer *data, Py_ssize_t offset) 1588 | /*[clinic end generated code: output=047df6a3b1645e39 input=a8a1a55238a5174c]*/ 1589 | // clang-format on 1590 | { 1591 | PyObject* return_value = NULL; 1592 | PyCompiledFormatDictObject self; 1593 | memset(&self, 0, sizeof(self)); 1594 | 1595 | if (CompiledFormatDict___init___impl(&self, fmt, names)) { 1596 | // CompiledFormatDict___init___impl has set the exception 1597 | goto exit; 1598 | } 1599 | return_value = CompiledFormatDict_unpack_from_impl(&self, data, offset); 1600 | 1601 | exit: 1602 | CompiledFormatDict_deinit(&self); 1603 | return return_value; 1604 | } 1605 | 1606 | // clang-format off 1607 | /*[clinic input] 1608 | compile 1609 | 1610 | fmt: str 1611 | names: object = None 1612 | 1613 | Returns a compiled object for the format 'fmt'. 1614 | [clinic start generated code]*/ 1615 | 1616 | static PyObject * 1617 | compile_impl(PyObject *module, const char *fmt, PyObject *names) 1618 | /*[clinic end generated code: output=cd39debf9cc766ce input=fbc275638eafb2e9]*/ 1619 | // clang-format on 1620 | { 1621 | PyObject *args = NULL, *type = NULL; 1622 | if (names == Py_None) { 1623 | args = Py_BuildValue("(s)", fmt); 1624 | type = (PyObject*)&PyCompiledFormatType; 1625 | } 1626 | else { 1627 | args = Py_BuildValue("(sO)", fmt, names); 1628 | type = (PyObject*)&PyCompiledFormatDictType; 1629 | } 1630 | PyObject* result = PyObject_CallObject(type, args); 1631 | Py_DECREF(args); 1632 | return result; 1633 | } 1634 | 1635 | // clang-format off 1636 | /*[clinic input] 1637 | calcsize -> Py_ssize_t 1638 | 1639 | fmt: str 1640 | 1641 | Return size in bits of the bit described by the format string. 1642 | [clinic start generated code]*/ 1643 | 1644 | static Py_ssize_t 1645 | calcsize_impl(PyObject *module, const char *fmt) 1646 | /*[clinic end generated code: output=3eb5ae42af038a45 input=b0a27d37ccb4dd03]*/ 1647 | // clang-format on 1648 | { 1649 | Py_ssize_t return_value = -1; 1650 | PyCompiledFormatObject self; 1651 | memset(&self, 0, sizeof(self)); 1652 | 1653 | if (CompiledFormat___init___impl(&self, fmt)) { 1654 | // CompiledFormat___init___impl has set the exception 1655 | goto exit; 1656 | } 1657 | return_value = CompiledFormat_calcsize_impl(&self); 1658 | 1659 | exit: 1660 | CompiledFormat_deinit(&self); 1661 | return return_value; 1662 | } 1663 | 1664 | // clang-format off 1665 | /*[clinic input] 1666 | byteswap 1667 | 1668 | fmt: object 1669 | data: Py_buffer 1670 | offset: Py_ssize_t = 0 1671 | 1672 | 1673 | Swap bytes in `data` according to `fmt`, starting at byte `offset` and return the result. 1674 | 1675 | `fmt` must be an iterable, iterating over 1676 | number of bytes to swap. For example, the format string ``'24'`` 1677 | applied to the bytes ``b'\x00\x11\x22\x33\x44\x55'`` will 1678 | produce the result ``b'\x11\x00\x55\x44\x33\x22'``. 1679 | [clinic start generated code]*/ 1680 | 1681 | static PyObject * 1682 | byteswap_impl(PyObject *module, PyObject *fmt, Py_buffer *data, 1683 | Py_ssize_t offset) 1684 | /*[clinic end generated code: output=3a2ad2de3d5d61ab input=54efc0a1db975ba3]*/ 1685 | // clang-format on 1686 | { 1687 | PyObject* return_value = NULL; 1688 | int* count_iter = NULL; 1689 | Py_ssize_t length = -1; 1690 | 1691 | if (!PyBuffer_IsContiguous(data, 'C')) { 1692 | PyErr_Format(PyExc_TypeError, "byteswap() expects a contiguous buffer"); 1693 | goto exit; 1694 | } 1695 | 1696 | length = PyObject_Length(fmt); 1697 | if (length < 0) { 1698 | goto exit; 1699 | } 1700 | 1701 | count_iter = PyMem_RawMalloc(length * sizeof(int)); 1702 | if (!count_iter) { 1703 | PyErr_NoMemory(); 1704 | goto exit; 1705 | } 1706 | 1707 | long sum = 0; 1708 | if (PyUnicode_Check(fmt)) { 1709 | const char* cfmt = PyUnicode_AsUTF8(fmt); 1710 | if (!cfmt) { 1711 | goto exit; 1712 | } 1713 | 1714 | for (int i = 0; i < length; ++i) { 1715 | int len = cfmt[i] - '0'; 1716 | if (len < 0 || len > 9) { 1717 | PyErr_SetString( 1718 | PyExc_ValueError, "bad value in byteswap format"); 1719 | goto exit; 1720 | } 1721 | sum += len; 1722 | count_iter[i] = len; 1723 | } 1724 | } 1725 | else { 1726 | for (int i = 0; i < length; ++i) { 1727 | PyObject* item = PySequence_GetItem(fmt, i); 1728 | if (!item) { 1729 | goto exit; 1730 | } 1731 | 1732 | long len = PyLong_AsLong(item); 1733 | sum += len; 1734 | count_iter[i] = len; 1735 | Py_DECREF(item); 1736 | if (len == -1 && PyErr_Occurred()) { 1737 | goto exit; 1738 | } 1739 | } 1740 | } 1741 | 1742 | if (sum > data->len) { 1743 | PyErr_Format( 1744 | PyExc_TypeError, 1745 | "byteswap() requires a buffer of at least %d bytes", 1746 | sum); 1747 | goto exit; 1748 | } 1749 | 1750 | uint8_t* buf = (uint8_t*)data->buf + offset; 1751 | for (int i = 0; i < length; ++i) { 1752 | int nbytes = count_iter[i]; 1753 | c_byteswitch(buf, nbytes); 1754 | buf += nbytes; 1755 | } 1756 | 1757 | return_value = PyBytes_FromStringAndSize( 1758 | ((const char*)data->buf) + offset, data->len - offset); 1759 | if (!return_value) { 1760 | PyErr_NoMemory(); 1761 | goto exit; 1762 | } 1763 | 1764 | exit: 1765 | if (count_iter) { 1766 | PyMem_RawFree(count_iter); 1767 | } 1768 | 1769 | return return_value; 1770 | } 1771 | 1772 | // clang-format off 1773 | static struct PyMethodDef py_module_functions[] = { 1774 | { 1775 | "pack", 1776 | (PyCFunction)pack, 1777 | METH_VARARGS|METH_KEYWORDS, 1778 | pack__doc__ 1779 | }, 1780 | { 1781 | "pack_into", 1782 | (PyCFunction)pack_into, 1783 | METH_VARARGS|METH_KEYWORDS, 1784 | pack_into__doc__ 1785 | }, 1786 | PACK_DICT_METHODDEF 1787 | PACK_INTO_DICT_METHODDEF 1788 | UNPACK_METHODDEF 1789 | UNPACK_FROM_METHODDEF 1790 | UNPACK_DICT_METHODDEF 1791 | UNPACK_FROM_DICT_METHODDEF 1792 | COMPILE_METHODDEF 1793 | CALCSIZE_METHODDEF 1794 | BYTESWAP_METHODDEF 1795 | {NULL, NULL, 0, NULL}, /* sentinel */ 1796 | }; 1797 | // clang-format on 1798 | 1799 | /* Module initialization */ 1800 | 1801 | // clang-format off 1802 | PyDoc_STRVAR(cbitstructmodule__doc__, 1803 | "A fast implementation of the bitstruct.\n" 1804 | "\n" 1805 | "Check https://bitstruct.readthedocs.io/en/latest/ for documentation."); 1806 | // clang-format on 1807 | static struct PyModuleDef _cbitstructmodule = { 1808 | PyModuleDef_HEAD_INIT, 1809 | .m_name = "_cbitstruct", 1810 | .m_doc = cbitstructmodule__doc__, 1811 | .m_methods = py_module_functions, 1812 | .m_size = -1, 1813 | }; 1814 | 1815 | PyMODINIT_FUNC PyInit__cbitstruct(void) 1816 | { 1817 | PyObject* m; 1818 | 1819 | if (PyType_Ready(&PyCompiledFormatType) < 0) { 1820 | return NULL; 1821 | } 1822 | 1823 | if (PyType_Ready(&PyCompiledFormatDictType) < 0) { 1824 | return NULL; 1825 | } 1826 | 1827 | m = PyModule_Create(&_cbitstructmodule); 1828 | if (!m) { 1829 | return NULL; 1830 | } 1831 | 1832 | Py_INCREF(&PyCompiledFormatType); 1833 | Py_INCREF(&PyCompiledFormatDictType); 1834 | 1835 | PyModule_AddObject(m, "CompiledFormat", (PyObject*)&PyCompiledFormatType); 1836 | PyModule_AddObject(m, "CompiledFormatDict", (PyObject*)&PyCompiledFormatDictType); 1837 | 1838 | return m; 1839 | } 1840 | --------------------------------------------------------------------------------