BIG_ENDIAN = '>' module-attribute

Big-endian, std. size & alignment

LITTLE_ENDIAN = '<' module-attribute

Little-endian, std. size & alignment

NATIVE_ORDER = '@' module-attribute

Native order, size & alignment

CEnum

Bases: AbstractCEnum

Source code in cstruct/cenum.py
4
5
6
7
8
class CEnum(AbstractCEnum):
    @classmethod
    def sizeof(cls) -> int:
        "Type size (in bytes)"
        return cls.__size__

sizeof() classmethod

Type size (in bytes)

cstruct/cenum.py
5
6
7
8
@classmethod
def sizeof(cls) -> int:
    "Type size (in bytes)"
    return cls.__size__

CStruct

Bases: AbstractCStruct

Convert C struct definitions into Python classes.

Attributes:
  • __struct__ (str) –

    definition of the struct (or union) in C syntax

  • __byte_order__ (str) –

    byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER

  • __is_union__ (bool) –

    True for union definitions, False for struct definitions

  • __size__ (int) –

    size of the structure in bytes (flexible array member size is omitted)

  • __fields__ (list) –

    list of structure fields

  • __fields_types__ (dict) –

    dictionary mapping field names to types

Source code in cstruct/cstruct.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class CStruct(AbstractCStruct):
    """
    Convert C struct definitions into Python classes.

    Attributes:
        __struct__ (str): definition of the struct (or union) in C syntax
        __byte_order__ (str): byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
        __is_union__ (bool): True for union definitions, False for struct definitions
        __size__ (int): size of the structure in bytes (flexible array member size is omitted)
        __fields__ (list): list of structure fields
        __fields_types__ (dict): dictionary mapping field names to types
    """

    def unpack_from(self, buffer: Optional[bytes], offset: int = 0, flexible_array_length: Optional[int] = None) -> bool:
        """
        Unpack bytes containing packed C structure data

        Args:
            buffer: bytes to be unpacked
            offset: optional buffer offset
            flexible_array_length: optional flexible array lenght (number of elements)
        """
        self.set_flexible_array_length(flexible_array_length)
        if buffer is None:
            buffer = CHAR_ZERO * self.size
        for field, field_type in self.__fields_types__.items():
            setattr(self, field, field_type.unpack_from(buffer, offset))
        return True

    def pack(self) -> bytes:
        """
        Pack the structure data into bytes

        Returns:
            bytes: The packed structure
        """
        result: List[bytes] = []
        for field, field_type in self.__fields_types__.items():
            if field_type.is_struct or field_type.is_union:
                if field_type.vlen == 1:  # single struct
                    v = getattr(self, field, field_type.ref())
                    v = v.pack()
                    result.append(v)
                else:  # multiple struct
                    values = getattr(self, field, [])
                    for j in range(0, field_type.vlen):
                        try:
                            v = values[j]
                        except KeyError:
                            v = field_type.ref()
                        v = v.pack()
                        result.append(v)
            else:
                data = getattr(self, field)
                result.append(field_type.pack(data))
        return bytes().join(result)

pack()

Pack the structure data into bytes

Returns:
  • bytes( bytes ) –

    The packed structure

cstruct/cstruct.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def pack(self) -> bytes:
    """
    Pack the structure data into bytes

    Returns:
        bytes: The packed structure
    """
    result: List[bytes] = []
    for field, field_type in self.__fields_types__.items():
        if field_type.is_struct or field_type.is_union:
            if field_type.vlen == 1:  # single struct
                v = getattr(self, field, field_type.ref())
                v = v.pack()
                result.append(v)
            else:  # multiple struct
                values = getattr(self, field, [])
                for j in range(0, field_type.vlen):
                    try:
                        v = values[j]
                    except KeyError:
                        v = field_type.ref()
                    v = v.pack()
                    result.append(v)
        else:
            data = getattr(self, field)
            result.append(field_type.pack(data))
    return bytes().join(result)

unpack_from(buffer, offset=0, flexible_array_length=None)

Unpack bytes containing packed C structure data

Parameters:
  • buffer (Optional[bytes]) –

    bytes to be unpacked

  • offset (int, default: 0 ) –

    optional buffer offset

  • flexible_array_length (Optional[int], default: None ) –

    optional flexible array lenght (number of elements)

cstruct/cstruct.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def unpack_from(self, buffer: Optional[bytes], offset: int = 0, flexible_array_length: Optional[int] = None) -> bool:
    """
    Unpack bytes containing packed C structure data

    Args:
        buffer: bytes to be unpacked
        offset: optional buffer offset
        flexible_array_length: optional flexible array lenght (number of elements)
    """
    self.set_flexible_array_length(flexible_array_length)
    if buffer is None:
        buffer = CHAR_ZERO * self.size
    for field, field_type in self.__fields_types__.items():
        setattr(self, field, field_type.unpack_from(buffer, offset))
    return True

