p_struct!() { /* proc-macro */ }
Expand description
Generates a pointer struct from a struct definition.
The original struct definition is discarded, while a new struct is generated with a P
prefix.
This new struct contains methods for reading the fields of the pointer struct.
Clone and Copy derives are added if they don’t exist, and the generated type is #[repr(transparent)]
to allow for transmutation from pointers.
§Offset Attribute
The offset
attribute is used to specify the offset of the field from the base address.
This offset is added to the base address to return the field.
Default behavior for generated offset getters is to use read_unaligned to read the field, copying it’s value and returning it from the function.
§Sub attributes:
reinterpret
: Overrides default behavior, reinterprets the returned pointer from base address + offset as a different pointer type.array
: Allows you to define the field as an array of pointers. This generates a method which allows for indexing into the array.
§Example
use pstruct::p_struct;
// Define a byte array to simulate struct data.
let byte_array: &[u8] = &[
69, // `field_b` (offset 0)
255, 255, 255, 255, // `field_a` (offset 1)
5, 0, 10, 0, 15, 0, 20, 0, 25, 0, // `field_d` (offset 5)
40, // `field_c` (offset 0xF)
1, 2, 3, 4 // Field `field_e` (offset 0x10)
];
// Define a pointer struct using the macro.
p_struct! {
pub struct Example {
#[offset(0x1)]
field_a: u32,
#[offset(0x0)]
field_b: u8,
#[offset(0xF, reinterpret)]
// Reinterprets the pointer from base + 0xF (via transmute) as a *const u8
// Use this feature to return pointers to a value rather than the value itself.
field_c: *const u8,
#[offset(0x5, array(5, size_t = 2))]
// Defines the field as an array of pointers, allowing for indexing into the array.
// This is useful for returning arrays of pointers.
field_d: *const u16,
// This is an array of 4 u8s, but the size of each element is determined by the size_fn attribute.
#[offset(0x10, array(4, size_fn = "core::mem::size_of::<u8>()"))]
field_e: *const u8,
}
}
let example_ptr = PExample::from(byte_array);
assert_eq!(unsafe { example_ptr.field_b() }, 69);
assert_eq!(unsafe { example_ptr.field_a() }, u32::MAX);
let field_d_1 = unsafe { example_ptr.get_field_d(0).unwrap() };
let field_d_2 = unsafe { example_ptr.get_field_d(1).unwrap() };
assert_eq!(unsafe { *field_d_1 }, 5u16);
assert_eq!(unsafe { *field_d_2 }, 10u16);
let reconstruct_array = unsafe { core::slice::from_raw_parts(example_ptr.field_d(), 5) };
assert_eq!(reconstruct_array, [5, 10, 15, 20, 25]);
assert_eq!(unsafe { *(example_ptr.field_c().as_ref().unwrap()) }, 40u8);
assert_eq!(unsafe { *example_ptr.get_field_e(0).unwrap() }, 1u8);
assert_eq!(unsafe { *example_ptr.get_field_e(1).unwrap() }, 2u8);