devela/sys/mem/pod.rs
1// devela::sys::mem::pod
2//
3//! Defines [`MemPod`].
4//
5// See also: <https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html>
6
7use core::{mem::MaybeUninit, num::*};
8
9/// Indicates a type is Plain Old Data, and meets specific memory layout guarantees.
10///
11/// Types that implement this trait are guaranteed to be safe for certain
12/// low-level memory operations, such as transmuting to and from byte slices,
13/// copying, and interfacing with C code.
14///
15/// # Safety
16///
17/// Implementing `MemPod` is `unsafe` because it requires the implementer to ensure the
18/// following invariants:
19///
20/// 1. **No Padding:** The type must not contain any padding bytes. This ensures that the
21/// memory representation of the type is fully defined by its fields.
22///
23/// 2. **Safe to Transmute:** The type must be safe to transmute to and from a byte slice
24/// (`&[u8]`). This requires that the type's memory layout is consistent and well-defined
25/// across different platforms.
26///
27/// 3. **Copyable:** The type must implement `Copy`, meaning it can be duplicated simply by
28/// copying its bits. This is a fundamental property of POD types.
29///
30/// 4. **Valid Bit Patterns:** Any bit pattern must represent a valid instance of the type.
31/// This means that transmuting arbitrary bytes into the type must always produce a valid
32/// value, and no bit pattern can cause undefined behavior when interpreted as this type.
33///
34/// # Implementing `MemPod`
35///
36/// When implementing `MemPod`, it is recommended to use `#[repr(C)]` or `#[repr(transparent)]`
37/// on the type to ensure a well-defined and predictable memory layout. This is particularly
38/// important when dealing with FFI or any situation where the exact memory layout of the type
39/// is critical.
40///
41/// Note that only types that are `Copy` can implement `MemPod`. This is because POD types
42/// inherently need to be trivially copyable without any additional logic (e.g. no destructors).
43///
44/// # Use Cases
45///
46/// - **FFI Compatibility:** `MemPod` types can be safely passed between Rust and C, as they
47/// have a predictable memory layout.
48/// - **Efficient Serialization:** `MemPod` types can be directly serialized to or deserialized
49/// from a byte buffer, making them ideal for low-level data manipulation and storage.
50/// - **Memory Safety:** By ensuring that types implementing `MemPod` adhere to strict memory
51/// guarantees, you can safely perform low-level operations without risking undefined behavior.
52///
53/// # Examples
54/// ```rust
55/// use devela::sys::mem::MemPod;
56///
57/// // Define a simple structure that can be safely used as POD.
58/// #[derive(Copy, Clone)]
59/// #[repr(C)] // Ensure a predictable memory layout compatible with C.
60/// struct MyStruct {
61/// pub a: u8,
62/// pub b: i16,
63/// pub c: u32,
64/// }
65///
66/// unsafe impl MemPod for MyStruct {}
67/// ```
68#[cfg(all(not(feature = "safe_mem"), feature = "unsafe_layout"))]
69pub unsafe trait MemPod: Copy + 'static {
70 /// Returns a new instance contrcuted from zeroes.
71 #[must_use]
72 fn zeroed() -> Self {
73 // SAFETY. An all-zero value of `T: MemPod` is always valid.
74 unsafe { core::mem::zeroed() }
75 }
76
77 /// Returns a new instance constructed from the given bytes.
78 ///
79 /// If the byte slice is too small, the remaining bytes will be 0.
80 #[must_use]
81 fn from_bytes(bytes: &[u8]) -> Self {
82 let size = size_of::<Self>();
83 let bytes_to_copy = core::cmp::min(bytes.len(), size);
84
85 let mut new_self = MaybeUninit::<Self>::uninit();
86
87 // SAFETY: We're only copying valid byte data into the uninitialized memory
88 unsafe {
89 // Copy the provided bytes into the memory
90 core::ptr::copy_nonoverlapping(
91 bytes.as_ptr(),
92 new_self.as_mut_ptr() as *mut u8,
93 bytes_to_copy,
94 );
95 // If there are remaining bytes, fill them with zeros
96 if bytes_to_copy < size {
97 core::ptr::write_bytes(
98 (new_self.as_mut_ptr() as *mut u8).add(bytes_to_copy),
99 0,
100 size - bytes_to_copy,
101 );
102 }
103 new_self.assume_init()
104 }
105 }
106
107 /// Returns the instance's data as a slice of bytes.
108 #[must_use]
109 fn as_bytes(&self) -> &[u8] {
110 let ptr = self as *const Self as *const u8;
111 let len = size_of::<Self>();
112 unsafe { core::slice::from_raw_parts(ptr, len) }
113 }
114
115 /// Returns the instance's data as a mutable slice of bytes.
116 #[must_use]
117 fn as_bytes_mut(&mut self) -> &mut [u8] {
118 let ptr = self as *mut Self as *mut u8;
119 let len = size_of::<Self>();
120 unsafe { core::slice::from_raw_parts_mut(ptr, len) }
121 }
122}
123
124// Implement MemPod
125#[rustfmt::skip]
126macro_rules! mem_pod {
127 // impl for types that are always POD.
128 ($($t:ty),+) => { $( mem_pod![@$t]; )+ };
129 (@$t:ty) => { unsafe impl MemPod for $t {} };
130
131 // impl for types that are POD only when wrapped in an Option.
132 (option: $($t:ty),+) => { $( mem_pod![@option: $t]; )+ };
133 (@option: $t:ty) => { unsafe impl MemPod for Option<$t> {} };
134}
135pub(crate) use mem_pod;
136
137#[rustfmt::skip]
138mem_pod![(), u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64];
139
140unsafe impl<T: MemPod, const N: usize> MemPod for [T; N] {}
141unsafe impl<T: MemPod> MemPod for MaybeUninit<T> {}
142unsafe impl<T: MemPod> MemPod for core::mem::ManuallyDrop<T> {}
143unsafe impl<T: MemPod> MemPod for core::num::Wrapping<T> {}
144unsafe impl<T: ?Sized + 'static> MemPod for core::marker::PhantomData<T> {}
145
146unsafe impl<T: MemPod> MemPod for Option<T> {}
147mem_pod![option:
148 NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
149 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize
150];