static_alloc/allocator_api2.rs
1use crate::{
2 bump::{Bump, BumpSlice, BumpView, Level},
3 unsync,
4};
5
6use allocator_api2::alloc::{AllocError, Allocator, Layout};
7use core::{cell, 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}
148
149/// Wrapper around an allocator, that gathers stats about the underlying store while allocating.
150///
151/// The `allocator_api2` design intentionally avoids generics in its result types. Allocations in
152/// the bump arena however provide some auxiliary information such as the usage level of the bump
153/// before and after an allocation (at least a snapshot of it for a Sync allocator). This
154/// information can be especially useful in case of an allocation failure but gather it separately
155/// would be racy. Allocating with this wrapper avoids the race.
156///
157/// For these use cases you can replace the allocator handle with this wrapper. The wrapper is
158/// [equivalent] to the underlying allocator.
159///
160/// [equivalent]: https://doc.rust-lang.org/stable/std/alloc/trait.Allocator.html#equivalent-allocators
161///
162/// # Usage
163///
164/// ```
165/// use static_alloc::allocator_api2::WithAllocationInfo;
166/// use static_alloc::bump::Bump;
167/// ```
168///
169/// # Design
170///
171/// Interestingly, we could also provide per-call allocator configuration. For a `Bump` shared
172/// between threads for instance there is an inner loop around a fallible CAS. A parameter could
173/// constrain this loop to exit early on a case-by-case basis. It does not make too much sense to
174/// enforce it for all allocations in the arena. You would often prefer to use a non-racy
175/// thread-local arena in those cases (please open an issue if you have a use case). A handle
176/// wrapper on the other hand would provide surgical amounts of control.
177pub struct WithAllocationInfo<A> {
178 /// The placement of the last allocation, as byte offset in the arena.
179 allocation: cell::Cell<Level>,
180 /// The observed usage of the allocator.
181 observed_level: cell::Cell<Level>,
182 /// We could have a type-erased other type where this is a direct `BumpView` instead. Making
183 /// this work would require complicated generics though.
184 inner: A,
185}
186
187impl<'lt, A: ?Sized> WithAllocationInfo<&'lt A> {
188 /// Wrap an arena to gather stats.
189 ///
190 /// This does not constrain the type `A`. However, it can only be sensibly used with a
191 /// [`Bump`](`crate::bump::Bump`) or [`BumpSlice`](`crate::bump::BumpSlice`) or the unsync
192 /// [`BumpSlice`](`crate::unsync::BumpSlice`). It does *not* work with unsync's `Bump` due to
193 /// current design differences. (It is implemented with Deref into a `BumpSlice` instead of a
194 /// view used for both. This will post-monomorphization error which we do not want to hide).
195 pub fn new(arena: &'lt A) -> Self {
196 WithAllocationInfo {
197 allocation: cell::Cell::new(Level(0)),
198 observed_level: cell::Cell::new(Level(0)),
199 inner: arena,
200 }
201 }
202}
203
204impl<A> WithAllocationInfo<A> {
205 /// Query the consumed level observed by the last allocation call.
206 ///
207 /// The value is updated both on success and failure.
208 ///
209 /// # Usage
210 ///
211 /// You will get information about the consumed bytes with each attempted allocation.
212 ///
213 /// ```
214 /// use static_alloc::allocator_api2::WithAllocationInfo;
215 /// use static_alloc::bump::Bump;
216 /// use allocator_api2::vec::Vec;
217 ///
218 /// let arena = Bump::<[usize; 4]>::uninit();
219 /// let hdl = WithAllocationInfo::new(&arena);
220 ///
221 /// let mut vec = Vec::<u32, _>::with_capacity_in(1, hdl);
222 /// assert_eq!(vec.allocator().last_observed_level().byte_offset(), 4);
223 ///
224 /// // This will not fit, and fail to allocate.
225 /// vec.try_reserve(32);
226 /// assert_eq!(vec.allocator().last_observed_level().byte_offset(), 4);
227 ///
228 /// // This will, reallocating the vector.
229 /// vec.try_reserve_exact(2);
230 /// assert_eq!(vec.allocator().last_observed_level().byte_offset(), 12);
231 /// ```
232 ///
233 /// The information is updated also when the arena is used concurrently for another purpose,
234 /// too.
235 ///
236 /// ```
237 /// use static_alloc::allocator_api2::WithAllocationInfo;
238 /// use static_alloc::bump::Bump;
239 /// use allocator_api2::vec::Vec;
240 ///
241 /// let arena = Bump::<[usize; 4]>::uninit();
242 /// let hdl = WithAllocationInfo::new(&arena);
243 ///
244 /// let mut vec = Vec::<u32, _>::new_in(hdl);
245 /// let _ = arena.leak(0u32);
246 ///
247 /// // Afterwards exactly three `u32` allocator, the one leaked and the 2 reserved.
248 /// vec.try_reserve_exact(2);
249 /// assert_eq!(vec.allocator().last_observed_level().byte_offset(), 12);
250 /// ```
251 pub fn last_observed_level(&self) -> Level {
252 self.observed_level.get()
253 }
254
255 /// Get the [`Level`] at which the last successful allocation sits.
256 ///
257 /// This is provided for all allocations, sized and unsized.
258 ///
259 /// # Usage
260 ///
261 /// ```
262 /// use static_alloc::allocator_api2::WithAllocationInfo;
263 /// use static_alloc::bump::Bump;
264 /// use allocator_api2::vec::Vec;
265 ///
266 /// let arena = Bump::<[usize; 4]>::uninit();
267 /// let hdl = WithAllocationInfo::new(&arena);
268 ///
269 /// let mut vec = Vec::<u32, _>::with_capacity_in(1, hdl);
270 /// assert_eq!(vec.allocator().last_allocation_start().byte_offset(), 0);
271 ///
272 /// // This will reallocate the vector, after the previous one.
273 /// vec.try_reserve_exact(2);
274 /// assert_eq!(vec.allocator().last_allocation_start().byte_offset(), 4);
275 /// ```
276 pub fn last_allocation_start(&self) -> Level {
277 self.allocation.get()
278 }
279}
280
281unsafe impl<T> Allocator for WithAllocationInfo<&'_ Bump<T>> {
282 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
283 let len = layout.size();
284 match self.inner.as_view().try_alloc(layout) {
285 Ok(allocation) => {
286 self.allocation.set(Level(allocation.level.0 - len));
287 self.observed_level.set(allocation.level);
288 Ok(NonNull::slice_from_raw_parts(allocation.ptr, len))
289 }
290 Err(level) => {
291 self.observed_level.set(level);
292 Err(AllocError)
293 }
294 }
295 }
296
297 unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
298
299 unsafe fn shrink(
300 &self,
301 ptr: NonNull<u8>,
302 old_layout: Layout,
303 new_layout: Layout,
304 ) -> Result<NonNull<[u8]>, AllocError> {
305 // Safety: forwarding arguments, i.e requirements, to an equivalent allocator.
306 unsafe { Allocator::shrink(&self.inner, ptr, old_layout, new_layout) }
307 }
308}
309
310unsafe impl Allocator for WithAllocationInfo<&'_ BumpSlice> {
311 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
312 let len = layout.size();
313 match self.inner.as_view().try_alloc(layout) {
314 Ok(allocation) => {
315 self.allocation.set(Level(allocation.level.0 - len));
316 self.observed_level.set(allocation.level);
317 Ok(NonNull::slice_from_raw_parts(allocation.ptr, len))
318 }
319 Err(level) => {
320 self.observed_level.set(level);
321 Err(AllocError)
322 }
323 }
324 }
325
326 unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
327
328 unsafe fn shrink(
329 &self,
330 ptr: NonNull<u8>,
331 old_layout: Layout,
332 new_layout: Layout,
333 ) -> Result<NonNull<[u8]>, AllocError> {
334 // Safety: forwarding arguments, i.e requirements, to an equivalent allocator.
335 unsafe { Allocator::shrink(&self.inner, ptr, old_layout, new_layout) }
336 }
337}
338
339// Not implemented for `unsync::Bump<T>` due to having to deref internally then.
340unsafe impl Allocator for WithAllocationInfo<&'_ unsync::BumpSlice> {
341 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
342 let len = layout.size();
343 match self.inner.try_alloc(layout) {
344 Ok(allocation) => {
345 self.allocation.set(Level(allocation.level.0 - len));
346 self.observed_level.set(allocation.level);
347 Ok(NonNull::slice_from_raw_parts(allocation.ptr, len))
348 }
349 Err(level) => {
350 self.observed_level.set(level);
351 Err(AllocError)
352 }
353 }
354 }
355
356 unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
357
358 unsafe fn shrink(
359 &self,
360 ptr: NonNull<u8>,
361 old_layout: Layout,
362 new_layout: Layout,
363 ) -> Result<NonNull<[u8]>, AllocError> {
364 // Safety: forwarding arguments, i.e requirements, to an equivalent allocator.
365 unsafe { Allocator::shrink(&self.inner, ptr, old_layout, new_layout) }
366 }
367}