MemCStruct

Bases: AbstractCStruct

Convert C struct definitions into Python classes.

Attributes:
  • __struct__ (str) –

    definition of the struct (or union) in C syntax

  • __byte_order__ (str) –

    byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER

  • __is_union__ (bool) –

    True for union definitions, False for struct definitions

  • __mem__

    mutable character buffer

  • __size__ (int) –

    size of the structure in bytes (flexible array member size is omitted)

  • __fields__ (list) –

    list of structure fields

  • __fields_types__ (dict) –

    dictionary mapping field names to types

Source code in cstruct/mem_cstruct.py
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
class MemCStruct(AbstractCStruct):
    """
    Convert C struct definitions into Python classes.

    Attributes:
        __struct__ (str): definition of the struct (or union) in C syntax
        __byte_order__ (str): byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
        __is_union__ (bool): True for union definitions, False for struct definitions
        __mem__: mutable character buffer
        __size__ (int): size of the structure in bytes (flexible array member size is omitted)
        __fields__ (list): list of structure fields
        __fields_types__ (dict): dictionary mapping field names to types
    """

    __mem__ = None
    __base__ = 0

    def unpack_from(self, buffer: Optional[bytes], offset: int = 0, flexible_array_length: Optional[int] = None) -> bool:
        """
        Unpack bytes containing packed C structure data

        Args:
            buffer: bytes to be unpacked
            offset: optional buffer offset
            flexible_array_length: optional flexible array lenght (number of elements)
        """
        self.set_flexible_array_length(flexible_array_length)
        self.__base__ = offset  # Base offset
        if buffer is None:
            # the buffer is one item larger than its size and the last element is NUL
            self.__mem__ = ctypes.create_string_buffer(self.size + 1)
        elif isinstance(buffer, ctypes.Array):
            self.__mem__ = buffer
        elif isinstance(buffer, int):
            # buffer is a pointer
            self.__mem__ = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_char * self.size)).contents
        else:
            self.__mem__ = ctypes.create_string_buffer(buffer)
        for field, field_type in self.__fields_types__.items():
            if field_type.is_struct or field_type.is_union:
                setattr(self, field, field_type.unpack_from(self.__mem__, offset))
        return True

    def memcpy(self, destination: int, source: bytes, num: int) -> None:
        """
        Copies the values of num bytes from source to the struct memory

        Args:
            destination: destination address
            source: source data to be copied
            num: number of bytes to copy
        """
        ctypes.memmove(ctypes.byref(self.__mem__, destination), source, num)

    def pack(self) -> bytes:
        """
        Pack the structure data into bytes

        Returns:
            bytes: The packed structure
        """
        return self.__mem__.raw[self.__base__ : self.__base__ + self.size]

    def set_flexible_array_length(self, flexible_array_length: Optional[int]) -> None:
        """
        Set flexible array length (i.e. number of elements)

        Args:
            flexible_array_length: flexible array length
        """
        super().set_flexible_array_length(flexible_array_length)
        if self.__mem__ is not None:
            try:
                ctypes.resize(self.__mem__, self.size + 1)
            except ValueError:
                pass

    def __getattr__(self, attr: str) -> Any:
        field_type = self.__fields_types__[attr]
        result = field_type.unpack_from(self.__mem__, self.__base__)
        if isinstance(result, list):
            return CStructList(result, name=attr, parent=self)
        else:
            return result

    def __setattr__(self, attr: str, value: Any) -> None:
        field_type = self.__fields_types__.get(attr)
        if field_type is None:
            object.__setattr__(self, attr, value)
        elif field_type.is_struct or field_type.is_union:
            object.__setattr__(self, attr, value)
        else:  # native
            if field_type.flexible_array and len(value) != field_type.vlen:
                # flexible array size changed, resize the buffer
                field_type.vlen = len(value)
                ctypes.resize(self.__mem__, self.size + 1)
            addr = field_type.offset + self.__base__
            self.memcpy(addr, field_type.pack(value), field_type.vsize)

    def on_change_list(self, attr: str, key: int, value: Any) -> None:
        field_type = self.__fields_types__[attr]
        # Calculate the single field format and size
        fmt = (self.__byte_order__ + field_type.fmt[-1]) if self.__byte_order__ is not None else field_type.fmt[-1]
        size = struct.calcsize(fmt)
        # Calculate the single field memory position
        addr = field_type.offset + self.__base__ + size * key
        # Update the memory
        self.memcpy(addr, struct.pack(fmt, value), size)

memcpy(destination, source, num)

Copies the values of num bytes from source to the struct memory

Parameters:
  • destination (int) –

    destination address

  • source (bytes) –

    source data to be copied

  • num (int) –

    number of bytes to copy

