abi_definitions/memory/
allocated.rs1use core::{alloc::Layout, ptr::NonNull};
2
3use alloc::slice;
4
5use crate::CompactLayout;
6
7pub struct Allocated {
8 pointer: NonNull<u8>,
9 padding: u8,
10}
11
12impl Allocated {
13 const HEADER_SIZE: usize = size_of::<CompactLayout>();
14
15 pub const fn get_padding(alignment: usize) -> usize {
16 let misalignment = Self::HEADER_SIZE % alignment;
17 if misalignment == 0 {
18 0
19 } else {
20 alignment - misalignment
21 }
22 }
23
24 pub fn get_layout_for_allocation(size: usize, alignment: usize) -> Layout {
25 let padding = Self::get_padding(alignment);
27
28 let offset = Self::HEADER_SIZE + padding;
29 let total_size = offset + size;
30
31 Layout::from_size_align(total_size, alignment).expect("Failed to create allocation layout")
34 }
35
36 fn new(pointer: *mut u8, padding: usize) -> Option<Self> {
37 Some(Self {
38 pointer: NonNull::new(pointer)?,
39 padding: padding as u8,
40 })
41 }
42
43 pub fn from_layout(pointer: *mut u8, layout: &Layout) -> Option<Self> {
44 let padding = Self::get_padding(layout.align());
45 let allocated = Self::new(pointer, padding)?;
46
47 allocated.set_layout(layout);
49
50 Some(allocated)
51 }
52
53 pub unsafe fn from_user_pointer(user_pointer: *mut u8) -> Option<Self> {
58 if user_pointer.is_null() {
59 return None;
60 }
61
62 let layout_ptr = unsafe { user_pointer.sub(size_of::<CompactLayout>()) };
64 let layout_bytes = unsafe { slice::from_raw_parts(layout_ptr, size_of::<CompactLayout>()) };
65 let layout = CompactLayout::from_le_bytes(layout_bytes.try_into().ok()?)?;
66
67 let padding = Self::get_padding(layout.get_alignment());
68 let total_header_size = Self::HEADER_SIZE + padding;
69 let base_pointer = unsafe { user_pointer.sub(total_header_size) };
70
71 Some(Self {
72 pointer: NonNull::new(base_pointer)?,
73 padding: padding as u8,
74 })
75 }
76
77 pub fn get_layout(&self) -> Option<Layout> {
78 let layout_pointer = unsafe { self.pointer.as_ptr().add(self.padding as usize) };
79
80 let bytes = unsafe { slice::from_raw_parts(layout_pointer, size_of::<CompactLayout>()) };
81
82 let layout = CompactLayout::from_le_bytes(
83 bytes
84 .try_into()
85 .expect("Failed to read CompactLayout from pointer"),
86 )?;
87
88 layout.into_layout()
89 }
90
91 pub fn erase_layout(&self) {
92 let layout_pointer = unsafe { self.pointer.as_ptr().add(self.padding as usize) };
93
94 let zero_bytes = [0u8; size_of::<CompactLayout>()];
95
96 unsafe {
97 core::ptr::copy_nonoverlapping(
98 zero_bytes.as_ptr(),
99 layout_pointer,
100 size_of::<CompactLayout>(),
101 );
102 }
103 }
104
105 pub fn set_layout(&self, layout: &Layout) {
106 let bytes = CompactLayout::from_layout(layout)
107 .expect("Failed to convert Layout to CompactLayout")
108 .to_le_bytes();
109
110 let layout_pointer = unsafe { self.pointer.as_ptr().add(self.padding as usize) };
111
112 unsafe {
113 core::ptr::copy_nonoverlapping(
114 bytes.as_ptr(),
115 layout_pointer,
116 size_of::<CompactLayout>(),
117 );
118 }
119 }
120
121 pub fn get_base_pointer(&self) -> *mut u8 {
122 self.pointer.as_ptr()
123 }
124
125 pub fn get_user_pointer(&self) -> *mut u8 {
126 let offset = self.padding as usize + Self::HEADER_SIZE;
127
128 unsafe { self.pointer.as_ptr().add(offset) }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use core::alloc::Layout;
136
137 #[test]
138 fn test_get_layout_for_allocation_basic() {
139 let layout = Layout::from_size_align(64, 8).unwrap();
140 let alloc_layout = Allocated::get_layout_for_allocation(layout.size(), layout.align());
141
142 let padding = Allocated::get_padding(layout.align());
143 let offset = Allocated::HEADER_SIZE + padding;
144 assert!(offset >= Allocated::HEADER_SIZE);
145 assert!(alloc_layout.size() >= layout.size() + Allocated::HEADER_SIZE);
146 assert_eq!(alloc_layout.align(), layout.align());
147 }
148
149 #[test]
150 fn test_get_layout_for_allocation_alignment() {
151 let layout = Layout::from_size_align(32, 16).unwrap();
152 let padding = Allocated::get_padding(layout.align());
153
154 let offset = Allocated::HEADER_SIZE + padding;
156 assert_eq!(offset % layout.align(), 0);
157 }
158
159 #[test]
160 fn test_new_with_null_pointer() {
161 let result = Allocated::new(core::ptr::null_mut(), 0);
162 assert!(result.is_none());
163 }
164
165 #[test]
166 fn test_new_with_valid_pointer() {
167 let mut data = [0u8; 64];
168 let result = Allocated::new(data.as_mut_ptr(), 8);
169 assert!(result.is_some());
170 }
171
172 #[test]
173 fn test_get_user_pointer_offset() {
174 let mut data = [0u8; 64];
175 let base_ptr = data.as_mut_ptr();
176 let padding = 16usize;
177
178 let allocated = Allocated::new(base_ptr, padding).unwrap();
179 let user_ptr = allocated.get_user_pointer();
180
181 assert_eq!(
182 user_ptr as usize,
183 base_ptr as usize + padding + Allocated::HEADER_SIZE
184 );
185 }
186
187 #[test]
188 fn test_layout_round_trip() {
189 let mut data = [0u8; 64];
190 let allocated = Allocated::new(data.as_mut_ptr(), 8).unwrap();
191
192 let original_layout = Layout::from_size_align(128, 16).unwrap();
193 allocated.set_layout(&original_layout);
194 let retrieved_layout = allocated.get_layout().unwrap();
195
196 assert_eq!(original_layout, retrieved_layout);
197 }
198
199 #[test]
200 fn test_multiple_alignment_values() {
201 for align in [1, 2, 4, 8, 16, 32, 64, 128] {
202 let padding = Allocated::get_padding(align);
203 let offset = Allocated::HEADER_SIZE + padding;
204 assert_eq!(offset % align, 0, "Failed for alignment {}", align);
205 }
206 }
207
208 #[test]
209 fn test_zero_size_layout() {
210 let layout = Layout::from_size_align(0, 1).unwrap();
211 let alloc_layout = Allocated::get_layout_for_allocation(layout.size(), layout.align());
212 let padding = Allocated::get_padding(layout.align());
213
214 let offset = Allocated::HEADER_SIZE + padding;
215 assert!(offset >= Allocated::HEADER_SIZE);
216 assert_eq!(alloc_layout.size(), offset);
217 }
218
219 #[test]
220 fn test_from_user_pointer_round_trip() {
221 let mut data = [0u8; 128];
223 let base_ptr = data.as_mut_ptr();
224
225 let layout = Layout::from_size_align(64, 16).unwrap();
226 let allocated = Allocated::from_layout(base_ptr, &layout).unwrap();
227
228 let user_ptr = allocated.get_user_pointer();
229 let recovered = unsafe { Allocated::from_user_pointer(user_ptr).unwrap() };
230
231 assert_eq!(recovered.pointer, allocated.pointer);
233 assert_eq!(recovered.padding, allocated.padding);
234 assert_eq!(recovered.get_layout().unwrap(), layout);
235 }
236
237 #[test]
238 fn test_user_pointer_alignment() {
239 use alloc::alloc::{alloc, dealloc};
241
242 for align in [1, 2, 4, 8, 16, 32, 64, 128] {
243 let layout = Layout::from_size_align(256, align).unwrap();
244 let base_ptr = unsafe { alloc(layout) };
245 assert!(!base_ptr.is_null(), "Allocation failed");
246
247 let user_layout = Layout::from_size_align(32, align).unwrap();
249 let padding = Allocated::get_padding(user_layout.align());
250
251 let allocated = Allocated::new(base_ptr, padding).unwrap();
252 let user_ptr = allocated.get_user_pointer();
253
254 assert_eq!(
256 user_ptr as usize % align,
257 0,
258 "User pointer not aligned to {} bytes",
259 align
260 );
261
262 unsafe { dealloc(base_ptr, layout) };
263 }
264 }
265
266 #[test]
267 fn test_layout_stored_before_user_pointer() {
268 let mut data = [0u8; 128];
270 let base_ptr = data.as_mut_ptr();
271
272 let layout = Layout::from_size_align(64, 8).unwrap();
273 let allocated = Allocated::from_layout(base_ptr, &layout).unwrap();
274 let user_ptr = allocated.get_user_pointer();
275
276 let layout_ptr = unsafe { user_ptr.sub(Allocated::HEADER_SIZE) };
278 let layout_bytes =
279 unsafe { core::slice::from_raw_parts(layout_ptr, size_of::<CompactLayout>()) };
280 let read_layout = CompactLayout::from_le_bytes(layout_bytes.try_into().unwrap());
281 let compact_layout = CompactLayout::from_layout(&layout).unwrap();
282
283 assert_eq!(read_layout.unwrap(), compact_layout);
284 }
285
286 #[test]
287 fn test_padding_calculation_correctness() {
288 for align in [1, 2, 4, 8, 16, 32, 64, 128] {
290 let padding = Allocated::get_padding(align);
291 let user_data_offset = Allocated::HEADER_SIZE + padding;
292
293 assert_eq!(
294 user_data_offset % align,
295 0,
296 "Padding calculation failed for alignment {}",
297 align
298 );
299 }
300 }
301
302 #[test]
303 fn test_allocation_layout_size() {
304 let layout = Layout::from_size_align(100, 32).unwrap();
306 let alloc_layout = Allocated::get_layout_for_allocation(layout.size(), layout.align());
307 let padding = Allocated::get_padding(layout.align());
308
309 let offset = Allocated::HEADER_SIZE + padding;
310 assert!(alloc_layout.size() >= Allocated::HEADER_SIZE + layout.size());
311 assert_eq!(alloc_layout.size(), offset + layout.size());
312 }
313}