Skip to content

Type System

PDC Struct provides fixed-width integer types that ensure precise control over binary serialization. These types guarantee specific byte sizes regardless of platform, making them essential for C interoperability and cross-platform protocols.

Overview

While Python's built-in int type has arbitrary precision, binary protocols and C structs require fixed-width integers. PDC Struct provides these types with built-in validation:

Type Size Range Struct Format
Int8 1 byte -128 to 127 b
UInt8 1 byte 0 to 255 B
Int16 2 bytes -32,768 to 32,767 h
UInt16 2 bytes 0 to 65,535 H

Usage

In StructModel Fields

from pdc_struct import StructModel, StructConfig, StructMode
from pdc_struct.c_types import Int8, UInt8, Int16, UInt16

class SensorReading(StructModel):
    sensor_id: UInt8          # 0-255
    temperature: Int16        # -32768 to 32767 (e.g., hundredths of degrees)
    humidity: UInt8           # 0-255 (percentage)
    status_code: Int8         # -128 to 127

    struct_config = StructConfig(mode=StructMode.C_COMPATIBLE)

# Values are validated on creation
reading = SensorReading(
    sensor_id=42,
    temperature=-1050,  # -10.50 degrees
    humidity=65,
    status_code=-1
)

Direct Instantiation

These types can be used directly and behave like regular integers:

from pdc_struct.c_types import UInt8, Int16

# Create instances
value = UInt8(255)
temp = Int16(-1000)

# Standard integer operations work
result = value + 1  # Returns regular int (256)
doubled = Int16(temp * 2)  # Wrap back in Int16 for validation

# Validation on creation
try:
    bad_value = UInt8(256)  # Raises ValueError
except ValueError as e:
    print(e)  # "UInt8 value must be between 0 and 255"

Comparison with Plain int

When you use plain int in a StructModel, it serializes as a platform-dependent long integer (typically 8 bytes on 64-bit systems). Fixed-width types give you explicit control:

class WithPlainInt(StructModel):
    value: int  # 8 bytes (platform-dependent)

class WithFixedWidth(StructModel):
    value: UInt16  # Always exactly 2 bytes

Validation Behavior

All fixed-width types validate their values at construction time:

from pdc_struct.c_types import Int8, UInt8

# Range validation
UInt8(-1)    # ValueError: UInt8 value must be between 0 and 255
Int8(128)    # ValueError: Int8 value must be between -128 and 127

# Type validation
UInt8("10")  # TypeError: UInt8 requires an integer value
Int16(3.14)  # TypeError: Int16 requires an integer value

Pydantic will also validate these types when used as model fields, providing detailed error messages.

C Equivalent Types

These types correspond to standard C integer types:

PDC Struct C Type stdint.h
Int8 signed char int8_t
UInt8 unsigned char uint8_t
Int16 short int16_t
UInt16 unsigned short uint16_t

Other Supported Types

Beyond fixed-width integers, StructModel supports these Python types automatically:

Python Type Serialization Notes
int 8-byte signed Platform long
float 8-byte double IEEE 754
bool 1 byte 0 or 1
str Null-terminated Requires max_length
bytes Fixed-length Requires max_length
Enum / IntEnum Varies Based on value type
IPv4Address 4 bytes Network byte order
UUID 16 bytes Binary format
StructModel Nested Recursive packing
BitFieldModel 1/2/4 bytes Based on bit_width

Class Reference

Int8

Bases: int

8-bit signed integer (-128 to 127).

Equivalent to C's int8_t or signed char. Serializes to exactly 1 byte. Use for small signed values where memory efficiency matters.

Example

from pdc_struct import StructModel, StructConfig, StructMode from pdc_struct.c_types import Int8

class Temperature(StructModel): ... celsius: Int8 # -128 to 127 degrees ... struct_config = StructConfig(mode=StructMode.C_COMPATIBLE)

reading = Temperature(celsius=-10) len(reading.to_bytes()) 1

