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}