Python library for converting between Python values and C bit field structs represented as byte strings.
npx @tessl/cli install tessl/pypi-bitstruct@8.21.0A Python library for converting between Python values and C bit field structs represented as byte strings. Bitstruct provides functionality similar to the built-in struct module but operates on individual bits instead of primitive data types, enabling precise bit-level data manipulation for binary protocols, embedded systems, and specialized data formats.
pip install bitstructimport bitstructCommon usage patterns:
from bitstruct import * # Import all functionsFor better performance (C extension):
import bitstruct.c as bitstruct # Optional C implementationimport bitstruct
# Pack values into bytes using format string
packed = bitstruct.pack('u1u3u4s16', 1, 2, 3, -4)
print(packed) # b'\xa3\xff\xfc'
# Unpack bytes back to values
unpacked = bitstruct.unpack('u1u3u4s16', packed)
print(unpacked) # (1, 2, 3, -4)
# Calculate the size in bits for a format
size = bitstruct.calcsize('u1u3u4s16')
print(size) # 24 bits
# Use compiled format for repeated operations
cf = bitstruct.compile('u1u3u4s16')
packed = cf.pack(1, 2, 3, -4)
unpacked = cf.unpack(packed)Format strings define the bit layout using type-length combinations:
> (MSB first, default) or < (LSB first)> (big endian, default) or < (little endian) as suffixu - unsigned integers - signed integerf - floating point (16, 32, or 64 bits)b - booleant - text (ASCII or UTF-8)r - raw bytesp - padding with zeros (ignored)P - padding with ones (ignored)Example format strings:
'u1u3u4s16' - 1-bit uint, 3-bit uint, 4-bit uint, 16-bit signed int'<u1u3u4s16<' - Same with LSB bit order and little endian byte order'u5s5f32b1r13t40' - Mixed types including float, boolean, raw, and textConvert Python values to bytes according to format strings.
def pack(fmt: str, *args) -> bytes:
"""Pack values into bytes according to format string."""
def pack_into(fmt: str, buf: bytearray, offset: int, *args, **kwargs) -> None:
"""Pack values into existing buffer at bit offset."""
def pack_dict(fmt: str, names: list[str], data: dict) -> bytes:
"""Pack values from dictionary using field names."""
def pack_into_dict(fmt: str, names: list[str], buf: bytearray, offset: int, data: dict, **kwargs) -> None:
"""Pack dictionary values into buffer at bit offset."""Convert bytes to Python values according to format strings.
def unpack(fmt: str, data: bytes, allow_truncated: bool = False, text_encoding: str = 'utf-8', text_errors: str = 'strict') -> tuple:
"""Unpack bytes according to format string."""
def unpack_from(fmt: str, data: bytes, offset: int = 0, allow_truncated: bool = False, text_encoding: str = 'utf-8', text_errors: str = 'strict') -> tuple:
"""Unpack from bytes starting at bit offset."""
def unpack_dict(fmt: str, names: list[str], data: bytes, allow_truncated: bool = False, text_encoding: str = 'utf-8', text_errors: str = 'strict') -> dict:
"""Unpack bytes to dictionary using field names."""
def unpack_from_dict(fmt: str, names: list[str], data: bytes, offset: int = 0, allow_truncated: bool = False, text_encoding: str = 'utf-8', text_errors: str = 'strict') -> dict:
"""Unpack from bytes to dictionary starting at bit offset."""Additional operations for format analysis and byte manipulation.
def calcsize(fmt: str) -> int:
"""Return the number of bits in given format string."""
def byteswap(fmt: str, data: bytes, offset: int = 0) -> bytes:
"""Swap bytes in data according to format."""
def compile(fmt: str, names: list[str] = None, text_encoding: str = 'utf-8', text_errors: str = 'strict') -> CompiledFormat | CompiledFormatDict:
"""Compile format string for repeated use."""Pre-compiled format objects for efficient repeated operations.
class CompiledFormat:
"""Compiled format string for tuple-based pack/unpack operations."""
def __init__(self, fmt: str, text_encoding: str = 'utf-8', text_errors: str = 'strict'): ...
def pack(self, *args) -> bytes:
"""Pack values using compiled format."""
def unpack(self, data: bytes, allow_truncated: bool = False) -> tuple:
"""Unpack using compiled format."""
def pack_into(self, buf: bytearray, offset: int, *args, **kwargs) -> None:
"""Pack into buffer using compiled format."""
def unpack_from(self, data: bytes, offset: int = 0, allow_truncated: bool = False) -> tuple:
"""Unpack from offset using compiled format."""
def calcsize(self) -> int:
"""Return the number of bits in the compiled format."""
class CompiledFormatDict:
"""Compiled format string for dictionary-based pack/unpack operations."""
def pack(self, data: dict) -> bytes:
"""Pack from dictionary using compiled format."""
def unpack(self, data: bytes, allow_truncated: bool = False) -> dict:
"""Unpack to dictionary using compiled format."""
def pack_into(self, buf: bytearray, offset: int, data: dict, **kwargs) -> None:
"""Pack dictionary into buffer using compiled format."""
def unpack_from(self, data: bytes, offset: int = 0, allow_truncated: bool = False) -> dict:
"""Unpack to dictionary from offset using compiled format."""Package-specific exceptions for error handling.
class Error(Exception):
"""Package-specific exception for bitstruct errors."""import bitstruct
# Pack unsigned int, signed int, float, boolean, raw bytes, and text
packed = bitstruct.pack('u5s5f32b1r13t40', 1, -1, 3.75, True, b'\xff\xff', 'hello')
print(packed) # b'\x0f\xd0\x1c\x00\x00?\xffhello'
# Unpack the same data
unpacked = bitstruct.unpack('u5s5f32b1r13t40', packed)
print(unpacked) # (1, -1, 3.75, True, b'\xff\xf8', 'hello')import bitstruct
# Define field names
names = ['a', 'b', 'c', 'd']
data = {'a': 1, 'b': 2, 'c': 3, 'd': -4}
# Pack from dictionary
packed = bitstruct.pack_dict('u1u3u4s16', names, data)
print(packed) # b'\xa3\xff\xfc'
# Unpack to dictionary
unpacked = bitstruct.unpack_dict('u1u3u4s16', names, packed)
print(unpacked) # {'a': 1, 'b': 2, 'c': 3, 'd': -4}import bitstruct
# Pack into existing buffer at bit offset
data = bytearray(b'\x00\x00\x00\x00')
bitstruct.pack_into('u1u3u4s16', data, 5, 1, 2, 3, -4)
print(data) # bytearray(b'\x05\x1f\xff\xe0')
# Unpack from bit offset
unpacked = bitstruct.unpack_from('u1u3u4s16', data, 5)
print(unpacked) # (1, 2, 3, -4)import bitstruct
# Compile format for repeated use
cf = bitstruct.compile('u1u3u4s16')
# Use compiled format multiple times
packed1 = cf.pack(1, 2, 3, -4)
packed2 = cf.pack(0, 7, 15, 32767)
unpacked1 = cf.unpack(packed1)
unpacked2 = cf.unpack(packed2)
print(f"Size: {cf.calcsize()} bits") # Size: 24 bitsimport bitstruct
# Big endian (default)
packed_be = bitstruct.pack('u1u3u4s16', 1, 2, 3, 1)
# Little endian
packed_le = bitstruct.pack('<u1u3u4s16<', 1, 2, 3, 1)
# Change byte order with byteswap
swapped = bitstruct.byteswap('12', packed_be)
unpacked_swapped = bitstruct.unpack('u1u3u4s16', swapped)
print(unpacked_swapped) # (1, 2, 3, 256)bitstruct modulebitstruct.c module for better performance
import bitstruct.c as bitstructcompile() for repeated operations to avoid re-parsing format stringsThe library raises bitstruct.Error for:
text_encoding and text_errors parameters)__version__: str # Package version string (currently '8.21.0')