Coverage for dataclasses_struct / types.py: 100%
98 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 22:31 +1200
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 22:31 +1200
1from typing import Annotated
3from . import field
5Char = bytes
6"""Single char type. Supported in both size modes."""
8Bool = Annotated[bool, field.BoolField()]
9"""Boolean type. Supported in both size modes."""
11I8 = Annotated[int, field.SignedStdIntField(1)]
12"""Fixed-width 8-bit signed integer. Supported with `size="std"`."""
14U8 = Annotated[int, field.UnsignedStdIntField(1)]
15"""Fixed-width 8-bit unsigned integer. Supported with `size="std"`."""
17I16 = Annotated[int, field.SignedStdIntField(2)]
18"""Fixed-width 16-bit signed integer. Supported with `size="std"`."""
20U16 = Annotated[int, field.UnsignedStdIntField(2)]
21"""Fixed-width 16-bit unsigned integer. Supported with `size="std"`."""
23I32 = Annotated[int, field.SignedStdIntField(4)]
24"""Fixed-width 32-bit signed integer. Supported with `size="std"`."""
26U32 = Annotated[int, field.UnsignedStdIntField(4)]
27"""Fixed-width 32-bit unsigned integer. Supported with `size="std"`."""
29I64 = Annotated[int, field.SignedStdIntField(8)]
30"""Fixed-width 64-bit signed integer. Supported with `size="std"`."""
32U64 = Annotated[int, field.UnsignedStdIntField(8)]
33"""Fixed-width 64-bit unsigned integer. Supported with `size="std"`."""
36# Native integer types
37SignedChar = Annotated[int, field.NativeIntField("b", "byte")]
38"""Equivalent to native C `signed char`. Supported with `size="native"`."""
40UnsignedChar = Annotated[int, field.NativeIntField("B", "ubyte")]
41"""Equivalent to native C `unsigned char`. Supported with `size="native"`."""
43Short = Annotated[int, field.NativeIntField("h", "short")]
44"""Equivalent to native C `short`. Supported with `size="native"`."""
46UnsignedShort = Annotated[int, field.NativeIntField("H", "ushort")]
47"""Equivalent to native C `unsigned short`. Supported with `size="native"`."""
49Int = Annotated[int, field.NativeIntField("i", "int")]
50"""Equivalent to native C `int`. Supported with `size="native"`."""
52UnsignedInt = Annotated[int, field.NativeIntField("I", "uint")]
53"""Equivalent to native C `unsigned int`. Supported with `size="native"`."""
55Long = Annotated[int, field.NativeIntField("l", "long")]
56"""Equivalent to native C `long`. Supported with `size="native"`."""
58UnsignedLong = Annotated[int, field.NativeIntField("L", "ulong")]
59"""Equivalent to native C `unsigned long`. Supported with `size="native"`."""
61LongLong = Annotated[int, field.NativeIntField("q", "longlong")]
62"""Equivalent to native C `long long`. Supported with `size="native"`."""
64UnsignedLongLong = Annotated[int, field.NativeIntField("Q", "ulonglong")]
65"""Equivalent to native C `unsigned long long`. Supported with
66`size="native"`."""
69# Native size types
70UnsignedSize = Annotated[int, field.SizeField(signed=False)]
71"""Equivalent to native C `size_t`. Supported with `size="native"`."""
73SignedSize = Annotated[int, field.SizeField(signed=True)]
74"""Equivalent to native C `ssize_t` (a POSIX extension type). Supported with
75`size="native"`."""
77# Native pointer types
78Pointer = Annotated[int, field.PointerField()]
79"""Equivalent to native C `void *` pointer. Supported with `size="native"`."""
81# Floating point types
82F16 = Annotated[float, field.FloatingPointField("e")]
83"""Half-precision floating point number. Supported in both size modes.
85Some compilers provide support for half precision floats on certain platforms
86(e.g. [GCC](https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html),
87[Clang](https://clang.llvm.org/docs/LanguageExtensions.html#half-precision-floating-point)).
88It is also available as
89[`std::float16_t`](https://en.cppreference.com/w/cpp/types/floating-point.html)
90in C++23.
91"""
93F32 = Annotated[float, field.FloatingPointField("f")]
94"""Single-precision floating point number, equivalent to `float` in C.
95Supported in both size modes."""
97F64 = Annotated[float, field.FloatingPointField("d")]
98"""Double-precision floating point number, equivalent to `double` in C.
99Supported in both size modes."""
102class LengthPrefixed(field.Field[bytes]):
103 """
104 Length-prefixed byte array, also known as a 'Pascal string'.
106 Packed to a fixed-length array of bytes, where the first byte is the length
107 of the data. Data shorter than the maximum size is padded with zero bytes.
109 Must be used to annotate a `bytes` field with `typing.Annotated`:
111 ```python
112 import dataclasses_struct as dcs
114 @dcs.dataclass_struct()
115 class Example:
116 fixed_length: Annotated[bytes, dcs.LengthPrefixed(10)]
117 ```
119 Args:
120 size: The maximum size of the string including the length byte. Must be
121 between 2 and 256 inclusive. The maximum array length that can be
122 stored without truncation is `size - 1`.
124 Raises:
125 ValueError: If `size` is outside the valid range.
126 """
128 field_type = bytes
130 def __init__(self, size: int):
131 if not (isinstance(size, int) and 2 <= size <= 256):
132 raise ValueError("size must be an int between 2 and 256")
133 self.size = size
135 def format(self) -> str:
136 return f"{self.size}p"
138 def __repr__(self) -> str:
139 return f"{type(self).__name__}({self.size})"
141 def validate_default(self, val: bytes) -> None:
142 if len(val) > self.size - 1:
143 msg = f"bytes cannot be longer than {self.size - 1} bytes"
144 raise ValueError(msg)
147class CString(field.Field[bytes]):
148 """
149 Null-terminated byte array, commonly known as a 'C string'.
151 Packed to a fixed-length array of bytes of length `size` that is guaranteed
152 to be null-terminated. Note that `size` includes the null byte, so arrays
153 longer than `size - 1` are truncated.
155 When unpacking, the trailing null byte and any subsequent bytes are
156 discarded.
158 Must be used to annotate a `bytes` field with `typing.Annotated`. For
159 example,
161 ```python
162 import dataclasses_struct as dcs
164 @dcs.dataclass_struct()
165 class Example:
166 cstr: Annotated[bytes, dcs.CString(5)]
168 >>> example = Example(b"123")
169 >>> packed = example.pack()
170 >>> packed
171 b'123\x00\x00'
172 >>> unpacked = Example.unpack(packed)
173 >>> unpacked
174 Example(cstr=b'123')
175 ```
177 Note that there is additional overhead when unpacking as the bytes array is
178 searched for the null terminator. If this is a concern (e.g. for very large
179 arrays), it may be better to just use a regular fixed-length bytes array
180 with a single trailing pad byte to ensure the array is always
181 null-terminated. E.g. the following
183 ```python
184 class Test:
185 array: Annotated[bytes, LENGTH, dcs.PadAfter(1)]`
186 ```
188 is equivalent to the following array declaration in C:
190 ```c
191 struct Test {
192 char array[LENGTH + 1];
193 };
194 ```
196 Args:
197 size: The total size of the byte array, including the trailing null
198 byte.
200 Raises:
201 ValueError: If `size` is not a positive, non-zero integer.
202 """
204 field_type = bytes
206 def __init__(self, size: int):
207 if not isinstance(size, int) or size <= 0:
208 raise ValueError("C string length must be positive non-zero int")
210 self.size = size
212 def __repr__(self) -> str:
213 return f"{type(self).__name__}({self.size})"
215 def format(self) -> str:
216 return f"{self.size - 1}sx"
218 def validate_default(self, val: bytes) -> None:
219 n = len(val)
220 if n >= self.size:
221 msg = (
222 f"C string cannot be longer than {self.size - 1} bytes"
223 " (size includes the null terminator)"
224 )
225 raise ValueError(msg)
228class _Padding:
229 before: bool
231 def __init__(self, size: int):
232 if not isinstance(size, int) or size < 0:
233 raise ValueError("padding size must be non-negative int")
234 self.size = size
236 def __repr__(self) -> str:
237 return f"{type(self).__name__}({self.size})"
240class PadBefore(_Padding):
241 """Add zero-bytes padding before the field.
243 Should be used with `typing.Annotated`.
245 ```python
246 from typing import Annotated
247 import dataclasses_struct as dcs
249 @dcs.dataclass_struct()
250 class Padded:
251 x: Annotated[int, dcs.PadBefore(5)]
252 ```
254 Args:
255 size: The number of padding bytes to add before the field.
256 """
258 before = True
260 def __init__(self, size: int):
261 super().__init__(size)
264class PadAfter(_Padding):
265 """Add zero-bytes padding after the field.
267 Should be used with `typing.Annotated`.
269 ```python
270 from typing import Annotated
271 import dataclasses_struct as dcs
273 @dcs.dataclass_struct()
274 class Padded:
275 x: Annotated[int, dcs.PadAfter(5)]
276 ```
278 Args:
279 size: The number of padding bytes to add after the field.
280 """
282 before = False
284 def __init__(self, size: int):
285 super().__init__(size)