cstruct/mem_cstruct.py
88
89
90
91
92
93
94
95
96
97
def memcpy(self, destination: int, source: bytes, num: int) -> None:
    """
    Copies the values of num bytes from source to the struct memory

    Args:
        destination: destination address
        source: source data to be copied
        num: number of bytes to copy
    """
    ctypes.memmove(ctypes.byref(self.__mem__, destination), source, num)

pack()

Pack the structure data into bytes

Returns:
  • bytes( bytes ) –

    The packed structure

cstruct/mem_cstruct.py
 99
100
101
102
103
104
105
106
def pack(self) -> bytes:
    """
    Pack the structure data into bytes

    Returns:
        bytes: The packed structure
    """
    return self.__mem__.raw[self.__base__ : self.__base__ + self.size]

set_flexible_array_length(flexible_array_length)

Set flexible array length (i.e. number of elements)

Parameters:
  • flexible_array_length (Optional[int]) –

    flexible array length

cstruct/mem_cstruct.py
108
109
110
111
112
113
114
115
116
117
118
119
120
def set_flexible_array_length(self, flexible_array_length: Optional[int]) -> None:
    """
    Set flexible array length (i.e. number of elements)

    Args:
        flexible_array_length: flexible array length
    """
    super().set_flexible_array_length(flexible_array_length)
    if self.__mem__ is not None:
        try:
            ctypes.resize(self.__mem__, self.size + 1)
        except ValueError:
            pass

unpack_from(buffer, offset=0, flexible_array_length=None)

Unpack bytes containing packed C structure data

Parameters:
  • buffer (Optional[bytes]) –

    bytes to be unpacked

  • offset (int, default: 0 ) –

    optional buffer offset

  • flexible_array_length (Optional[int], default: None ) –

    optional flexible array lenght (number of elements)

cstruct/mem_cstruct.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def unpack_from(self, buffer: Optional[bytes], offset: int = 0, flexible_array_length: Optional[int] = None) -> bool:
    """
    Unpack bytes containing packed C structure data

    Args:
        buffer: bytes to be unpacked
        offset: optional buffer offset
        flexible_array_length: optional flexible array lenght (number of elements)
    """
    self.set_flexible_array_length(flexible_array_length)
    self.__base__ = offset  # Base offset
    if buffer is None:
        # the buffer is one item larger than its size and the last element is NUL
        self.__mem__ = ctypes.create_string_buffer(self.size + 1)
    elif isinstance(buffer, ctypes.Array):
        self.__mem__ = buffer
    elif isinstance(buffer, int):
        # buffer is a pointer
        self.__mem__ = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_char * self.size)).contents
    else:
        self.__mem__ = ctypes.create_string_buffer(buffer)
    for field, field_type in self.__fields_types__.items():
        if field_type.is_struct or field_type.is_union:
            setattr(self, field, field_type.unpack_from(self.__mem__, offset))
    return True

define(key, value)

Define a constant that can be used in the C struct

>>> define("INIT_THREAD_SIZE", 16384)
Parameters:
  • key (str) –

    identifier

  • value (Any) –

    value of the constant

cstruct/__init__.py
67
68
69
70
71
72
73
74
75
76
77
78
def define(key: str, value: Any) -> None:
    """
    Define a constant that can be used in the C struct

    Examples:
        >>> define("INIT_THREAD_SIZE", 16384)

    Args:
        key: identifier
        value: value of the constant
    """
    DEFINES[key] = value

get_type(type_)

Get a data type (struct, union, enum) by name

>>> get_type("struct Position")
<class 'abc.Position'>
>>> get_type("enum htmlfont")
<enum 'htmlfont'>
Parameters:
  • type_ (str) –

    C type, struct or union (e.g. 'short int' or 'struct ZYZ'), enum or native type

Returns:
  • class( Any ) –

    data type class

Raises:
  • KeyError

    If type is not defined

cstruct/__init__.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def get_type(type_: str) -> Any:
    """
    Get a data type (struct, union, enum) by name

    Examples:
        >>> get_type("struct Position")
        <class 'abc.Position'>
        >>> get_type("enum htmlfont")
        <enum 'htmlfont'>

    Args:
        type_: C type, struct or union (e.g. 'short int' or 'struct ZYZ'), enum or native type

    Returns:
        class: data type class

    Raises:
        KeyError: If type is not defined
    """
    while type_ in TYPEDEFS:
        type_ = TYPEDEFS[type_]
    if isinstance(type_, CStructMeta):
        return type_
    elif type_.startswith("struct ") or type_.startswith("union "):
        kind, type_ = type_.split(" ", 1)
        try:
            return STRUCTS[type_]
        except KeyError:
            raise KeyError(f"Unknown {kind} `{type_}`")
    elif type_.startswith("enum "):
        kind, type_ = type_.split(" ", 1)
        try:
            return ENUMS[type_]
        except KeyError:
            raise KeyError(f"Unknown {kind} `{type_}`")
    else:
        return get_native_type(type_)

