devela/sys/mem/
pod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// devela::sys::mem::pod
//
//!
//
// See also: <https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html>

use core::{mem::MaybeUninit, num::*};

/// Indicates a type is Plain Old Data, and meets specific memory layout guarantees.
///
/// Types that implement this trait are guaranteed to be safe for certain
/// low-level memory operations, such as transmuting to and from byte slices,
/// copying, and interfacing with C code.
///
/// # Safety
///
/// Implementing `MemPod` is `unsafe` because it requires the implementer to ensure the
/// following invariants:
///
/// 1. **No Padding:** The type must not contain any padding bytes. This ensures that the
///    memory representation of the type is fully defined by its fields.
///
/// 2. **Safe to Transmute:** The type must be safe to transmute to and from a byte slice
///    (`&[u8]`). This requires that the type's memory layout is consistent and well-defined
///    across different platforms.
///
/// 3. **Copyable:** The type must implement `Copy`, meaning it can be duplicated simply by
///    copying its bits. This is a fundamental property of POD types.
///
/// 4. **Valid Bit Patterns:** Any bit pattern must represent a valid instance of the type.
///    This means that transmuting arbitrary bytes into the type must always produce a valid
///    value, and no bit pattern can cause undefined behavior when interpreted as this type.
///
/// # Implementing `MemPod`
///
/// When implementing `MemPod`, it is recommended to use `#[repr(C)]` or `#[repr(transparent)]`
/// on the type to ensure a well-defined and predictable memory layout. This is particularly
/// important when dealing with FFI or any situation where the exact memory layout of the type
/// is critical.
///
/// Note that only types that are `Copy` can implement `MemPod`. This is because POD types
/// inherently need to be trivially copyable without any additional logic (e.g. no destructors).
///
/// # Use Cases
///
/// - **FFI Compatibility:** `MemPod` types can be safely passed between Rust and C, as they
///   have a predictable memory layout.
/// - **Efficient Serialization:** `MemPod` types can be directly serialized to or deserialized
///   from a byte buffer, making them ideal for low-level data manipulation and storage.
/// - **Memory Safety:** By ensuring that types implementing `MemPod` adhere to strict memory
///   guarantees, you can safely perform low-level operations without risking undefined behavior.
///
/// # Examples
/// ```rust
/// use devela::sys::mem::MemPod;
///
/// // Define a simple structure that can be safely used as POD.
/// #[derive(Copy, Clone)]
/// #[repr(C)]  // Ensure a predictable memory layout compatible with C.
/// struct MyStruct {
///     pub a: u8,
///     pub b: i16,
///     pub c: u32,
/// }
///
/// unsafe impl MemPod for MyStruct {}
/// ```
#[cfg(all(not(feature = "safe_mem"), feature = "unsafe_layout"))]
pub unsafe trait MemPod: Copy + 'static {
    /// Returns a new instance contrcuted from zeroes.
    #[must_use]
    fn zeroed() -> Self {
        // SAFETY. An all-zero value of `T: MemPod` is always valid.
        unsafe { core::mem::zeroed() }
    }

    /// Returns a new instance constructed from the given bytes.
    ///
    /// If the byte slice is too small, the remaining bytes will be 0.
    #[must_use]
    fn from_bytes(bytes: &[u8]) -> Self {
        let size = size_of::<Self>();
        let bytes_to_copy = core::cmp::min(bytes.len(), size);

        let mut new_self = MaybeUninit::<Self>::uninit();

        // SAFETY: We're only copying valid byte data into the uninitialized memory
        unsafe {
            // Copy the provided bytes into the memory
            core::ptr::copy_nonoverlapping(
                bytes.as_ptr(),
                new_self.as_mut_ptr() as *mut u8,
                bytes_to_copy,
            );
            // If there are remaining bytes, fill them with zeros
            if bytes_to_copy < size {
                core::ptr::write_bytes(
                    (new_self.as_mut_ptr() as *mut u8).add(bytes_to_copy),
                    0,
                    size - bytes_to_copy,
                );
            }
            new_self.assume_init()
        }
    }

    /// Returns the instance's data as a slice of bytes.
    #[must_use]
    fn as_bytes(&self) -> &[u8] {
        let ptr = self as *const Self as *const u8;
        let len = size_of::<Self>();
        unsafe { core::slice::from_raw_parts(ptr, len) }
    }

    /// Returns the instance's data as a mutable slice of bytes.
    #[must_use]
    fn as_bytes_mut(&mut self) -> &mut [u8] {
        let ptr = self as *mut Self as *mut u8;
        let len = size_of::<Self>();
        unsafe { core::slice::from_raw_parts_mut(ptr, len) }
    }
}

// Implement MemPod
#[rustfmt::skip]
macro_rules! mem_pod {
    // impl for types that are always POD.
    ($($t:ty),+) => { $( mem_pod![@$t]; )+ };
    (@$t:ty) => { unsafe impl MemPod for $t {} };

    // impl for types that are POD only when wrapped in an Option.
    (option: $($t:ty),+) => { $( mem_pod![@option: $t]; )+ };
    (@option: $t:ty) => { unsafe impl MemPod for Option<$t> {} };
}
pub(crate) use mem_pod;

#[rustfmt::skip]
mem_pod![(), u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64];

unsafe impl<T: MemPod, const N: usize> MemPod for [T; N] {}
unsafe impl<T: MemPod> MemPod for MaybeUninit<T> {}
unsafe impl<T: MemPod> MemPod for core::mem::ManuallyDrop<T> {}
unsafe impl<T: MemPod> MemPod for core::num::Wrapping<T> {}
unsafe impl<T: ?Sized + 'static> MemPod for core::marker::PhantomData<T> {}

unsafe impl<T: MemPod> MemPod for Option<T> {}
mem_pod![option:
    NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize
];