Skip to main content

static_alloc/unsync/
bump.rs

1use core::{
2    alloc::{Layout, LayoutError},
3    cell::{Cell, UnsafeCell},
4    mem::{self, MaybeUninit},
5    ops,
6    ptr::{self, NonNull},
7};
8
9use alloc_traits::AllocTime;
10
11use crate::bump::{Allocation, Failure, Level};
12use crate::leaked::LeakBox;
13
14/// A bump allocator whose storage capacity and alignment is given by `T`.
15///
16/// This type dereferences to the generic `BumpSlice` that implements the allocation behavior. Note
17/// that `BumpSlice` is an unsized type. In contrast this type is sized so it is possible to
18/// construct an instance on the stack or leak one from another bump allocator such as a global
19/// one.
20///
21/// # Usage
22///
23/// For on-stack usage this works the same as [`Bump`]. Note that it is not possible to use as a
24/// global allocator though.
25///
26/// [`Bump`]: ../bump/struct.Bump.html
27///
28/// One interesting use case for this struct is as scratch space for subroutines. This ensures good
29/// locality and cache usage. It can also allows such subroutines to use a dynamic amount of space
30/// without the need to actually allocate. Contrary to other methods where the caller provides some
31/// preallocated memory it will also not 'leak' private data types. This could be used in handling
32/// web requests.
33///
34/// ```
35/// use static_alloc::unsync::Bump;
36/// # use static_alloc::unsync::BumpSlice;
37/// # fn subroutine_one(_: &BumpSlice) {}
38/// # fn subroutine_two(_: &BumpSlice) {}
39///
40/// let mut stack_buffer: Bump<[usize; 64]> = Bump::uninit();
41/// subroutine_one(&stack_buffer);
42/// stack_buffer.reset();
43/// subroutine_two(&stack_buffer);
44/// ```
45///
46/// Note that you need not use the stack for the `Bump` itself. Indeed, you could allocate a large
47/// contiguous instance from the global (synchronized) allocator and then do subsequent allocations
48/// from the `Bump` you've obtained. This avoids potential contention on a lock of the global
49/// allocator, especially in case you must do many small allocations. If you're writing an
50/// allocator yourself you might use this technique as an internal optimization.
51///
52#[cfg_attr(feature = "alloc", doc = "```")]
53#[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
54/// use static_alloc::unsync::{Bump, BumpSlice};
55/// # struct Request;
56/// # fn handle_request(_: &BumpSlice, _: Request) {}
57/// # fn iterate_recv() -> Option<Request> { None }
58/// let mut local_page: Box<Bump<[u64; 64]>> = Box::new(Bump::uninit());
59///
60/// for request in iterate_recv() {
61///     local_page.reset();
62///     handle_request(&local_page, request);
63/// }
64/// ```
65///
66/// ## Coercion into [`BumpSlice`]
67///
68/// This allocator nominally implements [`Deref`](core::ops::Deref) into [`BumpSlice`]. However, the
69/// layout of these two structs is equivalent only for types that have at most an alignment of
70/// [`usize`] (e.g. arrays of `u8`, `u16`, or more integers depending on the platform pointer size).
71///
72/// Warning: An attempt to use this dereference with an invalid type will trigger a
73/// post-monomorphization error! This choice was made to avoid complicated encoding of the
74/// precondition into a viral trait bound and considering you're likely to use very concrete
75/// instances that either work, or would have been UB.
76///
77/// For instance, this will *fail* to compile:
78///
79/// ```compile_fail
80/// use static_alloc::unsync::{Bump, BumpSlice};
81///
82/// #[repr(align(32))]
83/// struct HighlyAligned([u8; 128]);
84///
85/// let mut arena: Bump<HighlyAligned> = Bump::uninit();
86/// // Fails here, attempting to resolve `impl Deref for Bump<HighlyAligned>`.
87/// let _ = arena.get::<u32>();
88/// ```
89#[repr(C)]
90pub struct Bump<T> {
91    /// The index used in allocation.
92    header: Header,
93    /// The backing storage for raw allocated data.
94    _data: UnsafeCell<MaybeUninit<T>>,
95    // Warning: when changing the data layout, you must change `BumpSlice` as well.
96}
97
98/// An error used when one could not re-use raw memory for a bump allocator.
99#[derive(Debug)]
100pub struct FromMemError {
101    _inner: (),
102}
103
104/// A dynamically sized allocation block in which any type can be allocated.
105#[repr(C)]
106pub struct BumpSlice {
107    header: Header,
108
109    /// The data slice of a node. This slice
110    /// may be of any arbitrary size. We use
111    /// a Cell<MaybeUninit> to allow modification
112    /// trough a &self reference, and to allow
113    /// writing uninit padding bytes.
114    /// Note that the underlying memory is in one
115    /// contiguous `UnsafeCell`, it's only represented
116    /// here to make it easier to slice.
117    data: UnsafeCell<[MaybeUninit<u8>]>,
118}
119
120impl<T> Bump<T> {
121    /// Create an allocator with uninitialized memory.
122    ///
123    /// All allocations coming from the allocator will need to be initialized manually.
124    pub fn uninit() -> Self {
125        Bump {
126            header: Header::empty(),
127            _data: UnsafeCell::new(MaybeUninit::uninit()),
128        }
129    }
130
131    /// Create an allocator with zeroed memory.
132    ///
133    /// The caller can rely on all allocations to be zeroed.
134    pub fn zeroed() -> Self {
135        Bump {
136            header: Header::empty(),
137            _data: UnsafeCell::new(MaybeUninit::zeroed()),
138        }
139    }
140
141    /// Construct a bump allocator into an uninitialized memory location.
142    ///
143    /// This fills in only a constant sized header. The rest of the allocation is left-as, i.e. if
144    /// remains initialized exactly in those spots the caller may have initialized with external
145    /// means.
146    ///
147    /// Note that this method is `const` (though this is not particularly useful yet as of `0.3.0`).
148    ///
149    /// # Usage
150    ///
151    /// This method allows `Bump` to be used together with interfaces that require an outer
152    /// `MaybeUninit` for their safety proofs, e.g. [`Box::new_uninit_slice`].
153    ///
154    /// ```
155    /// # use static_alloc::unsync::Bump;
156    /// type Allocator = Bump<[u32; 128]>;
157    ///
158    /// # let num_components = 4;
159    /// // 4 independent allocators, e.g. for four components of your software.
160    /// // Still guaranteed to live in consecutive memory.
161    /// let mut allocators = Box::<[Allocator]>::new_uninit_slice(num_components);
162    ///
163    /// // The index here might be a runtime address.
164    /// // Now this arena can be used without initializing the others already.
165    /// let c0 = Bump::from_maybe_uninit(&mut allocators[0]);
166    /// // Etc. Use this temporary stack allocator.
167    /// let _ = c0.bump_box::<usize>();
168    /// ```
169    pub const fn from_maybe_uninit(data: &mut MaybeUninit<Self>) -> &'_ mut Self {
170        // Safety: dereferencing a pointer into a `&mut MaybeUninit`.
171        let header = unsafe { &raw mut (*data.as_mut_ptr()).header };
172        // Safety: pointer points into a `MaybeUninit` which we have derived a mutable provenance
173        // pointer into.
174        unsafe { core::ptr::write(header, Header::empty()) };
175        // Safety: only the header field requires initialization. The storage is a no-op.
176        unsafe { data.assume_init_mut() }
177    }
178}
179
180#[cfg(feature = "alloc")]
181impl BumpSlice {
182    /// Allocate some space to use for a bump allocator.
183    pub fn new(capacity: usize) -> alloc::boxed::Box<Self> {
184        let layout = Self::layout_from_size(capacity).expect("Bad layout");
185        // NOTE: if std allows, we'd very much like to use `Vec<Header>::try_with_capacity` here
186        // instead. But currently we can't leak that into a `Box<[MaybeUninit<Header>]>` which makes
187        // it unfortunately inert.
188        let ptr = NonNull::new(unsafe { alloc::alloc::alloc(layout) })
189            .unwrap_or_else(|| alloc::alloc::handle_alloc_error(layout));
190        let ptr = ptr::slice_from_raw_parts_mut(ptr.as_ptr(), capacity);
191        // Safety: `layout_from_size` ensures at least the header fits, and the allocation was
192        // obviously successful as just seen.
193        unsafe { ptr::write(ptr as *mut Header, Header::empty()) };
194        unsafe { alloc::boxed::Box::from_raw(ptr as *mut BumpSlice) }
195    }
196}
197
198impl BumpSlice {
199    /// Initialize a bump allocator from existing memory.
200    ///
201    /// # Usage
202    ///
203    /// ```
204    /// use core::mem::MaybeUninit;
205    /// use static_alloc::unsync::BumpSlice;
206    ///
207    /// let mut backing = [MaybeUninit::new(0); 128];
208    /// let alloc = BumpSlice::from_mem(&mut backing)?;
209    ///
210    /// # Ok::<(), static_alloc::unsync::FromMemError>(())
211    /// ```
212    pub fn from_mem(mem: &mut [MaybeUninit<u8>]) -> Result<LeakBox<'_, Self>, FromMemError> {
213        let header = Self::header_layout();
214        let offset = mem.as_ptr().align_offset(header.align());
215        // Align the memory for the header.
216        let mem = mem.get_mut(offset..).ok_or(FromMemError { _inner: () })?;
217        let hdr = mem
218            .get_mut(..header.size())
219            .ok_or(FromMemError { _inner: () })?;
220        // Safety: `mem` is a mutable ref, and we just verified the size and align. We'd consider
221        // MaybeUninit::as_bytes` and copy instead but it's not stable.
222        unsafe { ptr::write(hdr.as_mut_ptr().cast(), Header::empty()) };
223        // Safety: we just verified the size, and pivoted to the correct alignment.
224        Ok(unsafe { Self::from_mem_unchecked(mem) })
225    }
226
227    /// Construct a bump allocator from existing memory without reinitializing.
228    ///
229    /// This allows the caller to (unsafely) fallback to manual borrow checking of the memory
230    /// region between regions of allocator use.
231    ///
232    /// # Safety
233    ///
234    /// The memory must contain data that has been previously wrapped as a `BumpSlice`, exactly. The
235    /// only endorsed sound form of obtaining such memory is [`BumpSlice::into_mem`].
236    ///
237    /// Warning: Any _use_ of the memory will have invalidated all pointers to allocated objects,
238    /// more specifically the provenance of these pointers is no longer valid! You _must_ derive
239    /// new pointers based on their offsets.
240    pub unsafe fn from_mem_unchecked(mem: &mut [MaybeUninit<u8>]) -> LeakBox<'_, Self> {
241        // Safety: memory already valid, according to the caller.
242        let raw = unsafe { Self::reinterpret_aligned_mem(mem) };
243        // Safety: we own this value in the sense that `Drop` is not called by the caller.
244        unsafe { LeakBox::from_mut_unchecked(raw) }
245    }
246
247    /// Cast pre-initialized, aligned memory into a bump allocator.
248    #[allow(unused_unsafe)]
249    unsafe fn reinterpret_aligned_mem(mem: &mut [MaybeUninit<u8>]) -> &mut Self {
250        // Safety: supposedly guaranteed by the caller.
251        unsafe { core::hint::assert_unchecked(mem.as_ptr().cast::<Header>().is_aligned()) };
252
253        let header = Self::header_layout();
254        // debug_assert!(mem.len() >= header.size());
255        // debug_assert!(mem.as_ptr().align_offset(header.align()) == 0);
256
257        let datasize = mem.len() - header.size();
258        // Round down to the header alignment! The whole struct will occupy memory according to its
259        // natural alignment. We must be prepared fro the `pad_to_align` so to speak.
260        let datasize = datasize - datasize % header.align();
261        debug_assert!(Self::layout_from_size(datasize).is_ok_and(|l| l.size() <= mem.len()));
262
263        let raw = mem.as_mut_ptr() as *mut u8;
264        // Turn it into a fat pointer with correct metadata for a `BumpSlice`.
265        // Safety:
266        // - The data is writable as we owned
267        unsafe { &mut *(ptr::slice_from_raw_parts_mut(raw, datasize) as *mut BumpSlice) }
268    }
269
270    /// Unwrap the memory owned by an unsized bump allocator.
271    ///
272    /// This releases the memory used by the allocator, similar to `Box::leak`, with the difference
273    /// of operating on unique references instead. It is necessary to own the bump allocator due to
274    /// internal state contained within the memory region that the caller can subsequently
275    /// invalidate.
276    ///
277    /// # Example
278    ///
279    /// ```rust
280    /// use core::mem::MaybeUninit;
281    /// use static_alloc::unsync::BumpSlice;
282    ///
283    /// # let mut backing = [MaybeUninit::new(0); 128];
284    /// # let alloc = BumpSlice::from_mem(&mut backing)?;
285    /// let memory: &mut [_] = BumpSlice::into_mem(alloc);
286    /// assert!(memory.len() <= 128, "Not guaranteed to use all memory");
287    ///
288    /// // Safety: We have not touched the memory itself.
289    /// unsafe { BumpSlice::from_mem_unchecked(memory) };
290    /// # Ok::<(), static_alloc::unsync::FromMemError>(())
291    /// ```
292    pub fn into_mem<'lt>(this: LeakBox<'lt, Self>) -> &'lt mut [MaybeUninit<u8>] {
293        let layout = Layout::for_value(&*this);
294        let mem_pointer = LeakBox::into_raw(this) as *mut MaybeUninit<u8>;
295        unsafe { &mut *ptr::slice_from_raw_parts_mut(mem_pointer, layout.size()) }
296    }
297
298    /// Returns the layout for the `header` of a `BumpSlice`.
299    /// The definition of `header` in this case is all the
300    /// fields that come **before** the `data` field.
301    /// If any of the fields of a BumpSlice are modified,
302    /// this function likely has to be modified too.
303    fn header_layout() -> Layout {
304        Layout::new::<Cell<usize>>()
305    }
306
307    /// Returns the layout for an array with the size of `size`
308    fn data_layout(size: usize) -> Result<Layout, LayoutError> {
309        Layout::array::<UnsafeCell<MaybeUninit<u8>>>(size)
310    }
311
312    /// Returns a layout for a BumpSlice where the length of the data field is `size`.
313    /// This relies on the two functions defined above.
314    pub(crate) fn layout_from_size(size: usize) -> Result<Layout, LayoutError> {
315        let data_tail = Self::data_layout(size)?;
316        let (layout, _) = Self::header_layout().extend(data_tail)?;
317        Ok(layout.pad_to_align())
318    }
319
320    /// Returns capacity of this `BumpSlice`.
321    /// This is how many *bytes* can be allocated
322    /// within this node.
323    pub const fn capacity(&self) -> usize {
324        self.data.get().len()
325    }
326
327    /// Get a raw pointer to the data.
328    ///
329    /// Note that *any* use of the pointer must be done with extreme care as it may invalidate
330    /// existing references into the allocated region. Furthermore, bytes may not be initialized.
331    /// The length of the valid region is [`BumpSlice::capacity`].
332    ///
333    /// Prefer [`BumpSlice::get_unchecked`] for reconstructing a prior allocation.
334    pub fn data_ptr(&self) -> NonNull<u8> {
335        NonNull::new(self.data.get() as *mut u8).expect("from a reference")
336    }
337
338    /// Allocate a region of memory.
339    ///
340    /// This is a safe alternative to [GlobalAlloc::alloc](#impl-GlobalAlloc).
341    ///
342    /// # Panics
343    /// This function will panic if the requested layout has a size of `0`. For the use in a
344    /// `GlobalAlloc` this is explicitely forbidden to request and would allow any behaviour but we
345    /// instead strictly check it.
346    ///
347    /// FIXME(breaking): this could well be a `Result<_, Failure>`.
348    pub fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
349        Some(self.try_alloc(layout)?.ptr)
350    }
351
352    /// Try to allocate some layout with a precise base location.
353    ///
354    /// The base location is the currently consumed byte count, without correction for the
355    /// alignment of the allocation. This will succeed if it can be allocate exactly at the
356    /// expected location.
357    ///
358    /// # Panics
359    /// This function may panic if the provided `level` is from a different slab.
360    pub fn alloc_at(&self, layout: Layout, level: Level) -> Result<NonNull<u8>, Failure> {
361        let Allocation { ptr, .. } = self.try_alloc_at(layout, level.0)?;
362        Ok(ptr)
363    }
364
365    /// Get an allocation for a specific type.
366    ///
367    /// It is not yet initialized but provides an interface for that initialization.
368    ///
369    /// ## Usage
370    ///
371    /// ```
372    /// # use static_alloc::unsync::Bump;
373    /// use core::cell::{Ref, RefCell};
374    ///
375    /// let slab: Bump<[Ref<'static, usize>; 1]> = Bump::uninit();
376    /// let data = RefCell::new(0xff);
377    ///
378    /// // We can place a `Ref` here but we did not yet.
379    /// let alloc = slab.get::<Ref<usize>>().unwrap();
380    /// let cell_ref = unsafe {
381    ///     alloc.leak(data.borrow())
382    /// };
383    ///
384    /// assert_eq!(**cell_ref, 0xff);
385    /// ```
386    ///
387    /// FIXME(breaking): this could well be a `Result<_, Failure>`.
388    pub fn get<V>(&self) -> Option<Allocation<'_, V>> {
389        let alloc = self.try_alloc(Layout::new::<V>())?;
390        Some(Allocation {
391            lifetime: alloc.lifetime,
392            level: alloc.level,
393            ptr: alloc.ptr.cast(),
394        })
395    }
396
397    /// Get an allocation for a specific type at a specific level.
398    ///
399    /// See [`get`] for usage. This can be used to ensure that data is contiguous in concurrent
400    /// access to the allocator.
401    ///
402    /// [`get`]: #method.get
403    pub fn get_at<V>(&self, level: Level) -> Result<Allocation<'_, V>, Failure> {
404        let alloc = self.try_alloc_at(Layout::new::<V>(), level.0)?;
405        Ok(Allocation {
406            lifetime: alloc.lifetime,
407            level: alloc.level,
408            ptr: alloc.ptr.cast(),
409        })
410    }
411
412    /// Reacquire an allocation that has been performed previously.
413    ///
414    /// This call won't invalidate any other allocations.
415    ///
416    /// # Safety
417    ///
418    /// The caller must guarantee that no other pointers to this prior allocation are alive, or can
419    /// be created. This is guaranteed if the allocation was performed previously, has since been
420    /// discarded, and `reset` can not be called (for example, the caller holds a shared
421    /// reference).
422    ///
423    /// # Usage
424    ///
425    /// ```
426    /// # use core::mem::MaybeUninit;
427    /// # use static_alloc::unsync::BumpSlice;
428    /// # let mut backing = [MaybeUninit::new(0); 128];
429    /// # let alloc = BumpSlice::from_mem(&mut backing).unwrap();
430    /// // Create an initial allocation.
431    /// let level = alloc.level();
432    /// let allocation = alloc.get_at::<usize>(level)?;
433    /// let address = allocation.ptr.as_ptr() as usize;
434    /// // pretend to lose the owning pointer of the allocation.
435    /// let _ = { allocation };
436    ///
437    /// // Restore our access.
438    /// let renewed = unsafe { alloc.get_unchecked::<usize>(level) };
439    /// assert_eq!(address, renewed.ptr.as_ptr() as usize);
440    /// # Ok::<_, static_alloc::bump::Failure>(())
441    /// ```
442    ///
443    /// Crucially, you can rely on *other* allocations to stay valid. The caller is responsible of
444    /// using the returning pointer to only refer to allocations that are not referenced through
445    /// any other way.
446    ///
447    /// ```
448    /// # use core::mem::MaybeUninit;
449    /// # use static_alloc::{leaked::LeakBox, unsync::BumpSlice};
450    /// # let mut backing = [MaybeUninit::new(0); 128];
451    /// # let alloc = BumpSlice::from_mem(&mut backing).unwrap();
452    /// let level = alloc.level();
453    /// alloc.get_at::<usize>(level)?;
454    ///
455    /// let other_val = alloc.bump_box()?;
456    /// let other_val = LeakBox::write(other_val, 0usize);
457    ///
458    /// let renew = unsafe { alloc.get_unchecked::<usize>(level) };
459    /// assert_eq!(*other_val, 0); // Not UB!
460    /// # Ok::<_, static_alloc::bump::Failure>(())
461    /// ```
462    pub unsafe fn get_unchecked<V>(&self, level: Level) -> Allocation<'_, V> {
463        debug_assert!(level.0 < self.capacity());
464
465        debug_assert!(
466            level <= self.level(),
467            "Tried to access an allocation that does not yet exist"
468        );
469
470        let base_ptr = self.data_ptr().as_ptr();
471        // SAFETY: `level.0` is in bounds as assert above, or by the caller by having provided an
472        // existing allocation—all allocations we hand out are in bounds.
473        let alloc = unsafe { base_ptr.add(level.0) };
474        let ptr = NonNull::new(alloc).unwrap().cast::<V>();
475
476        debug_assert!(
477            ptr.as_ptr().is_aligned(),
478            "Tried to access an allocation with improper type"
479        );
480
481        Allocation {
482            level,
483            lifetime: AllocTime::default(),
484            ptr,
485        }
486    }
487
488    /// Allocate space for one `T` without initializing it.
489    ///
490    /// Note that the returned `MaybeUninit` can be unwrapped from `LeakBox`. Or you can store an
491    /// arbitrary value and ensure it is safely dropped before the borrow ends.
492    ///
493    /// ## Usage
494    ///
495    /// ```
496    /// # use static_alloc::unsync::Bump;
497    /// use core::cell::RefCell;
498    /// use static_alloc::leaked::LeakBox;
499    ///
500    /// let slab: Bump<[usize; 4]> = Bump::uninit();
501    /// let data = RefCell::new(0xff);
502    ///
503    /// let slot = slab.bump_box().unwrap();
504    /// let cell_box = LeakBox::write(slot, data.borrow());
505    ///
506    /// assert_eq!(**cell_box, 0xff);
507    /// drop(cell_box);
508    ///
509    /// assert!(data.try_borrow_mut().is_ok());
510    /// ```
511    ///
512    /// FIXME(breaking): should return evidence of the level (observed, and post). Something
513    /// similar to `Allocation` but containing a `LeakBox<T>` instead? Introduce that to the sync
514    /// `Bump` allocator as well.
515    ///
516    /// FIXME(breaking): align with sync `Bump::get` (probably rename get to bump_box).
517    pub fn bump_box<'bump, T: 'bump>(
518        &'bump self,
519    ) -> Result<LeakBox<'bump, MaybeUninit<T>>, Failure> {
520        let allocation = self.get_at(self.level())?;
521        Ok(unsafe { allocation.uninit() }.into())
522    }
523
524    /// Allocate space for a slice of `T`s without initializing any.
525    ///
526    /// Retrieve individual `MaybeUninit` elements and wrap them as a `LeakBox` to store values. Or
527    /// use the slice as backing memory for one of the containers from `without-alloc`. Or manually
528    /// initialize them.
529    ///
530    /// ## Usage
531    ///
532    /// Quicksort, implemented recursively, requires a maximum of `log n` stack frames in the worst
533    /// case when implemented optimally. Since each frame is quite large this is wasteful. We can
534    /// use a properly sized buffer instead and implement an iterative solution. (Left as an
535    /// exercise to the reader, or see the examples for `without-alloc` where we use such a dynamic
536    /// allocation with an inline vector as our stack).
537    pub fn bump_array<'bump, T: 'bump>(
538        &'bump self,
539        n: usize,
540    ) -> Result<LeakBox<'bump, [MaybeUninit<T>]>, Failure> {
541        let layout = Layout::array::<T>(n).map_err(|_| Failure::Exhausted)?;
542        let raw = self.alloc(layout).ok_or(Failure::Exhausted)?;
543        let slice = ptr::slice_from_raw_parts_mut(raw.cast().as_ptr(), n);
544        let uninit = unsafe { &mut *slice };
545        Ok(uninit.into())
546    }
547
548    /// Get the number of already allocated bytes.
549    pub fn level(&self) -> Level {
550        Level(self.header.index.get())
551    }
552
553    /// Reset the bump allocator.
554    ///
555    /// This requires a unique reference to the allocator hence no allocation can be alive at this
556    /// point. It will reset the internal count of used bytes to zero.
557    pub fn reset(&mut self) {
558        self.header.index.set(0)
559    }
560
561    fn try_alloc(&self, layout: Layout) -> Option<Allocation<'_>> {
562        let consumed = self.header.index.get();
563        match self.try_alloc_at(layout, consumed) {
564            Ok(alloc) => Some(alloc),
565            Err(Failure::Exhausted) => None,
566            Err(Failure::Mismatch { observed: _ }) => {
567                unreachable!("Count in Cell concurrently modified, this UB")
568            }
569        }
570    }
571
572    fn try_alloc_at(
573        &self,
574        layout: Layout,
575        expect_consumed: usize,
576    ) -> Result<Allocation<'_>, Failure> {
577        assert!(layout.size() > 0);
578        let length = mem::size_of_val(&self.data);
579        // We want to access contiguous slice, so cast to a single cell.
580        let base_ptr = self.data.get().cast::<u8>();
581
582        let alignment = layout.align();
583        let requested = layout.size();
584
585        // Ensure no overflows when calculating offets within.
586        assert!(expect_consumed <= length, "{}/{}", expect_consumed, length);
587
588        let available = length.checked_sub(expect_consumed).unwrap();
589        let ptr_to = base_ptr.wrapping_add(expect_consumed);
590        let offset = ptr_to.align_offset(alignment);
591
592        if Some(requested) > available.checked_sub(offset) {
593            return Err(Failure::Exhausted); // exhausted
594        }
595
596        // `size` can not be zero, saturation will thus always make this true.
597        assert!(offset < available);
598        let at_aligned = expect_consumed.checked_add(offset).unwrap();
599        let new_consumed = at_aligned.checked_add(requested).unwrap();
600        // new_consumed
601        //    = consumed + offset + requested  [lines above]
602        //   <= consumed + available  [bail out: exhausted]
603        //   <= length  [first line of loop]
604        // So it's ok to store `allocated` into `consumed`.
605        assert!(new_consumed <= length);
606        assert!(at_aligned < length);
607
608        // Try to actually allocate.
609        match self.bump(expect_consumed, new_consumed) {
610            Ok(()) => (),
611            Err(observed) => {
612                // Someone else was faster, if you want it then recalculate again.
613                return Err(Failure::Mismatch {
614                    observed: Level(observed),
615                });
616            }
617        }
618
619        let aligned = unsafe {
620            // SAFETY:
621            // * `0 <= at_aligned < length` in bounds as checked above.
622            base_ptr.byte_add(at_aligned)
623        };
624
625        Ok(Allocation {
626            ptr: NonNull::new(aligned).unwrap(),
627            lifetime: AllocTime::default(),
628            level: Level(new_consumed),
629        })
630    }
631
632    fn bump(&self, expect: usize, consume: usize) -> Result<(), usize> {
633        debug_assert!(consume <= self.capacity());
634        debug_assert!(expect <= consume);
635
636        let prev = self.header.index.get();
637        if prev != expect {
638            Err(prev)
639        } else {
640            self.header.index.set(consume);
641            Ok(())
642        }
643    }
644}
645
646struct EnsureDerefIsApplicable<T>(core::marker::PhantomData<T>);
647
648impl<T> EnsureDerefIsApplicable<T> {
649    pub const ASSERT: () = {
650        if mem::offset_of!(Bump<T>, _data) != mem::size_of::<Header>() {
651            panic!(
652                // `data` follows header directly, using the macro requires a value for unsized types.
653                "This `unsync::Bump` can not be used as a `BumpSlice` since the reinterpretation changes the data layout. (Hint: its alignment must be at most `usize`).",
654            );
655        }
656    };
657}
658
659impl<T> ops::Deref for Bump<T> {
660    type Target = BumpSlice;
661    fn deref(&self) -> &BumpSlice {
662        // This provokes post-mono error!
663        let _: () = EnsureDerefIsApplicable::<T>::ASSERT;
664
665        let from_layout = Layout::for_value(self);
666        let data_layout = Layout::new::<MaybeUninit<T>>();
667        // Construct a point with the meta data of a slice to `data`, but pointing to the whole
668        // struct instead. This meta data is later copied to the meta data of `bump` when cast.
669        let ptr = (self as *const Self).cast::<MaybeUninit<u8>>();
670        let mem: *const [MaybeUninit<u8>] = ptr::slice_from_raw_parts(ptr, data_layout.size());
671        // Now we have a pointer to BumpSlice with length meta data of the data slice.
672        let bump = unsafe { &*(mem as *const BumpSlice) };
673        debug_assert_eq!(from_layout, Layout::for_value(bump));
674        bump
675    }
676}
677
678impl<T> ops::DerefMut for Bump<T> {
679    fn deref_mut(&mut self) -> &mut BumpSlice {
680        // This provokes post-mono error!
681        let _: () = EnsureDerefIsApplicable::<T>::ASSERT;
682
683        let from_layout = Layout::for_value(self);
684        let data_layout = Layout::new::<MaybeUninit<T>>();
685        // Construct a point with the meta data of a slice to `data`, but pointing to the whole
686        // struct instead. This meta data is later copied to the meta data of `bump` when cast.
687        let ptr = (self as *mut Self).cast::<MaybeUninit<u8>>();
688        let mem: *mut [MaybeUninit<u8>] = ptr::slice_from_raw_parts_mut(ptr, data_layout.size());
689        // Now we have a pointer to BumpSlice with length meta data of the data slice.
690        let bump = unsafe { &mut *(mem as *mut BumpSlice) };
691        debug_assert_eq!(from_layout, Layout::for_value(bump));
692        bump
693    }
694}
695
696struct Header {
697    /// An index into the data field. This index
698    /// will always be an index to an element
699    /// that has not been allocated into.
700    /// Again this is wrapped in a Cell,
701    /// to allow modification with just a
702    /// &self reference.
703    index: Cell<usize>,
704}
705
706impl Header {
707    const fn empty() -> Self {
708        Header {
709            index: Cell::new(0),
710        }
711    }
712}
713
714#[test]
715fn mem_bump_derefs_correctly() {
716    let bump = Bump::<usize>::zeroed();
717    let mem: &BumpSlice = &bump;
718    assert_eq!(mem::size_of_val(&bump), mem::size_of_val(mem));
719}