Source code in pdc_struct/c_types.py
class Int8(int):
    """8-bit signed integer (-128 to 127).

    Equivalent to C's ``int8_t`` or ``signed char``. Serializes to exactly 1 byte.
    Use for small signed values where memory efficiency matters.

    Example:
        >>> from pdc_struct import StructModel, StructConfig, StructMode
        >>> from pdc_struct.c_types import Int8
        >>>
        >>> class Temperature(StructModel):
        ...     celsius: Int8  # -128 to 127 degrees
        ...     struct_config = StructConfig(mode=StructMode.C_COMPATIBLE)
        >>>
        >>> reading = Temperature(celsius=-10)
        >>> len(reading.to_bytes())
        1
    """

    _min_value: ClassVar[int] = -128
    _max_value: ClassVar[int] = 127
    _struct_format: ClassVar[str] = "b"

    def __new__(cls, value: int):
        if not isinstance(value, (int, Int8)):
            raise TypeError(f"{cls.__name__} requires an integer value")
        if not cls._min_value <= value <= cls._max_value:
            raise ValueError(
                f"{cls.__name__} value must be between {cls._min_value} and {cls._max_value}"
            )
        return super().__new__(cls, value)

    @classmethod
    def __get_pydantic_core_schema__(
        cls,
        _source_type: Any,
        _handler: Any,
    ) -> CoreSchema:
        """Pydantic validation schema."""
        return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

__get_pydantic_core_schema__(_source_type, _handler) classmethod

Pydantic validation schema.

Source code in pdc_struct/c_types.py
@classmethod
def __get_pydantic_core_schema__(
    cls,
    _source_type: Any,
    _handler: Any,
) -> CoreSchema:
    """Pydantic validation schema."""
    return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

UInt8

Bases: int

8-bit unsigned integer (0 to 255).

Equivalent to C's uint8_t or unsigned char. Serializes to exactly 1 byte. Commonly used for byte values, flags, and small counters.

Example

from pdc_struct import StructModel, StructConfig, StructMode from pdc_struct.c_types import UInt8

class Pixel(StructModel): ... r: UInt8 ... g: UInt8 ... b: UInt8 ... struct_config = StructConfig(mode=StructMode.C_COMPATIBLE)

pixel = Pixel(r=255, g=128, b=0) pixel.to_bytes() b'\xff\x80\x00'

Source code in pdc_struct/c_types.py
class UInt8(int):
    """8-bit unsigned integer (0 to 255).

    Equivalent to C's ``uint8_t`` or ``unsigned char``. Serializes to exactly 1 byte.
    Commonly used for byte values, flags, and small counters.

    Example:
        >>> from pdc_struct import StructModel, StructConfig, StructMode
        >>> from pdc_struct.c_types import UInt8
        >>>
        >>> class Pixel(StructModel):
        ...     r: UInt8
        ...     g: UInt8
        ...     b: UInt8
        ...     struct_config = StructConfig(mode=StructMode.C_COMPATIBLE)
        >>>
        >>> pixel = Pixel(r=255, g=128, b=0)
        >>> pixel.to_bytes()
        b'\\xff\\x80\\x00'
    """

    _min_value: ClassVar[int] = 0
    _max_value: ClassVar[int] = 255
    _struct_format: ClassVar[str] = "B"

    def __new__(cls, value: int):
        if not isinstance(value, (int, UInt8)):
            raise TypeError(f"{cls.__name__} requires an integer value")
        if not cls._min_value <= value <= cls._max_value:
            raise ValueError(
                f"{cls.__name__} value must be between {cls._min_value} and {cls._max_value}"
            )
        return super().__new__(cls, value)

    @classmethod
    def __get_pydantic_core_schema__(
        cls,
        _source_type: Any,
        _handler: Any,
    ) -> CoreSchema:
        """Pydantic validation schema."""
        return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

__get_pydantic_core_schema__(_source_type, _handler) classmethod

Pydantic validation schema.

Source code in pdc_struct/c_types.py
@classmethod
def __get_pydantic_core_schema__(
    cls,
    _source_type: Any,
    _handler: Any,
) -> CoreSchema:
    """Pydantic validation schema."""
    return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

Int16

Bases: int

16-bit signed integer (-32,768 to 32,767).

Equivalent to C's int16_t or short. Serializes to exactly 2 bytes. Use for medium-range signed values like audio samples or relative coordinates.

Example

from pdc_struct import StructModel, StructConfig, StructMode, ByteOrder from pdc_struct.c_types import Int16

class AudioSample(StructModel): ... left: Int16 ... right: Int16 ... struct_config = StructConfig( ... mode=StructMode.C_COMPATIBLE, ... byte_order=ByteOrder.LITTLE_ENDIAN ... )

sample = AudioSample(left=-16384, right=16383) len(sample.to_bytes()) 4

