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];