Claude Code for struct: Binary Data Packing in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for struct: Binary Data Packing in Python
AI

Claude Code for struct: Binary Data Packing in Python

Published: July 19, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s struct module packs and unpacks C-style binary data. import struct. pack: struct.pack(">HI", 1, 256) → bytes. unpack: struct.unpack(">HI", data) → tuple. pack_into: struct.pack_into(fmt, buf, offset, *values) — write into bytearray. unpack_from: struct.unpack_from(fmt, buf, offset=0) — read from buffer. calcsize: struct.calcsize(">HI") → 6. Struct: s = struct.Struct(">HI"); s.pack(1, 256); s.unpack(data) — compiled, reusable. iter_unpack: for vals in struct.iter_unpack(">H", data): — stream records. Byte order: > big-endian (network); < little-endian; = native; ! network (= big). Format chars: B uint8, b int8, H uint16, h int16, I uint32, i int32, Q uint64, q int64, f float32, d float64, ? bool, c char (1 byte), s string bytes (e.g. 4s), x padding byte, p pascal string. struct.error: raised on size mismatch or bad format. peek via unpack_from with offset. Checksum: pack CRC after data: struct.pack(">I", zlib.crc32(data) & 0xFFFFFFFF). Claude Code generates binary protocol parsers, file format readers, network frame decoders, and BLE/serial packet builders.

CLAUDE.md for struct

## struct Stack
- Stdlib: import struct
- Pack:   struct.pack(">BHI", uint8, uint16, uint32)  — > = big-endian
- Unpack: struct.unpack(">BHI", data)  — returns tuple
- Reuse:  s = struct.Struct(fmt); s.pack(*values); s.unpack(data)
- Size:   struct.calcsize(fmt)  — bytes needed
- Stream: for (val,) in struct.iter_unpack(">H", data):
- Offset: struct.unpack_from(fmt, buf, offset=n)

struct Binary Protocol Pipeline

# app/binutil.py — pack, unpack, Struct, protocols, file formats, checksums
from __future__ import annotations

import io
import struct
import zlib
from dataclasses import dataclass
from typing import Any, BinaryIO, Iterator


# ─────────────────────────────────────────────────────────────────────────────
# 1. Low-level pack/unpack helpers
# ─────────────────────────────────────────────────────────────────────────────

def pack_uint8(value: int) -> bytes:
    """Pack a single unsigned byte."""
    return struct.pack("B", value)


def pack_uint16_be(value: int) -> bytes:
    """Pack an unsigned 16-bit integer, big-endian."""
    return struct.pack(">H", value)


def pack_uint32_be(value: int) -> bytes:
    """Pack an unsigned 32-bit integer, big-endian."""
    return struct.pack(">I", value)


def pack_float32_be(value: float) -> bytes:
    """Pack a 32-bit float, big-endian."""
    return struct.pack(">f", value)


def unpack_uint16_be(data: bytes, offset: int = 0) -> int:
    """Unpack a big-endian uint16 from data at offset."""
    (val,) = struct.unpack_from(">H", data, offset)
    return val


def unpack_uint32_be(data: bytes, offset: int = 0) -> int:
    """Unpack a big-endian uint32 from data at offset."""
    (val,) = struct.unpack_from(">I", data, offset)
    return val


def read_cstring(data: bytes, offset: int = 0) -> tuple[str, int]:
    """
    Read a null-terminated C string from data at offset.
    Returns (string, new_offset).

    Example:
        s, end = read_cstring(b"hello\x00world\x00", 0)  # ("hello", 6)
    """
    end = data.index(b"\x00", offset)
    return data[offset:end].decode("utf-8", errors="replace"), end + 1


# ─────────────────────────────────────────────────────────────────────────────
# 2. Reusable Struct objects
# ─────────────────────────────────────────────────────────────────────────────

# Common network/binary primitives
UINT8_S  = struct.Struct("B")
UINT16_S = struct.Struct(">H")
UINT32_S = struct.Struct(">I")
UINT64_S = struct.Struct(">Q")
FLOAT32_S = struct.Struct(">f")
FLOAT64_S = struct.Struct(">d")


def iter_uint16_be(data: bytes) -> Iterator[int]:
    """
    Iterate over big-endian uint16 values packed densely in data.

    Example:
        values = list(iter_uint16_be(b"\\x00\\x01\\x00\\x02\\x00\\x03"))  # [1, 2, 3]
    """
    for (val,) in struct.iter_unpack(">H", data):
        yield val


def iter_uint32_be(data: bytes) -> Iterator[int]:
    """Iterate over big-endian uint32 dense array."""
    for (val,) in struct.iter_unpack(">I", data):
        yield val


def pack_array(values: list[int | float], fmt_char: str = "H", order: str = ">") -> bytes:
    """
    Pack a list of same-type values as a dense binary array.

    Example:
        packet = pack_array([100, 200, 300], fmt_char="H", order=">")
    """
    n = len(values)
    return struct.pack(f"{order}{n}{fmt_char}", *values)