Source code in pdc_struct/c_types.py
class Int16(int):
    """16-bit signed integer (-32,768 to 32,767).

    Equivalent to C's ``int16_t`` or ``short``. Serializes to exactly 2 bytes.
    Use for medium-range signed values like audio samples or relative coordinates.

    Example:
        >>> from pdc_struct import StructModel, StructConfig, StructMode, ByteOrder
        >>> from pdc_struct.c_types import Int16
        >>>
        >>> class AudioSample(StructModel):
        ...     left: Int16
        ...     right: Int16
        ...     struct_config = StructConfig(
        ...         mode=StructMode.C_COMPATIBLE,
        ...         byte_order=ByteOrder.LITTLE_ENDIAN
        ...     )
        >>>
        >>> sample = AudioSample(left=-16384, right=16383)
        >>> len(sample.to_bytes())
        4
    """

    _min_value: ClassVar[int] = -32768
    _max_value: ClassVar[int] = 32767
    _struct_format: ClassVar[str] = "h"

    def __new__(cls, value: int):
        if not isinstance(value, (int, Int16)):
            raise TypeError(f"{cls.__name__} requires an integer value")
        if not cls._min_value <= value <= cls._max_value:
            raise ValueError(
                f"{cls.__name__} value must be between {cls._min_value} and {cls._max_value}"
            )
        return super().__new__(cls, value)

    @classmethod
    def __get_pydantic_core_schema__(
        cls,
        _source_type: Any,
        _handler: Any,
    ) -> CoreSchema:
        """Pydantic validation schema."""
        return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

__get_pydantic_core_schema__(_source_type, _handler) classmethod

Pydantic validation schema.

Source code in pdc_struct/c_types.py
@classmethod
def __get_pydantic_core_schema__(
    cls,
    _source_type: Any,
    _handler: Any,
) -> CoreSchema:
    """Pydantic validation schema."""
    return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

UInt16

Bases: int

16-bit unsigned integer (0 to 65,535).

Equivalent to C's uint16_t or unsigned short. Serializes to exactly 2 bytes. Commonly used for port numbers, lengths, and medium-range counters.

Example

from pdc_struct import StructModel, StructConfig, StructMode, ByteOrder from pdc_struct.c_types import UInt16

class NetworkHeader(StructModel): ... source_port: UInt16 ... dest_port: UInt16 ... length: UInt16 ... struct_config = StructConfig( ... mode=StructMode.C_COMPATIBLE, ... byte_order=ByteOrder.BIG_ENDIAN # Network byte order ... )

header = NetworkHeader(source_port=8080, dest_port=443, length=100) header.to_bytes() b'\x1f\x90\x01\xbb\x00d'

Source code in pdc_struct/c_types.py
class UInt16(int):
    """16-bit unsigned integer (0 to 65,535).

    Equivalent to C's ``uint16_t`` or ``unsigned short``. Serializes to exactly 2 bytes.
    Commonly used for port numbers, lengths, and medium-range counters.

    Example:
        >>> from pdc_struct import StructModel, StructConfig, StructMode, ByteOrder
        >>> from pdc_struct.c_types import UInt16
        >>>
        >>> class NetworkHeader(StructModel):
        ...     source_port: UInt16
        ...     dest_port: UInt16
        ...     length: UInt16
        ...     struct_config = StructConfig(
        ...         mode=StructMode.C_COMPATIBLE,
        ...         byte_order=ByteOrder.BIG_ENDIAN  # Network byte order
        ...     )
        >>>
        >>> header = NetworkHeader(source_port=8080, dest_port=443, length=100)
        >>> header.to_bytes()
        b'\\x1f\\x90\\x01\\xbb\\x00d'
    """

    _min_value: ClassVar[int] = 0
    _max_value: ClassVar[int] = 65535
    _struct_format: ClassVar[str] = "H"

    def __new__(cls, value: int):
        if not isinstance(value, (int, UInt16)):
            raise TypeError(f"{cls.__name__} requires an integer value")
        if not cls._min_value <= value <= cls._max_value:
            raise ValueError(
                f"{cls.__name__} value must be between {cls._min_value} and {cls._max_value}"
            )
        return super().__new__(cls, value)

    @classmethod
    def __get_pydantic_core_schema__(
        cls,
        _source_type: Any,
        _handler: Any,
    ) -> CoreSchema:
        """Pydantic validation schema."""
        return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

__get_pydantic_core_schema__(_source_type, _handler) classmethod

Pydantic validation schema.

Source code in pdc_struct/c_types.py
@classmethod
def __get_pydantic_core_schema__(
    cls,
    _source_type: Any,
    _handler: Any,
) -> CoreSchema:
    """Pydantic validation schema."""
    return core_schema.int_schema(ge=cls._min_value, le=cls._max_value)

See Also