safe_allocator_api/raw_alloc.rs
1//! A safe wrapper around low-level allocation primitives from `alloc::alloc`.
2//!
3//! This crate provides a safe interface for working with raw allocations while maintaining
4//! the same error handling semantics as the underlying allocation APIs.
5use crate::allocator_api::*;
6use core::ptr::NonNull;
7use core::{alloc::Layout, fmt};
8
9/// A safe wrapper around a raw allocation with known layout.
10///
11/// # Safety
12///
13/// This type ensures that:
14/// - The wrapped pointer is always non-null and properly aligned
15/// - Memory is automatically deallocated when dropped
16/// - Reallocation maintains proper alignment and size constraints
17///
18/// # Example
19///
20/// ```rust
21/// # use core::alloc::Layout;
22/// use safe_allocator_api::RawAlloc;
23///
24/// // Create a new allocation of 1024 bytes
25/// let layout = Layout::array::<u8>(1024).unwrap();
26/// let mut alloc = RawAlloc::new(layout).expect("allocation failed");
27///
28/// // Write some data
29/// unsafe {
30/// core::ptr::write(alloc.as_mut_ptr(), 42u8);
31/// }
32///
33/// // Automatically deallocated when dropped
34/// ```
35pub struct RawAlloc<A: Allocator = Global> {
36 ptr: NonNull<[u8]>,
37 layout: Layout,
38 allocator: A,
39}
40
41impl<A: Allocator> RawAlloc<A> {
42 /// Creates a new allocation with the given layout using the provided allocator.
43 ///
44 /// This is equivalent to calling [`Allocator::allocate`] but provides automatic
45 /// cleanup when the allocation is no longer needed.
46 ///
47 /// # Arguments
48 ///
49 /// * `layout` - The desired memory layout
50 /// * `allocator` - The allocator to use
51 ///
52 /// # Errors
53 ///
54 /// Returns [`AllocError`] if the allocator reports an error or if the layout
55 /// has a size of 0.
56 ///
57 /// # Example
58 ///
59 /// ```rust
60 /// #![feature(allocator_api)]
61 ///
62 /// use core::alloc::Layout;
63 /// use safe_allocator_api::allocator_api::*;
64 /// use safe_allocator_api::RawAlloc;
65 ///
66 /// let layout = Layout::new::<u64>();
67 /// let alloc = RawAlloc::new_in(layout, Global)?;
68 /// # Ok::<_, AllocError>(())
69 /// ```
70 pub fn new_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
71 if layout.size() == 0 {
72 return Err(AllocError);
73 }
74
75 let ptr = allocator.allocate(layout)?;
76
77 Ok(Self {
78 ptr,
79 layout,
80 allocator,
81 })
82 }
83
84 /// Creates a new zeroed allocation with the given layout using the provided allocator.
85 ///
86 /// This is equivalent to calling [`Allocator::allocate_zeroed`] but provides automatic
87 /// cleanup when the allocation is no longer needed.
88 ///
89 /// # Errors
90 ///
91 /// Returns [`AllocError`] if the allocator reports an error or if the layout
92 /// has a size of 0.
93 pub fn new_zeroed_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
94 if layout.size() == 0 {
95 return Err(AllocError);
96 }
97
98 let ptr = allocator.allocate_zeroed(layout)?;
99
100 Ok(Self {
101 ptr,
102 layout,
103 allocator,
104 })
105 }
106
107 /// Attempts to grow the allocation to the new layout.
108 ///
109 /// # Errors
110 ///
111 /// Returns [`AllocError`] if:
112 /// - The allocator reports an error
113 /// - The new layout has a size of 0
114 /// - The new size is smaller than the current size (use [`Self::shrink`] instead)
115 ///
116 /// # Example
117 ///
118 /// ```rust
119 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
120 ///
121 /// use safe_allocator_api::allocator_api::*;
122 /// use safe_allocator_api::RawAlloc;
123 ///
124 /// let layout = Layout::array::<u8>(100).unwrap();
125 /// let mut alloc = RawAlloc::new(layout)?;
126 ///
127 /// // Grow the allocation
128 /// let new_layout = Layout::array::<u8>(200).unwrap();
129 /// alloc.grow(new_layout)?;
130 /// # Ok::<_, AllocError>(())
131 /// ```
132 pub fn grow(&mut self, new_layout: Layout) -> Result<(), AllocError> {
133 if new_layout.size() == 0 {
134 return Err(AllocError);
135 }
136 if new_layout.size() <= self.layout.size() {
137 return Err(AllocError);
138 }
139
140 let new_ptr = unsafe {
141 self.allocator.grow(
142 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
143 self.layout,
144 new_layout,
145 )?
146 };
147
148 self.ptr = new_ptr;
149 self.layout = new_layout;
150 Ok(())
151 }
152
153 /// Attempts to grow the allocation to the new layout, zeroing the additional memory.
154 ///
155 /// This is equivalent to [`Self::grow`] but ensures any additional memory is zeroed.
156 ///
157 /// # Errors
158 ///
159 /// Returns [`AllocError`] if:
160 /// - The allocator reports an error
161 /// - The new layout has a size of 0
162 /// - The new size is smaller than the current size (use [`Self::shrink`] instead)
163 pub fn grow_zeroed(&mut self, new_layout: Layout) -> Result<(), AllocError> {
164 if new_layout.size() == 0 {
165 return Err(AllocError);
166 }
167 if new_layout.size() <= self.layout.size() {
168 return Err(AllocError);
169 }
170
171 let new_ptr = unsafe {
172 self.allocator.grow_zeroed(
173 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
174 self.layout,
175 new_layout,
176 )?
177 };
178
179 self.ptr = new_ptr;
180 self.layout = new_layout;
181 Ok(())
182 }
183
184 /// Attempts to shrink the allocation to the new layout.
185 ///
186 /// # Errors
187 ///
188 /// Returns [`AllocError`] if:
189 /// - The allocator reports an error
190 /// - The new layout has a size of 0
191 /// - The new size is larger than the current size (use [`Self::grow`] instead)
192 ///
193 /// # Example
194 ///
195 /// ```rust
196 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
197 ///
198 /// use safe_allocator_api::allocator_api::*;
199 /// use safe_allocator_api::RawAlloc;
200 ///
201 /// let layout = Layout::array::<u8>(200).unwrap();
202 /// let mut alloc = RawAlloc::new(layout)?;
203 ///
204 /// // Shrink the allocation
205 /// let new_layout = Layout::array::<u8>(100).unwrap();
206 /// alloc.shrink(new_layout)?;
207 /// # Ok::<_, AllocError>(())
208 /// ```
209 pub fn shrink(&mut self, new_layout: Layout) -> Result<(), AllocError> {
210 if new_layout.size() == 0 {
211 return Err(AllocError);
212 }
213 if new_layout.size() >= self.layout.size() {
214 return Err(AllocError);
215 }
216
217 let new_ptr = unsafe {
218 self.allocator.shrink(
219 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
220 self.layout,
221 new_layout,
222 )?
223 };
224
225 self.ptr = new_ptr;
226 self.layout = new_layout;
227 Ok(())
228 }
229
230 /// Returns a raw pointer to the allocated memory.
231 ///
232 /// # Safety
233 ///
234 /// The caller must ensure that the memory is accessed according to
235 /// the original layout constraints.
236 pub fn as_ptr(&self) -> *const u8 {
237 self.ptr.as_ptr() as *const u8
238 }
239
240 /// Returns a raw mutable pointer to the allocated memory.
241 ///
242 /// # Safety
243 ///
244 /// The caller must ensure that the memory is accessed according to
245 /// the original layout constraints.
246 pub fn as_mut_ptr(&mut self) -> *mut u8 {
247 self.ptr.as_ptr() as *mut u8
248 }
249
250 /// Returns a slice reference to the allocated memory.
251 ///
252 /// This provides a safe interface to access the allocated memory as a byte slice.
253 ///
254 /// # Example
255 ///
256 /// ```rust
257 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
258 ///
259 /// use safe_allocator_api::allocator_api::*;
260 /// use safe_allocator_api::RawAlloc;
261 ///
262 /// let layout = Layout::array::<u8>(100).unwrap();
263 /// let alloc = RawAlloc::new(layout)?;
264 /// let slice = alloc.as_slice();
265 /// assert_eq!(slice.len(), 100);
266 /// # Ok::<_, AllocError>(())
267 /// ```
268 pub fn as_slice(&self) -> &[u8] {
269 unsafe { core::slice::from_raw_parts(self.as_ptr(), self.layout.size()) }
270 }
271
272 /// Returns a mutable slice reference to the allocated memory.
273 ///
274 /// This provides a safe interface to access the allocated memory as a mutable byte slice.
275 ///
276 /// # Example
277 ///
278 /// ```rust
279 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
280 ///
281 /// use safe_allocator_api::allocator_api::*;
282 /// use safe_allocator_api::RawAlloc;
283 ///
284 /// let layout = Layout::array::<u8>(100).unwrap();
285 /// let mut alloc = RawAlloc::new(layout)?;
286 /// let slice = alloc.as_mut_slice();
287 /// slice[0] = 42;
288 /// assert_eq!(slice[0], 42);
289 /// # Ok::<_, AllocError>(())
290 /// ```
291 pub fn as_mut_slice(&mut self) -> &mut [u8] {
292 unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.layout.size()) }
293 }
294
295 /// Returns the layout used for this allocation.
296 pub fn layout(&self) -> Layout {
297 self.layout
298 }
299
300 /// Represents the length of the allocation.
301 ///
302 /// # Remarks
303 ///
304 /// This is the length with which the allocation was created with, extracted
305 /// from the [`Layout`]; in practice, due to alignment, the number of available
306 /// bytes may be slightly larger in practice; but you shouldn't rely on that.
307 pub fn len(&self) -> usize {
308 self.layout.size()
309 }
310
311 /// Checks if the allocation is empty.
312 pub fn is_empty(&self) -> bool {
313 self.len() == 0
314 }
315}
316
317impl<A: Allocator> Drop for RawAlloc<A> {
318 fn drop(&mut self) {
319 unsafe {
320 self.allocator.deallocate(
321 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
322 self.layout,
323 );
324 }
325 }
326}
327
328impl<A: Allocator> fmt::Debug for RawAlloc<A> {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 f.debug_struct("RawAlloc")
331 .field("ptr", &self.ptr)
332 .field("layout", &self.layout)
333 .finish()
334 }
335}
336
337// Convenience constructors using the Global allocator
338impl RawAlloc {
339 /// Creates a new allocation with the given layout using the global allocator.
340 ///
341 /// This is equivalent to calling [`Self::new_in`] with the global allocator.
342 pub fn new(layout: Layout) -> Result<Self, AllocError> {
343 Self::new_in(layout, Global)
344 }
345
346 /// Creates a new zeroed allocation with the given layout using the global allocator.
347 ///
348 /// This is equivalent to calling [`Self::new_zeroed_in`] with the global allocator.
349 pub fn new_zeroed(layout: Layout) -> Result<Self, AllocError> {
350 Self::new_zeroed_in(layout, Global)
351 }
352}
353
354// Cannot implement Send + Sync automatically due to the raw pointer
355// Users must opt-in by implementing these traits based on their usage
356unsafe impl<A: Allocator> Send for RawAlloc<A> {}
357unsafe impl<A: Allocator> Sync for RawAlloc<A> {}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use core::mem::size_of;
363
364 #[test]
365 fn zero_sized_alloc_returns_error() {
366 let layout = Layout::from_size_align(0, 1).unwrap();
367 assert!(RawAlloc::new(layout).is_err());
368 }
369
370 #[test]
371 fn basic_alloc_and_write() {
372 let layout = Layout::new::<u32>();
373 let mut alloc = RawAlloc::new(layout).unwrap();
374
375 unsafe {
376 core::ptr::write(alloc.as_mut_ptr() as *mut u32, 0xDEADBEEF);
377 assert_eq!(core::ptr::read(alloc.as_ptr() as *const u32), 0xDEADBEEF);
378 }
379 }
380
381 #[test]
382 fn zeroed_allocation() {
383 let size = 1024;
384 let layout = Layout::array::<u8>(size).unwrap();
385 let alloc = RawAlloc::new_zeroed(layout).unwrap();
386
387 unsafe {
388 let slice = core::slice::from_raw_parts(alloc.as_ptr(), size);
389 assert!(slice.iter().all(|&x| x == 0));
390 }
391 }
392
393 #[test]
394 fn custom_allocator() {
395 let layout = Layout::new::<i32>();
396 let alloc = RawAlloc::new_in(layout, Global).unwrap();
397 assert_eq!(alloc.layout().size(), size_of::<i32>());
398 }
399
400 #[test]
401 fn array_allocation() {
402 let elements = 100;
403 let layout = Layout::array::<u64>(elements).unwrap();
404 let mut alloc = RawAlloc::new(layout).unwrap();
405
406 unsafe {
407 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr() as *mut u64, elements);
408
409 for (i, item) in slice.iter_mut().enumerate() {
410 *item = i as u64;
411 }
412
413 assert_eq!(slice[42], 42);
414 }
415 }
416
417 #[test]
418 fn alignment_requirements() {
419 let align = 64; // Test a large alignment
420 let size = 128;
421 let layout = Layout::from_size_align(size, align).unwrap();
422 let alloc = RawAlloc::new(layout).unwrap();
423
424 let addr = alloc.as_ptr() as usize;
425 assert_eq!(addr % align, 0, "Allocation not properly aligned");
426 }
427
428 #[test]
429 fn multiple_allocations() {
430 let layout = Layout::new::<u8>();
431 let mut allocations = Vec::new();
432
433 // Create many allocations to stress the allocator
434 for i in 0..100 {
435 let mut alloc = RawAlloc::new(layout).unwrap();
436 unsafe {
437 core::ptr::write(alloc.as_mut_ptr(), i as u8);
438 }
439 allocations.push(alloc);
440 }
441
442 // Verify each allocation is independent
443 for (i, alloc) in allocations.iter().enumerate() {
444 unsafe {
445 assert_eq!(core::ptr::read(alloc.as_ptr()), i as u8);
446 }
447 }
448 }
449
450 #[test]
451 fn oversized_allocation() {
452 // Try to allocate a very large size (but not so large it would definitely fail)
453 let layout = Layout::array::<u8>(1024 * 1024).unwrap();
454 let result = RawAlloc::new(layout);
455
456 // We don't assert success or failure here, as it depends on the system,
457 // but we verify it doesn't panic
458 let _ = result.is_ok();
459 }
460
461 #[test]
462 fn grow_allocation() {
463 let initial_size = 100;
464 let layout = Layout::array::<u8>(initial_size).unwrap();
465 let mut alloc = RawAlloc::new(layout).unwrap();
466
467 // Write some data
468 unsafe {
469 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
470 slice[0] = 42;
471 }
472
473 // Grow the allocation
474 let new_size = 200;
475 let new_layout = Layout::array::<u8>(new_size).unwrap();
476 alloc.grow(new_layout).unwrap();
477
478 // Verify the data is preserved
479 unsafe {
480 let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
481 assert_eq!(slice[0], 42);
482 }
483 }
484
485 #[test]
486 fn grow_zeroed_allocation() {
487 let initial_size = 100;
488 let layout = Layout::array::<u8>(initial_size).unwrap();
489 let mut alloc = RawAlloc::new(layout).unwrap();
490
491 // Write some data
492 unsafe {
493 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
494 slice[0] = 42;
495 }
496
497 // Grow the allocation
498 let new_size = 200;
499 let new_layout = Layout::array::<u8>(new_size).unwrap();
500 alloc.grow_zeroed(new_layout).unwrap();
501
502 unsafe {
503 let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
504 // Verify original data is preserved
505 assert_eq!(slice[0], 42);
506 // Verify new memory is zeroed
507 assert!(slice[initial_size..].iter().all(|&x| x == 0));
508 }
509 }
510
511 #[test]
512 fn shrink_allocation() {
513 let initial_size = 200;
514 let layout = Layout::array::<u8>(initial_size).unwrap();
515 let mut alloc = RawAlloc::new(layout).unwrap();
516
517 // Write some data
518 unsafe {
519 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
520 slice[0] = 42;
521 }
522
523 // Shrink the allocation
524 let new_size = 100;
525 let new_layout = Layout::array::<u8>(new_size).unwrap();
526 alloc.shrink(new_layout).unwrap();
527
528 // Verify the data is preserved
529 unsafe {
530 let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
531 assert_eq!(slice[0], 42);
532 }
533 }
534
535 #[test]
536 fn grow_zero_size_fails() {
537 let layout = Layout::array::<u8>(100).unwrap();
538 let mut alloc = RawAlloc::new(layout).unwrap();
539
540 let new_layout = Layout::from_size_align(0, 1).unwrap();
541 assert!(alloc.grow(new_layout).is_err());
542 }
543
544 #[test]
545 fn shrink_zero_size_fails() {
546 let layout = Layout::array::<u8>(100).unwrap();
547 let mut alloc = RawAlloc::new(layout).unwrap();
548
549 let new_layout = Layout::from_size_align(0, 1).unwrap();
550 assert!(alloc.shrink(new_layout).is_err());
551 }
552
553 #[test]
554 fn grow_smaller_size_fails() {
555 let layout = Layout::array::<u8>(200).unwrap();
556 let mut alloc = RawAlloc::new(layout).unwrap();
557
558 let new_layout = Layout::array::<u8>(100).unwrap();
559 assert!(alloc.grow(new_layout).is_err());
560 }
561
562 #[test]
563 fn shrink_larger_size_fails() {
564 let layout = Layout::array::<u8>(100).unwrap();
565 let mut alloc = RawAlloc::new(layout).unwrap();
566
567 let new_layout = Layout::array::<u8>(200).unwrap();
568 assert!(alloc.shrink(new_layout).is_err());
569 }
570}