shm_primitives/
region.rs

1use core::mem::{align_of, size_of};
2use core::ptr::NonNull;
3
4/// A contiguous region of memory addressed by offset.
5///
6/// # Safety
7///
8/// The caller must ensure:
9/// - `base` is valid for `len` bytes and properly aligned for all contained types
10/// - the memory remains valid for the lifetime of this Region
11#[derive(Clone, Copy)]
12pub struct Region {
13    base: NonNull<u8>,
14    len: usize,
15}
16
17impl Region {
18    /// Create a region from a raw pointer and length.
19    ///
20    /// # Safety
21    ///
22    /// - `base` must be valid for `len` bytes
23    /// - `base` must be aligned for all contained types
24    /// - the memory must remain valid for the lifetime of this Region
25    pub unsafe fn from_raw(base: *mut u8, len: usize) -> Self {
26        let base = NonNull::new(base).expect("region base must be non-null");
27        Self { base, len }
28    }
29
30    /// Returns the base pointer of the region.
31    #[inline]
32    pub fn as_ptr(&self) -> *mut u8 {
33        self.base.as_ptr()
34    }
35
36    /// Returns the size of the region in bytes.
37    #[inline]
38    pub fn len(&self) -> usize {
39        self.len
40    }
41
42    /// Returns true if the region has zero length.
43    #[inline]
44    pub fn is_empty(&self) -> bool {
45        self.len == 0
46    }
47
48    /// Returns a pointer to offset `off` within the region.
49    #[inline]
50    pub fn offset(&self, off: usize) -> *mut u8 {
51        assert!(
52            off < self.len,
53            "offset {off} out of bounds (len={})",
54            self.len
55        );
56        unsafe { self.as_ptr().add(off) }
57    }
58
59    /// Returns a reference to a `T` at the given byte offset.
60    ///
61    /// # Safety
62    ///
63    /// The offset must be aligned for `T` and within bounds.
64    #[inline]
65    pub unsafe fn get<T>(&self, off: usize) -> &T {
66        debug_assert!(off + size_of::<T>() <= self.len);
67        debug_assert!(off.is_multiple_of(align_of::<T>()));
68        unsafe { &*(self.offset(off) as *const T) }
69    }
70
71    /// Returns a mutable reference to a `T` at the given byte offset.
72    ///
73    /// # Safety
74    ///
75    /// The offset must be aligned for `T` and within bounds.
76    #[inline]
77    #[allow(clippy::mut_from_ref)]
78    pub unsafe fn get_mut<T>(&self, off: usize) -> &mut T {
79        debug_assert!(off + size_of::<T>() <= self.len);
80        debug_assert!(off.is_multiple_of(align_of::<T>()));
81        unsafe { &mut *(self.offset(off) as *mut T) }
82    }
83}
84
85unsafe impl Send for Region {}
86unsafe impl Sync for Region {}
87
88mod heap {
89    use super::Region;
90    use core::ptr::NonNull;
91    use std::alloc::{Layout, alloc_zeroed, dealloc};
92
93    /// Heap-backed region for tests or heap-based usage.
94    pub struct HeapRegion {
95        base: NonNull<u8>,
96        len: usize,
97        layout: Layout,
98    }
99
100    impl HeapRegion {
101        /// Allocate a zeroed heap region aligned to 64 bytes.
102        pub fn new_zeroed(size: usize) -> Self {
103            let layout =
104                Layout::from_size_align(size.max(1), 64).expect("invalid heap region layout");
105            let ptr = unsafe { alloc_zeroed(layout) };
106            let base = NonNull::new(ptr).expect("heap region allocation failed");
107            Self {
108                base,
109                len: size,
110                layout,
111            }
112        }
113
114        /// Returns a Region view of this allocation.
115        #[inline]
116        pub fn region(&self) -> Region {
117            unsafe { Region::from_raw(self.base.as_ptr(), self.len) }
118        }
119
120        /// Returns the allocation size.
121        #[inline]
122        pub fn len(&self) -> usize {
123            self.len
124        }
125
126        /// Returns true if the allocation is zero-length.
127        #[inline]
128        pub fn is_empty(&self) -> bool {
129            self.len == 0
130        }
131    }
132
133    impl Drop for HeapRegion {
134        fn drop(&mut self) {
135            unsafe { dealloc(self.base.as_ptr(), self.layout) };
136        }
137    }
138
139    unsafe impl Send for HeapRegion {}
140    unsafe impl Sync for HeapRegion {}
141}
142
143pub use heap::HeapRegion;