def unpack_array(data: bytes, fmt_char: str = "H", order: str = ">") -> list:
    """
    Unpack a dense binary array of same-type values.

    Example:
        values = unpack_array(data, fmt_char="H", order=">")
    """
    size  = struct.calcsize(fmt_char)
    n     = len(data) // size
    return list(struct.unpack(f"{order}{n}{fmt_char}", data[:n * size]))


# ─────────────────────────────────────────────────────────────────────────────
# 3. Framed binary protocol
# ─────────────────────────────────────────────────────────────────────────────

# Frame layout:  [magic: 4s][version: B][msg_type: B][length: I][payload: Ns][crc32: I]
FRAME_HEADER = struct.Struct(">4sBBI")
FRAME_CRC    = struct.Struct(">I")
FRAME_MAGIC  = b"MYPR"  # my protocol


@dataclass
class Frame:
    """
    A binary protocol frame.

    Example:
        frame = Frame(version=1, msg_type=2, payload=b'{"event":"click"}')
        wire  = Frame.encode(frame)
        received = Frame.decode(wire)
    """
    version:  int
    msg_type: int
    payload:  bytes

    @staticmethod
    def encode(frame: "Frame") -> bytes:
        """
        Serialize frame to wire bytes.

        Example:
            wire = Frame.encode(Frame(version=1, msg_type=0x01, payload=b"ping"))
        """
        hdr  = FRAME_HEADER.pack(FRAME_MAGIC, frame.version, frame.msg_type, len(frame.payload))
        body = hdr + frame.payload
        crc  = FRAME_CRC.pack(zlib.crc32(body) & 0xFFFFFFFF)
        return body + crc

    @staticmethod
    def decode(data: bytes) -> "Frame":
        """
        Deserialize a frame from bytes; raises ValueError on CRC mismatch.

        Example:
            frame = Frame.decode(wire_bytes)
        """
        hdr_size = FRAME_HEADER.size
        magic, version, msg_type, length = FRAME_HEADER.unpack_from(data, 0)
        if magic != FRAME_MAGIC:
            raise ValueError(f"Bad magic: {magic!r}")
        payload  = data[hdr_size: hdr_size + length]
        crc_off  = hdr_size + length
        (received_crc,) = FRAME_CRC.unpack_from(data, crc_off)
        computed_crc    = zlib.crc32(data[:crc_off]) & 0xFFFFFFFF
        if received_crc != computed_crc:
            raise ValueError(f"CRC mismatch: {received_crc:#010x} != {computed_crc:#010x}")
        return Frame(version=version, msg_type=msg_type, payload=payload)

    @staticmethod
    def read_from(stream: BinaryIO) -> "Frame":
        """
        Read one frame from a binary stream.

        Example:
            with open("capture.bin", "rb") as f:
                while True:
                    try: frame = Frame.read_from(f)
                    except EOFError: break
        """
        hdr_bytes = stream.read(FRAME_HEADER.size)
        if len(hdr_bytes) < FRAME_HEADER.size:
            raise EOFError("End of stream")
        magic, version, msg_type, length = FRAME_HEADER.unpack(hdr_bytes)
        payload   = stream.read(length)
        crc_bytes = stream.read(FRAME_CRC.size)
        (received_crc,) = FRAME_CRC.unpack(crc_bytes)
        body    = hdr_bytes + payload
        computed = zlib.crc32(body) & 0xFFFFFFFF
        if received_crc != computed:
            raise ValueError("CRC mismatch")
        return Frame(version=version, msg_type=msg_type, payload=payload)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Simple binary file format reader
# ─────────────────────────────────────────────────────────────────────────────

# WAV-lite: simplified RIFF-like PCM reader (fixed format, mono, 16-bit)
WAV_HEADER   = struct.Struct("<4sI4s4sIHHIIHH4sI")  # RIFF/WAVE/fmt /data headers
WAV_EXPECTED = b"RIFF", b"WAVE"


@dataclass
class PCMInfo:
    num_channels:    int
    sample_rate:     int
    bits_per_sample: int
    num_frames:      int
    duration_s:      float


