A small library to ease the creation, usage, serialization and deserialization of C structs.
Project description
QuickStruct
QuickStruct is a small library written in Python that allows you to easily create C structs (and a bit more dynamic stuff) in Python!
It's fairly easy to use
from quickstruct import *
class Person(DataStruct):
name: String
age: i8
Structs can also be composed
class TeachingClass(DataStruct):
teacher: Person
# We use Array[T] to make it dynamic sized
students: Array[Person]
And structs can also inherit other structs (we even support multiple inheritance!)
class Employee(Person):
salary: i32
Now let's use the structs we defined
# We have 2 options when initializing.
# Either by setting each attribute individually
person = Person()
person.name = "John Doe"
person.age = 42
# Or by passing them as keyword arguments
person = Person(name="John Doe", age=42)
The main use for C structs is to convert them from bytes and back
data = person.to_bytes()
# Do something with the data
# And it's also easy to deserialize
person = Person.from_bytes(data)
When deserializing a struct with multiple bases or if one of the fields was overriden, the deserialization must be done through the exact type of the struct.
Fixed Size Structs
A fixed size struct is any struct that has a known fixed size at build time that doesn't depend on the data it holds. QuickStruct can verify a struct has a fixed size.
# The StructFlags.FixedSize flag is used to verify the struct has a fixed size.
# If the size could not be verified, a SizeError is raised.
class FixedSizeStruct(DataStruct, flags=StructFlags.FixedSize):
a: i32
b: i8
c: f32
d: char
e: String[10] # 10 character string
f: Person[3] # 3 'person' objects
# g: Array[i32] <- not a fixed size field. this will error
Struct Properties
It is possible to query some information about a structure.
from quickstruct import *
class Fixed(DataStruct):
s: String[10]
x: i32
class Dynamic(DataStruct):
s: String
x: i32
print("Fixed.size:", Fixed.size) # 16 (padding automatically added)
print("Dynamic.size:", Dynamic.size) # -1 (dynamic size)
print("Fixed.is_fixed_size:", Fixed.is_fixed_size) # True
print("Dynamic.is_fixed_size:", Dynamic.is_fixed_size) # False
print("Fixed.is_dynamic_size:", Fixed.is_dynamic_size) # False
print("Dynamic.is_dynamic_size:", Dynamic.is_dynamic_size) # True
print("Fixed.fields:", Fixed.fields) # [s: String[10], __pad_0__: Padding(2), x: i32]
print("Dynamic.fields:", Dynamic.fields) # [s: String, x: i32]
print("Fixed.alignment:", Fixed.alignment) # 16.
print("Dynamic.alignment:", Dynamic.alignment) # -1 (no alignment because dynamic struct can't be aligned).
print("Fixed.is_final:", Fixed.is_final) # False
print("Dynamic.is_final:", Dynamic.is_final) # False
print("Fixed.is_protected:", Fixed.is_protected) # False
print("Dynamic.is_protected:", Dynamic.is_protected) # False
Alignment
It is also possible to add padding to the struct. There are 2 ways to do that:
Manual Alignment
This can be done with the Padding
type.
class AlignedStruct(DataStruct):
c1: char
# This adds a single byte padding
_pad0: Padding
short: i16
# We can also add multi-byte padding
# Here we'll pad to get 8 byte alignment (missing 4 bytes)
_pad1: Padding[4]
Automatic Alignment
This can done by passing some flags to the class definition. By default the struct is automatically aligned.
# Aligned on 2 byte boundary
class AlignedStruct(DataStruct, flags = StructFlags.Align2Bytes):
c1: char
# Padding will be added here
short: i16
Struct Flags
Flag | Description |
---|---|
Default | The default to use if no flags are given. Same as AllowOverride | AlignAuto . |
NoAlignment | This is the most packed form of the struct. All fields are adjacent with no padding (unless manually added) |
Packed | Same as NoAlignment except that NoAlignment is a bit more optimized because no alignment is done. |
Align1Byte | Same as Packed |
Align2Bytes | Aligns the fields on 2 byte boundary. |
Align4Bytes | Aligns the fields on 4 byte boundary. |
Align8Bytes | Aligns the fields on 8 byte boundary. |
AlignAuto | Aligns the fields by their type. |
ReorderFields | Specifies the fields should be reordered in order to make the struct a little more compressed. |
ForceDataOnly | Deprecated. Specifies that the struct may only contain serializable fields. Data-only structs may only inherit data-only structs. |
AllowOverride | If set, fields defined in the struct may override fields that are defined in the base struct. |
TypeSafeOverride | If set, when fields are overridden, they must have the same type (which would make it pretty useless to override). Implies AllowOverride . If fields have a different type, an UnsafeOverrideError is raised. |
ForceSafeOverride | Deprectaed. Same as TypeSafeOverride . |
FixedSize | If set, the struct must have a fixed size. If not, an exception SizeError is raised. |
ForceFixedSize | Deprecated. Same as FixedSize . |
AllowInline | Deprecated. If set, the struct's fields will be inlined into another struct the contains this struct. |
Protected | If set, denies any overrides of that structure. If a struct is trying to override a field of it, an UnoverridableFieldError is raised. |
LockedStructure | Deprecated. Same as Protected . |
Final | Marks the structure so it won't be inheritable by any other class. If a struct is trying to inherit it, an InheritanceError is raised. |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for quickstruct-0.2.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5b5f2081791ac353e091fbfb010c772aee8d3e86ca47ec3af1e0942f03781248 |
|
MD5 | 86910d0db0e5cf5be323c776db37b876 |
|
BLAKE2b-256 | aca89921540974f9b5c776144a3600272874916ff76c00512e27d3f3c6c7aa9e |