bump_scope/traits/
bump_allocator.rs

1use core::{alloc::Layout, ops::Range, ptr::NonNull};
2
3use crate::{
4    BaseAllocator, Bump, BumpScope, Checkpoint, MinimumAlignment, SupportedMinimumAlignment, WithoutDealloc, WithoutShrink,
5    alloc::{AllocError, Allocator},
6    layout::CustomLayout,
7    stats::AnyStats,
8    traits::{assert_dyn_compatible, assert_implements},
9};
10
11pub trait Sealed {}
12
13impl<B: Sealed + ?Sized> Sealed for &B {}
14impl<B: Sealed + ?Sized> Sealed for &mut B {}
15impl<B: Sealed> Sealed for WithoutDealloc<B> {}
16impl<B: Sealed> Sealed for WithoutShrink<B> {}
17
18impl<A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool, const DEALLOCATES: bool> Sealed
19    for Bump<A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED, DEALLOCATES>
20where
21    MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
22    A: BaseAllocator<GUARANTEED_ALLOCATED>,
23{
24}
25
26impl<A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool, const DEALLOCATES: bool> Sealed
27    for BumpScope<'_, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED, DEALLOCATES>
28where
29    MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
30    A: BaseAllocator<GUARANTEED_ALLOCATED>,
31{
32}
33
34/// A bump allocator.
35///
36/// This trait provides additional methods and guarantees on top of an [`Allocator`].
37///
38/// A `BumpAllocator` has laxer safety conditions when using `Allocator` methods:
39/// - You can call `grow*`, `shrink` and `deallocate` with pointers that did not come from this allocator. In this case:
40///   - `grow*` will always allocate a new memory block.
41///   - `deallocate` will do nothing
42///   - `shrink` will either do nothing or allocate iff the alignment increases
43/// - Memory blocks can be split.
44/// - `deallocate` can be called with any pointer or alignment when the size is `0`.
45/// - `shrink` never errors unless the new alignment is greater
46///
47/// Those invariants are used here:
48/// - Handling of foreign pointers is necessary for implementing [`BumpVec::from_parts`], [`BumpBox::into_box`] and [`Bump(Scope)::dealloc`][Bump::dealloc].
49/// - Memory block splitting is necessary for [`split_off`] and [`split_at`].
50/// - Deallocate with a size of `0` is used in the drop implementation of [`BumpVec`].
51/// - The non-erroring behavior of `shrink` is necessary for [`BumpAllocatorExt::shrink_slice`]
52///
53/// # Safety
54///
55/// An implementor must support the conditions described above.
56///
57/// [`BumpVec::from_parts`]: crate::BumpVec::from_parts
58/// [`BumpBox::into_box`]: crate::BumpBox::into_box
59/// [`split_off`]: crate::BumpVec::split_off
60/// [`split_at`]: crate::BumpBox::split_at
61/// [`BumpVec`]: crate::BumpVec
62/// [`BumpAllocatorExt::shrink_slice`]: crate::BumpAllocatorExt::shrink_slice
63pub unsafe trait BumpAllocator: Allocator + Sealed {
64    /// Returns a type which provides statistics about the memory usage of the bump allocator.
65    fn any_stats(&self) -> AnyStats<'_>;
66
67    /// Creates a checkpoint of the current bump position.
68    ///
69    /// The bump position can be reset to this checkpoint with [`reset_to`].
70    ///
71    /// [`reset_to`]: BumpAllocator::reset_to
72    fn checkpoint(&self) -> Checkpoint;
73
74    /// Resets the bump position to a previously created checkpoint.
75    /// The memory that has been allocated since then will be reused by future allocations.
76    ///
77    /// # Safety
78    ///
79    /// - the checkpoint must have been created by this bump allocator
80    /// - the bump allocator must not have been [`reset`] since creation of this checkpoint
81    /// - there must be no references to allocations made since creation of this checkpoint
82    /// - the checkpoint must not have been created by an`!GUARANTEED_ALLOCATED` when self is `GUARANTEED_ALLOCATED`
83    ///
84    /// [`reset`]: crate::Bump::reset
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # extern crate alloc;
90    /// # use bump_scope::{Bump, BumpAllocator, alloc::Global};
91    /// # use alloc::alloc::Layout;
92    /// fn test(bump: impl BumpAllocator) {
93    ///     let checkpoint = bump.checkpoint();
94    ///     
95    ///     {
96    ///         let hello = bump.allocate(Layout::new::<[u8;5]>()).unwrap();
97    ///         assert_eq!(bump.any_stats().allocated(), 5);
98    ///         # _ = hello;
99    ///     }
100    ///     
101    ///     unsafe { bump.reset_to(checkpoint); }
102    ///     assert_eq!(bump.any_stats().allocated(), 0);
103    /// }
104    ///
105    /// test(<Bump>::new());
106    /// test(Bump::<Global, 1, true, false>::unallocated());
107    /// ```
108    unsafe fn reset_to(&self, checkpoint: Checkpoint);
109
110    /// Returns a pointer range of free space in the bump allocator with a size of at least `layout.size()`.
111    ///
112    /// Both the start and the end of the range is aligned to `layout.align()`.
113    ///
114    /// The pointer range takes up as much of the free space of the chunk as possible while satisfying the other conditions.
115    ///
116    /// # Errors
117    /// Errors if the allocation fails.
118    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError>;
119
120    /// Allocate part of the free space returned from a [`prepare_allocation`] call.
121    ///
122    /// # Safety
123    /// - `range` must have been returned from a call to [`prepare_allocation`]
124    /// - no allocation, grow, shrink or deallocate must have taken place since then
125    /// - no resets must have taken place since then
126    /// - `layout` must be less than or equal to the `layout` used when calling
127    ///   [`prepare_allocation`], both in size and alignment
128    ///
129    /// [`prepare_allocation`]: BumpAllocator::prepare_allocation
130    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8>;
131
132    /// Allocate part of the free space returned from a [`prepare_allocation`] call starting at the end.
133    ///
134    /// # Safety
135    /// - `range` must have been returned from a call to [`prepare_allocation`]
136    /// - no allocation, grow, shrink or deallocate must have taken place since then
137    /// - no resets must have taken place since then
138    /// - `layout` must be less than or equal to the `layout` used when calling
139    ///   [`prepare_allocation`], both in size and alignment
140    ///
141    /// [`prepare_allocation`]: BumpAllocator::prepare_allocation
142    /// [`allocate_prepared`]: BumpAllocator::allocate_prepared
143    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8>;
144}
145
146assert_dyn_compatible!(BumpAllocator);
147
148assert_implements! {
149    [BumpAllocator + ?Sized]
150
151    Bump
152    &Bump
153    &mut Bump
154
155    BumpScope
156    &BumpScope
157    &mut BumpScope
158
159    dyn BumpAllocator
160    &dyn BumpAllocator
161    &mut dyn BumpAllocator
162
163    dyn BumpAllocatorScope
164    &dyn BumpAllocatorScope
165    &mut dyn BumpAllocatorScope
166
167    dyn MutBumpAllocator
168    &dyn MutBumpAllocator
169    &mut dyn MutBumpAllocator
170
171    dyn MutBumpAllocatorScope
172    &dyn MutBumpAllocatorScope
173    &mut dyn MutBumpAllocatorScope
174}
175
176unsafe impl<B: BumpAllocator + ?Sized> BumpAllocator for &B {
177    #[inline(always)]
178    fn any_stats(&self) -> AnyStats<'_> {
179        B::any_stats(self)
180    }
181
182    #[inline(always)]
183    fn checkpoint(&self) -> Checkpoint {
184        B::checkpoint(self)
185    }
186
187    #[inline(always)]
188    unsafe fn reset_to(&self, checkpoint: Checkpoint) {
189        unsafe { B::reset_to(self, checkpoint) };
190    }
191
192    #[inline(always)]
193    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
194        B::prepare_allocation(self, layout)
195    }
196
197    #[inline(always)]
198    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
199        unsafe { B::allocate_prepared(self, layout, range) }
200    }
201
202    #[inline(always)]
203    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
204        unsafe { B::allocate_prepared_rev(self, layout, range) }
205    }
206}
207
208unsafe impl<B: BumpAllocator + ?Sized> BumpAllocator for &mut B {
209    #[inline(always)]
210    fn any_stats(&self) -> AnyStats<'_> {
211        B::any_stats(self)
212    }
213
214    #[inline(always)]
215    fn checkpoint(&self) -> Checkpoint {
216        B::checkpoint(self)
217    }
218
219    #[inline(always)]
220    unsafe fn reset_to(&self, checkpoint: Checkpoint) {
221        unsafe { B::reset_to(self, checkpoint) };
222    }
223
224    #[inline(always)]
225    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
226        B::prepare_allocation(self, layout)
227    }
228
229    #[inline(always)]
230    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
231        unsafe { B::allocate_prepared(self, layout, range) }
232    }
233
234    #[inline(always)]
235    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
236        unsafe { B::allocate_prepared_rev(self, layout, range) }
237    }
238}
239
240unsafe impl<B: BumpAllocator> BumpAllocator for WithoutDealloc<B> {
241    #[inline(always)]
242    fn any_stats(&self) -> AnyStats<'_> {
243        B::any_stats(&self.0)
244    }
245
246    #[inline(always)]
247    fn checkpoint(&self) -> Checkpoint {
248        B::checkpoint(&self.0)
249    }
250
251    #[inline(always)]
252    unsafe fn reset_to(&self, checkpoint: Checkpoint) {
253        unsafe { B::reset_to(&self.0, checkpoint) };
254    }
255
256    #[inline(always)]
257    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
258        B::prepare_allocation(&self.0, layout)
259    }
260
261    #[inline(always)]
262    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
263        unsafe { B::allocate_prepared(&self.0, layout, range) }
264    }
265
266    #[inline(always)]
267    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
268        unsafe { B::allocate_prepared_rev(&self.0, layout, range) }
269    }
270}
271
272unsafe impl<B: BumpAllocator> BumpAllocator for WithoutShrink<B> {
273    #[inline(always)]
274    fn any_stats(&self) -> AnyStats<'_> {
275        B::any_stats(&self.0)
276    }
277
278    #[inline(always)]
279    fn checkpoint(&self) -> Checkpoint {
280        B::checkpoint(&self.0)
281    }
282
283    #[inline(always)]
284    unsafe fn reset_to(&self, checkpoint: Checkpoint) {
285        unsafe { B::reset_to(&self.0, checkpoint) };
286    }
287
288    #[inline(always)]
289    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
290        B::prepare_allocation(&self.0, layout)
291    }
292
293    #[inline(always)]
294    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
295        unsafe { B::allocate_prepared(&self.0, layout, range) }
296    }
297
298    #[inline(always)]
299    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
300        unsafe { B::allocate_prepared_rev(&self.0, layout, range) }
301    }
302}
303
304unsafe impl<A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool, const DEALLOCATES: bool>
305    BumpAllocator for Bump<A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED, DEALLOCATES>
306where
307    MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
308    A: BaseAllocator<GUARANTEED_ALLOCATED>,
309{
310    #[inline(always)]
311    fn any_stats(&self) -> AnyStats<'_> {
312        self.as_scope().any_stats()
313    }
314
315    #[inline(always)]
316    fn checkpoint(&self) -> Checkpoint {
317        self.as_scope().checkpoint()
318    }
319
320    #[inline(always)]
321    unsafe fn reset_to(&self, checkpoint: Checkpoint) {
322        unsafe { self.as_scope().reset_to(checkpoint) };
323    }
324
325    #[inline(always)]
326    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
327        self.as_scope().prepare_allocation(layout)
328    }
329
330    #[inline(always)]
331    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
332        unsafe { self.as_scope().allocate_prepared(layout, range) }
333    }
334
335    #[inline(always)]
336    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
337        unsafe { self.as_scope().allocate_prepared_rev(layout, range) }
338    }
339}
340
341unsafe impl<A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool, const DEALLOCATES: bool>
342    BumpAllocator for BumpScope<'_, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED, DEALLOCATES>
343where
344    MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
345    A: BaseAllocator<GUARANTEED_ALLOCATED>,
346{
347    #[inline(always)]
348    fn any_stats(&self) -> AnyStats<'_> {
349        self.stats().into()
350    }
351
352    #[inline]
353    fn checkpoint(&self) -> Checkpoint {
354        Self::checkpoint(self)
355    }
356
357    #[inline]
358    unsafe fn reset_to(&self, checkpoint: Checkpoint) {
359        unsafe { Self::reset_to(self, checkpoint) }
360    }
361
362    #[inline(always)]
363    fn prepare_allocation(&self, layout: Layout) -> Result<Range<NonNull<u8>>, AllocError> {
364        #[cold]
365        #[inline(never)]
366        unsafe fn prepare_allocation_in_another_chunk<
367            A,
368            const MIN_ALIGN: usize,
369            const UP: bool,
370            const GUARANTEED_ALLOCATED: bool,
371            const DEALLOCATES: bool,
372        >(
373            this: &BumpScope<'_, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED, DEALLOCATES>,
374            layout: Layout,
375        ) -> Result<Range<NonNull<u8>>, AllocError>
376        where
377            MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
378            A: BaseAllocator<GUARANTEED_ALLOCATED>,
379        {
380            unsafe {
381                this.in_another_chunk(CustomLayout(layout), |chunk, layout| {
382                    chunk.prepare_allocation_range(MinimumAlignment::<MIN_ALIGN>, layout)
383                })
384            }
385        }
386
387        match self
388            .chunk
389            .get()
390            .prepare_allocation_range(MinimumAlignment::<MIN_ALIGN>, CustomLayout(layout))
391        {
392            Some(ptr) => Ok(ptr),
393            None => unsafe { prepare_allocation_in_another_chunk(self, layout) },
394        }
395    }
396
397    #[inline(always)]
398    unsafe fn allocate_prepared(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
399        debug_assert_eq!(range.start.addr().get() % layout.align(), 0);
400        debug_assert_eq!(range.end.addr().get() % layout.align(), 0);
401        debug_assert_eq!(layout.size() % layout.align(), 0);
402
403        unsafe {
404            if UP {
405                let end = range.start.add(layout.size());
406                self.set_pos(end.addr());
407                range.start
408            } else {
409                let src = range.start;
410                let dst_end = range.end;
411                let dst = dst_end.sub(layout.size());
412                src.copy_to(dst, layout.size());
413                self.set_pos(dst.addr());
414                dst
415            }
416        }
417    }
418
419    #[inline(always)]
420    unsafe fn allocate_prepared_rev(&self, layout: Layout, range: Range<NonNull<u8>>) -> NonNull<u8> {
421        debug_assert_eq!(range.start.addr().get() % layout.align(), 0);
422        debug_assert_eq!(range.end.addr().get() % layout.align(), 0);
423        debug_assert_eq!(layout.size() % layout.align(), 0);
424
425        unsafe {
426            if UP {
427                let dst = range.start;
428                let dst_end = dst.add(layout.size());
429
430                let src_end = range.end;
431                let src = src_end.sub(layout.size());
432
433                src.copy_to(dst, layout.size());
434
435                self.set_pos(dst_end.addr());
436
437                dst
438            } else {
439                let dst_end = range.end;
440                let dst = dst_end.sub(layout.size());
441                self.set_pos(dst.addr());
442                dst
443            }
444        }
445    }
446}