def parse_wav_header(data: bytes) -> PCMInfo:
    """
    Parse a minimal WAV header (PCM, any channel count).
    Raises ValueError on invalid format.

    Example:
        with open("audio.wav", "rb") as f:
            info = parse_wav_header(f.read(44))
    """
    if len(data) < WAV_HEADER.size:
        raise ValueError("Too short for WAV header")
    fields = WAV_HEADER.unpack_from(data, 0)
    riff_id, _, wave_id, fmt_id, _, audio_fmt, channels, rate, _, _, bps, data_id, data_size = fields
    if riff_id != b"RIFF" or wave_id != b"WAVE":
        raise ValueError(f"Not a RIFF/WAVE: {riff_id!r}/{wave_id!r}")
    if audio_fmt != 1:
        raise ValueError(f"Only PCM (format 1) supported, got {audio_fmt}")
    frame_size  = channels * (bps // 8)
    num_frames  = data_size // frame_size if frame_size else 0
    return PCMInfo(
        num_channels=channels,
        sample_rate=rate,
        bits_per_sample=bps,
        num_frames=num_frames,
        duration_s=num_frames / rate if rate else 0.0,
    )


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== struct demo ===")

    print("\n--- pack / unpack round-trip ---")
    fmt  = ">BHIf"
    size = struct.calcsize(fmt)
    vals = (7, 1000, 99999, 3.14)
    packed = struct.pack(fmt, *vals)
    print(f"  values: {vals}{len(packed)} bytes: {packed.hex()}")
    unpacked = struct.unpack(fmt, packed)
    print(f"  unpacked: {unpacked}")
    print(f"  float eq: {abs(unpacked[3] - 3.14) < 0.001}")

    print("\n--- pack_array / unpack_array ---")
    sensor_data = [100, 200, 150, 300, 250]
    wire = pack_array(sensor_data, fmt_char="H")
    print(f"  packed {len(sensor_data)} uint16: {wire.hex()}")
    back = unpack_array(wire, fmt_char="H")
    print(f"  unpacked: {back}")

    print("\n--- iter_uint16_be ---")
    counts = list(iter_uint16_be(wire))
    print(f"  iter_uint16_be: {counts}")

    print("\n--- Frame encode / decode ---")
    frame = Frame(version=1, msg_type=0x01, payload=b'{"event":"ping"}')
    wire_frame = Frame.encode(frame)
    print(f"  encoded: {len(wire_frame)} bytes, hex[:20]={wire_frame[:20].hex()}")
    decoded = Frame.decode(wire_frame)
    print(f"  decoded: version={decoded.version} type={decoded.msg_type} payload={decoded.payload!r}")

    print("\n--- Frame CRC tamper detection ---")
    tampered = bytearray(wire_frame)
    tampered[10] ^= 0xFF
    try:
        Frame.decode(bytes(tampered))
    except ValueError as e:
        print(f"  caught: {e}")

    print("\n--- Frame.read_from stream ---")
    buf = io.BytesIO()
    for msg_type, payload in [(1, b"ping"), (2, b"data"), (3, b"bye")]:
        buf.write(Frame.encode(Frame(version=1, msg_type=msg_type, payload=payload)))
    buf.seek(0)
    while True:
        try:
            f = Frame.read_from(buf)
            print(f"  read frame type={f.msg_type} payload={f.payload!r}")
        except EOFError:
            break

    print("\n--- read_cstring ---")
    raw = b"hello\x00world\x00!"
    s1, off = read_cstring(raw, 0)
    s2, _   = read_cstring(raw, off)
    print(f"  strings: {s1!r}, {s2!r}")

    print("\n--- WAV header parse ---")
    # Synthesize a minimal valid 44-byte WAV header (mono, 44100 Hz, 16-bit, 100 frames)
    data_size = 100 * 1 * 2  # frames * channels * bytes_per_sample
    wav_hdr = struct.pack(
        "<4sI4s4sIHHIIHH4sI",
        b"RIFF", 36 + data_size, b"WAVE",
        b"fmt ", 16, 1, 1, 44100, 44100*2, 2, 16,
        b"data", data_size,
    )
    info = parse_wav_header(wav_hdr)
    print(f"  {info}")

    print("\n=== done ===")

For the construct alternative — construct (PyPI, claude-code-construct) provides a declarative DSL for describing binary formats using Python objects: Struct("header" / Bytes(4), "length" / Int32ub) — it auto-generates parsers and builders and handles nested structures, arrays, conditional fields, and checksums; stdlib struct requires manual format strings and offsetting — use construct for complex multi-layered binary protocols (TLS records, ELF sections, custom filesystems) where documenting the format as code is valuable, stdlib struct for simple fixed-layout records, performance-critical inner loops, and zero-dependency environments. For the ctypes alternative — ctypes.Structure maps C struct layouts including bitfields, pointers, and platform-aligned types, and interoperates directly with C shared libraries via ctypes.CDLL; stdlib struct is pure Python with no C interop — use ctypes.Structure when calling native C libraries, interfacing with OS APIs (ioctl, Windows WINAPI), or needing C bitfield semantics, stdlib struct for portable binary file I/O and network protocols where C interop is not required. The Claude Skills 360 bundle includes struct skill sets covering pack_uint8/16/32/float helpers, iter_uint16_be/iter_uint32_be streaming unpacking, pack_array()/unpack_array() dense array helpers, Frame dataclass with encode()/decode()/read_from() binary protocol implementation with CRC32 validation, and parse_wav_header() example file format reader. Start with the free tier to try binary protocol parsing and struct pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free