getdef(key)

Return the value for a constant

>>> define("INIT_THREAD_SIZE", 16384)
>>> getdef("INIT_THREAD_SIZE")
Parameters:
  • key (str) –

    identifier

Raises:
  • KeyError

    If key is not defined

cstruct/__init__.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def getdef(key: str) -> Any:
    """
    Return the value for a constant

    Examples:
        >>> define("INIT_THREAD_SIZE", 16384)
        >>> getdef("INIT_THREAD_SIZE")

    Args:
        key: identifier

    Raises:
        KeyError: If key is not defined
    """
    return DEFINES[key]

parse(__struct__, __cls__=None, **kargs)

Return a new class mapping a C struct/union/enum definition. If the string does not contains any definition, return None. If the string contains multiple struct/union/enum definitions, returns the last definition.

>>> cstruct.parse('struct Pair { unsigned char a; unsigned char b; };')
<class 'abc.Pair'>
Parameters:
  • __struct__ (str) –

    definition of the struct (or union/enum) in C syntax

  • __cls__ (type, default: None ) –

    super class - CStruct(default) or MemCStruct

  • __byte_order__ (str) –

    byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER

Returns:
Raises:
  • ParserError

    Parsing exception

cstruct/__init__.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def parse(
    __struct__: str, __cls__: Optional[Type[AbstractCStruct]] = None, **kargs: Dict[str, Any]
) -> Union[Type[AbstractCStruct], Type[AbstractCEnum], None]:
    """
    Return a new class mapping a C struct/union/enum definition.
    If the string does not contains any definition, return None.
    If the string contains multiple struct/union/enum definitions, returns the last definition.

    Examples:
        >>> cstruct.parse('struct Pair { unsigned char a; unsigned char b; };')
        <class 'abc.Pair'>

    Args:
        __struct__ (str): definition of the struct (or union/enum) in C syntax
        __cls__ (type): super class - CStruct(default) or MemCStruct
        __byte_order__ (str): byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER

    Returns:
        cls: __cls__ subclass

    Raises:
        cstruct.exceptions.ParserError: Parsing exception

    """
    if __cls__ is None:
        __cls__ = MemCStruct
    cls_def = parse_struct_def(__struct__, __cls__=__cls__, process_muliple_definition=True, **kargs)
    if cls_def is None:
        return None
    return cls_def["__cls__"].parse(cls_def, **kargs)

sizeof(type_)

Return the size of the type.

>>> sizeof("struct Position")
16
>>> sizeof('enum htmlfont')
4
>>> sizeof("int")
4
Parameters:
  • type_ (str) –

    C type, struct or union (e.g. 'short int' or 'struct ZYZ'), enum or native type

Returns:
  • size( int ) –

    size in bytes

Raises:
  • KeyError

    If type is not defined

cstruct/__init__.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def sizeof(type_: str) -> int:
    """
    Return the size of the type.

    Examples:
        >>> sizeof("struct Position")
        16
        >>> sizeof('enum htmlfont')
        4
        >>> sizeof("int")
        4

    Args:
        type_: C type, struct or union (e.g. 'short int' or 'struct ZYZ'), enum or native type

    Returns:
        size: size in bytes

    Raises:
        KeyError: If type is not defined
    """
    while type_ in TYPEDEFS:
        type_ = TYPEDEFS[type_]
    data_type = get_type(type_)
    return data_type.sizeof()

typedef(type_, alias)

Define an alias name for a data type

>>> typedef("int", "status")
>>> sizeof("status")
4
Parameters:
  • type_ (str) –

    data type

  • alias (str) –

    new alias name

cstruct/__init__.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def typedef(type_: str, alias: str) -> None:
    """
    Define an alias name for a data type

    Examples:
        >>> typedef("int", "status")
        >>> sizeof("status")
        4

    Args:
        type_: data type
        alias: new alias name
    """
    TYPEDEFS[alias] = type_

undef(key)

Undefine a symbol that was previously defined with define

>>> define("INIT_THREAD_SIZE", 16384)
>>> undef("INIT_THREAD_SIZE")
Parameters:
  • key (str) –

    identifier

Raises:
  • KeyError

    If key is not defined

cstruct/__init__.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
def undef(key: str) -> None:
    """
    Undefine a symbol that was previously defined with define

    Examples:
        >>> define("INIT_THREAD_SIZE", 16384)
        >>> undef("INIT_THREAD_SIZE")

    Args:
        key: identifier

    Raises:
        KeyError: If key is not defined
    """
    del DEFINES[key]