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::prelude::*;
6use core::fmt;
7use core::ptr::NonNull;
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    /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
61    ///
62    /// use safe_allocator_api::prelude::*;
63    /// use safe_allocator_api::RawAlloc;
64    ///
65    /// let layout = Layout::new::<u64>();
66    /// let alloc = RawAlloc::new_in(layout, Global)?;
67    /// # Ok::<_, AllocError>(())
68    /// ```
69    pub fn new_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
70        if layout.size() == 0 {
71            return Err(AllocError);
72        }
73
74        let ptr = allocator.allocate(layout)?;
75
76        Ok(Self {
77            ptr,
78            layout,
79            allocator,
80        })
81    }
82
83    /// Creates a new zeroed allocation with the given layout using the provided allocator.
84    ///
85    /// This is equivalent to calling [`Allocator::allocate_zeroed`] but provides automatic
86    /// cleanup when the allocation is no longer needed.
87    ///
88    /// # Errors
89    ///
90    /// Returns [`AllocError`] if the allocator reports an error or if the layout
91    /// has a size of 0.
92    pub fn new_zeroed_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
93        if layout.size() == 0 {
94            return Err(AllocError);
95        }
96
97        let ptr = allocator.allocate_zeroed(layout)?;
98
99        Ok(Self {
100            ptr,
101            layout,
102            allocator,
103        })
104    }
105
106    /// Attempts to grow the allocation to the new layout.
107    ///
108    /// # Errors
109    ///
110    /// Returns [`AllocError`] if:
111    /// - The allocator reports an error
112    /// - The new layout has a size of 0
113    /// - The new size is smaller than the current size (use [`Self::shrink`] instead)
114    ///
115    /// # Example
116    ///
117    /// ```rust
118    /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
119    ///
120    /// use safe_allocator_api::prelude::*;
121    /// use safe_allocator_api::RawAlloc;
122    ///
123    /// let layout = Layout::array::<u8>(100).unwrap();
124    /// let mut alloc = RawAlloc::new(layout)?;
125    ///
126    /// // Grow the allocation
127    /// let new_layout = Layout::array::<u8>(200).unwrap();
128    /// alloc.grow(new_layout)?;
129    /// # Ok::<_, AllocError>(())
130    /// ```
131    pub fn grow(&mut self, new_layout: Layout) -> Result<(), AllocError> {
132        if new_layout.size() == 0 {
133            return Err(AllocError);
134        }
135        if new_layout.size() <= self.layout.size() {
136            return Err(AllocError);
137        }
138
139        let new_ptr = unsafe {
140            self.allocator.grow(
141                NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
142                self.layout,
143                new_layout,
144            )?
145        };
146
147        self.ptr = new_ptr;
148        self.layout = new_layout;
149        Ok(())
150    }
151
152    /// Attempts to grow the allocation to the new layout, zeroing the additional memory.
153    ///
154    /// This is equivalent to [`Self::grow`] but ensures any additional memory is zeroed.
155    ///
156    /// # Errors
157    ///
158    /// Returns [`AllocError`] if:
159    /// - The allocator reports an error
160    /// - The new layout has a size of 0
161    /// - The new size is smaller than the current size (use [`Self::shrink`] instead)
162    pub fn grow_zeroed(&mut self, new_layout: Layout) -> Result<(), AllocError> {
163        if new_layout.size() == 0 {
164            return Err(AllocError);
165        }
166        if new_layout.size() <= self.layout.size() {
167            return Err(AllocError);
168        }
169
170        let new_ptr = unsafe {
171            self.allocator.grow_zeroed(
172                NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
173                self.layout,
174                new_layout,
175            )?
176        };
177
178        self.ptr = new_ptr;
179        self.layout = new_layout;
180        Ok(())
181    }
182
183    /// Attempts to shrink the allocation to the new layout.
184    ///
185    /// # Errors
186    ///
187    /// Returns [`AllocError`] if:
188    /// - The allocator reports an error
189    /// - The new layout has a size of 0
190    /// - The new size is larger than the current size (use [`Self::grow`] instead)
191    ///
192    /// # Example
193    ///
194    /// ```rust
195    /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
196    ///
197    /// use safe_allocator_api::prelude::*;
198    /// use safe_allocator_api::RawAlloc;
199    ///
200    /// let layout = Layout::array::<u8>(200).unwrap();
201    /// let mut alloc = RawAlloc::new(layout)?;
202    ///
203    /// // Shrink the allocation
204    /// let new_layout = Layout::array::<u8>(100).unwrap();
205    /// alloc.shrink(new_layout)?;
206    /// # Ok::<_, AllocError>(())
207    /// ```
208    pub fn shrink(&mut self, new_layout: Layout) -> Result<(), AllocError> {
209        if new_layout.size() == 0 {
210            return Err(AllocError);
211        }
212        if new_layout.size() >= self.layout.size() {
213            return Err(AllocError);
214        }
215
216        let new_ptr = unsafe {
217            self.allocator.shrink(
218                NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
219                self.layout,
220                new_layout,
221            )?
222        };
223
224        self.ptr = new_ptr;
225        self.layout = new_layout;
226        Ok(())
227    }
228
229    /// Returns a raw pointer to the allocated memory.
230    ///
231    /// # Safety
232    ///
233    /// The caller must ensure that the memory is accessed according to
234    /// the original layout constraints.
235    pub fn as_ptr(&self) -> *const u8 {
236        self.ptr.as_ptr() as *const u8
237    }
238
239    /// Returns a raw mutable pointer to the allocated memory.
240    ///
241    /// # Safety
242    ///
243    /// The caller must ensure that the memory is accessed according to
244    /// the original layout constraints.
245    pub fn as_mut_ptr(&mut self) -> *mut u8 {
246        self.ptr.as_ptr() as *mut u8
247    }
248
249    /// Returns a slice reference to the allocated memory.
250    ///
251    /// This provides a safe interface to access the allocated memory as a byte slice.
252    ///
253    /// # Example
254    ///
255    /// ```rust
256    /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
257    ///
258    /// use safe_allocator_api::prelude::*;
259    /// use safe_allocator_api::RawAlloc;
260    ///
261    /// let layout = Layout::array::<u8>(100).unwrap();
262    /// let alloc = RawAlloc::new(layout)?;
263    /// let slice = alloc.as_slice();
264    /// assert_eq!(slice.len(), 100);
265    /// # Ok::<_, AllocError>(())
266    /// ```
267    pub fn as_slice(&self) -> &[u8] {
268        unsafe { core::slice::from_raw_parts(self.as_ptr(), self.layout.size()) }
269    }
270
271    /// Returns a mutable slice reference to the allocated memory.
272    ///
273    /// This provides a safe interface to access the allocated memory as a mutable byte slice.
274    ///
275    /// # Example
276    ///
277    /// ```rust
278    /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
279    ///
280    /// use safe_allocator_api::prelude::*;
281    /// use safe_allocator_api::RawAlloc;
282    ///
283    /// let layout = Layout::array::<u8>(100).unwrap();
284    /// let mut alloc = RawAlloc::new(layout)?;
285    /// let slice = alloc.as_mut_slice();
286    /// slice[0] = 42;
287    /// assert_eq!(slice[0], 42);
288    /// # Ok::<_, AllocError>(())
289    /// ```
290    pub fn as_mut_slice(&mut self) -> &mut [u8] {
291        unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.layout.size()) }
292    }
293
294    /// Returns the layout used for this allocation.
295    pub fn layout(&self) -> Layout {
296        self.layout
297    }
298
299    /// Represents the length of the allocation.
300    ///
301    /// # Remarks
302    ///
303    /// This is the length with which the allocation was created with, extracted
304    /// from the [`Layout`]; in practice, due to alignment, the number of available
305    /// bytes may be slightly larger in practice; but you shouldn't rely on that.
306    pub fn len(&self) -> usize {
307        self.layout.size()
308    }
309
310    /// Checks if the allocation is empty.
311    pub fn is_empty(&self) -> bool {
312        self.len() == 0
313    }
314}
315
316impl<A: Allocator> Drop for RawAlloc<A> {
317    fn drop(&mut self) {
318        unsafe {
319            self.allocator.deallocate(
320                NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
321                self.layout,
322            );
323        }
324    }
325}
326
327impl<A: Allocator> fmt::Debug for RawAlloc<A> {
328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329        f.debug_struct("RawAlloc")
330            .field("ptr", &self.ptr)
331            .field("layout", &self.layout)
332            .finish()
333    }
334}
335
336// Convenience constructors using the Global allocator
337impl RawAlloc {
338    /// Creates a new allocation with the given layout using the global allocator.
339    ///
340    /// This is equivalent to calling [`Self::new_in`] with the global allocator.
341    pub fn new(layout: Layout) -> Result<Self, AllocError> {
342        Self::new_in(layout, Global)
343    }
344
345    /// Creates a new zeroed allocation with the given layout using the global allocator.
346    ///
347    /// This is equivalent to calling [`Self::new_zeroed_in`] with the global allocator.
348    pub fn new_zeroed(layout: Layout) -> Result<Self, AllocError> {
349        Self::new_zeroed_in(layout, Global)
350    }
351}
352
353// Cannot implement Send + Sync automatically due to the raw pointer
354// Users must opt-in by implementing these traits based on their usage
355unsafe impl<A: Allocator> Send for RawAlloc<A> {}
356unsafe impl<A: Allocator> Sync for RawAlloc<A> {}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361    use alloc::vec::Vec;
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}