Skip to main content

static_alloc/
allocator_api2.rs

1use crate::{
2    bump::{Bump, BumpSlice, BumpView},
3    unsync,
4};
5
6use allocator_api2::alloc::{AllocError, Allocator, Layout};
7use core::ptr::NonNull;
8
9unsafe impl<T> Allocator for Bump<T> {
10    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
11        Allocator::allocate(&self.as_view(), layout)
12    }
13
14    unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
15
16    unsafe fn shrink(
17        &self,
18        ptr: NonNull<u8>,
19        old_layout: Layout,
20        new_layout: Layout,
21    ) -> Result<NonNull<[u8]>, AllocError> {
22        // Safety: passing along requirements. These two allocators serve the same allocations, a
23        // property we permit for these two of our own types.
24        unsafe { Allocator::shrink(&self.as_view(), ptr, old_layout, new_layout) }
25    }
26}
27
28unsafe impl Allocator for BumpSlice {
29    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
30        Allocator::allocate(&self.as_view(), layout)
31    }
32
33    unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
34
35    unsafe fn shrink(
36        &self,
37        ptr: NonNull<u8>,
38        old_layout: Layout,
39        new_layout: Layout,
40    ) -> Result<NonNull<[u8]>, AllocError> {
41        // Safety: passing along requirements. These two allocators serve the same allocations, a
42        // property we permit for these two of our own types.
43        unsafe { Allocator::shrink(&self.as_view(), ptr, old_layout, new_layout) }
44    }
45}
46
47unsafe impl Allocator for BumpView<'_> {
48    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
49        let len = layout.size();
50        match self.get_layout(layout) {
51            None => Err(AllocError),
52            Some(allocation) => Ok(NonNull::slice_from_raw_parts(allocation.ptr, len)),
53        }
54    }
55
56    unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
57
58    unsafe fn shrink(
59        &self,
60        ptr: NonNull<u8>,
61        old_layout: Layout,
62        new_layout: Layout,
63    ) -> Result<NonNull<[u8]>, AllocError> {
64        // Safety: Caller guarantees `ptr` was allocated from `self` (or equivalent, for transitive
65        // use of this) which requires it to be valid and described by `old_layout`.
66        unsafe { shrink_in_place(ptr, old_layout, new_layout) }
67    }
68}
69
70unsafe impl<T> Allocator for unsync::Bump<T> {
71    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
72        <unsync::BumpSlice as Allocator>::allocate(self, layout)
73    }
74
75    unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
76
77    unsafe fn shrink(
78        &self,
79        ptr: NonNull<u8>,
80        old_layout: Layout,
81        new_layout: Layout,
82    ) -> Result<NonNull<[u8]>, AllocError> {
83        // Safety: passing along requirements. These two allocators serve the same allocations, a
84        // property we permit for these two of our own types.
85        unsafe { <unsync::BumpSlice as Allocator>::shrink(self, ptr, old_layout, new_layout) }
86    }
87}
88
89unsafe impl Allocator for unsync::BumpSlice {
90    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
91        let len = layout.size();
92        match self.alloc(layout) {
93            None => Err(AllocError),
94            Some(allocation) => Ok(NonNull::slice_from_raw_parts(allocation, len)),
95        }
96    }
97
98    unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
99
100    unsafe fn shrink(
101        &self,
102        ptr: NonNull<u8>,
103        old_layout: Layout,
104        new_layout: Layout,
105    ) -> Result<NonNull<[u8]>, AllocError> {
106        // Safety: Caller guarantees `ptr` was allocated from `self` (or equivalent, for transitive
107        // use of this) which requires it to be valid and described by `old_layout`.
108        unsafe { shrink_in_place(ptr, old_layout, new_layout) }
109    }
110}
111
112/// Safety: caller must only call this on `ptr` point to a valid allocation with the fitting layout
113/// `old_layout`. Returns a derived pointer into the same allocation on success.
114unsafe fn shrink_in_place(
115    ptr: NonNull<u8>,
116    old_layout: Layout,
117    new_layout: Layout,
118) -> Result<NonNull<[u8]>, AllocError> {
119    debug_assert!(new_layout.size() <= old_layout.size());
120    let len = new_layout.size();
121
122    let offset = ptr.align_offset(new_layout.align());
123
124    if offset > 0 {
125        if old_layout
126            .size()
127            .checked_sub(offset)
128            .is_none_or(|n| n < len)
129        {
130            // Won't fit in-place. Sorry.
131            return Err(AllocError);
132        }
133
134        // Safety: in-bounds as we just verified that old layout has at least as many bytes as
135        // offset, and the caller was required to pass a live allocation with corresponding
136        // layout; implying that it also has that many bytes.
137        let dst = unsafe { ptr.byte_add(offset) };
138        // Safety: just verified that layout has at least `len` bytes after the offset so `dst`
139        // also has provenance according to the caller's requirements.
140        unsafe { ptr.copy_to(dst, len) };
141        dst
142    } else {
143        ptr
144    };
145
146    Ok(NonNull::slice_from_raw_parts(ptr, len))
147}