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 alloc::vec::Vec;
363    use core::mem::size_of;
364
365    #[test]
366    fn zero_sized_alloc_returns_error() {
367        let layout = Layout::from_size_align(0, 1).unwrap();
368        assert!(RawAlloc::new(layout).is_err());
369    }
370
371    #[test]
372    fn basic_alloc_and_write() {
373        let layout = Layout::new::<u32>();
374        let mut alloc = RawAlloc::new(layout).unwrap();
375
376        unsafe {
377            core::ptr::write(alloc.as_mut_ptr() as *mut u32, 0xDEADBEEF);
378            assert_eq!(core::ptr::read(alloc.as_ptr() as *const u32), 0xDEADBEEF);
379        }
380    }
381
382    #[test]
383    fn zeroed_allocation() {
384        let size = 1024;
385        let layout = Layout::array::<u8>(size).unwrap();
386        let alloc = RawAlloc::new_zeroed(layout).unwrap();
387
388        unsafe {
389            let slice = core::slice::from_raw_parts(alloc.as_ptr(), size);
390            assert!(slice.iter().all(|&x| x == 0));
391        }
392    }
393
394    #[test]
395    fn custom_allocator() {
396        let layout = Layout::new::<i32>();
397        let alloc = RawAlloc::new_in(layout, Global).unwrap();
398        assert_eq!(alloc.layout().size(), size_of::<i32>());
399    }
400
401    #[test]
402    fn array_allocation() {
403        let elements = 100;
404        let layout = Layout::array::<u64>(elements).unwrap();
405        let mut alloc = RawAlloc::new(layout).unwrap();
406
407        unsafe {
408            let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr() as *mut u64, elements);
409
410            for (i, item) in slice.iter_mut().enumerate() {
411                *item = i as u64;
412            }
413
414            assert_eq!(slice[42], 42);
415        }
416    }
417
418    #[test]
419    fn alignment_requirements() {
420        let align = 64; // Test a large alignment
421        let size = 128;
422        let layout = Layout::from_size_align(size, align).unwrap();
423        let alloc = RawAlloc::new(layout).unwrap();
424
425        let addr = alloc.as_ptr() as usize;
426        assert_eq!(addr % align, 0, "Allocation not properly aligned");
427    }
428
429    #[test]
430    fn multiple_allocations() {
431        let layout = Layout::new::<u8>();
432        let mut allocations = Vec::new();
433
434        // Create many allocations to stress the allocator
435        for i in 0..100 {
436            let mut alloc = RawAlloc::new(layout).unwrap();
437            unsafe {
438                core::ptr::write(alloc.as_mut_ptr(), i as u8);
439            }
440            allocations.push(alloc);
441        }
442
443        // Verify each allocation is independent
444        for (i, alloc) in allocations.iter().enumerate() {
445            unsafe {
446                assert_eq!(core::ptr::read(alloc.as_ptr()), i as u8);
447            }
448        }
449    }
450
451    #[test]
452    fn oversized_allocation() {
453        // Try to allocate a very large size (but not so large it would definitely fail)
454        let layout = Layout::array::<u8>(1024 * 1024).unwrap();
455        let result = RawAlloc::new(layout);
456
457        // We don't assert success or failure here, as it depends on the system,
458        // but we verify it doesn't panic
459        let _ = result.is_ok();
460    }
461
462    #[test]
463    fn grow_allocation() {
464        let initial_size = 100;
465        let layout = Layout::array::<u8>(initial_size).unwrap();
466        let mut alloc = RawAlloc::new(layout).unwrap();
467
468        // Write some data
469        unsafe {
470            let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
471            slice[0] = 42;
472        }
473
474        // Grow the allocation
475        let new_size = 200;
476        let new_layout = Layout::array::<u8>(new_size).unwrap();
477        alloc.grow(new_layout).unwrap();
478
479        // Verify the data is preserved
480        unsafe {
481            let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
482            assert_eq!(slice[0], 42);
483        }
484    }
485
486    #[test]
487    fn grow_zeroed_allocation() {
488        let initial_size = 100;
489        let layout = Layout::array::<u8>(initial_size).unwrap();
490        let mut alloc = RawAlloc::new(layout).unwrap();
491
492        // Write some data
493        unsafe {
494            let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
495            slice[0] = 42;
496        }
497
498        // Grow the allocation
499        let new_size = 200;
500        let new_layout = Layout::array::<u8>(new_size).unwrap();
501        alloc.grow_zeroed(new_layout).unwrap();
502
503        unsafe {
504            let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
505            // Verify original data is preserved
506            assert_eq!(slice[0], 42);
507            // Verify new memory is zeroed
508            assert!(slice[initial_size..].iter().all(|&x| x == 0));
509        }
510    }
511
512    #[test]
513    fn shrink_allocation() {
514        let initial_size = 200;
515        let layout = Layout::array::<u8>(initial_size).unwrap();
516        let mut alloc = RawAlloc::new(layout).unwrap();
517
518        // Write some data
519        unsafe {
520            let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
521            slice[0] = 42;
522        }
523
524        // Shrink the allocation
525        let new_size = 100;
526        let new_layout = Layout::array::<u8>(new_size).unwrap();
527        alloc.shrink(new_layout).unwrap();
528
529        // Verify the data is preserved
530        unsafe {
531            let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
532            assert_eq!(slice[0], 42);
533        }
534    }
535
536    #[test]
537    fn grow_zero_size_fails() {
538        let layout = Layout::array::<u8>(100).unwrap();
539        let mut alloc = RawAlloc::new(layout).unwrap();
540
541        let new_layout = Layout::from_size_align(0, 1).unwrap();
542        assert!(alloc.grow(new_layout).is_err());
543    }
544
545    #[test]
546    fn shrink_zero_size_fails() {
547        let layout = Layout::array::<u8>(100).unwrap();
548        let mut alloc = RawAlloc::new(layout).unwrap();
549
550        let new_layout = Layout::from_size_align(0, 1).unwrap();
551        assert!(alloc.shrink(new_layout).is_err());
552    }
553
554    #[test]
555    fn grow_smaller_size_fails() {
556        let layout = Layout::array::<u8>(200).unwrap();
557        let mut alloc = RawAlloc::new(layout).unwrap();
558
559        let new_layout = Layout::array::<u8>(100).unwrap();
560        assert!(alloc.grow(new_layout).is_err());
561    }
562
563    #[test]
564    fn shrink_larger_size_fails() {
565        let layout = Layout::array::<u8>(100).unwrap();
566        let mut alloc = RawAlloc::new(layout).unwrap();
567
568        let new_layout = Layout::array::<u8>(200).unwrap();
569        assert!(alloc.shrink(new_layout).is_err());
570    }
571}