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 _index: Cell<usize>,
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 _index: Cell::new(0),
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 _index: Cell::new(0),
137 _data: UnsafeCell::new(MaybeUninit::zeroed()),
138 }
139 }
140}
141
142#[cfg(feature = "alloc")]
143impl BumpSlice {
144 /// Allocate some space to use for a bump allocator.
145 pub fn new(capacity: usize) -> alloc::boxed::Box<Self> {
146 let layout = Self::layout_from_size(capacity).expect("Bad layout");
147 // NOTE: if std allows, we'd very much like to use `Vec<Header>::try_with_capacity` here
148 // instead. But currently we can't leak that into a `Box<[MaybeUninit<Header>]>` which makes
149 // it unfortunately inert.
150 let ptr = NonNull::new(unsafe { alloc::alloc::alloc(layout) })
151 .unwrap_or_else(|| alloc::alloc::handle_alloc_error(layout));
152 let ptr = ptr::slice_from_raw_parts_mut(ptr.as_ptr(), capacity);
153 // Safety: `layout_from_size` ensures at least the header fits, and the allocation was
154 // obviously successful as just seen.
155 unsafe { ptr::write(ptr as *mut Header, Header::empty()) };
156 unsafe { alloc::boxed::Box::from_raw(ptr as *mut BumpSlice) }
157 }
158}
159
160impl BumpSlice {
161 /// Initialize a bump allocator from existing memory.
162 ///
163 /// # Usage
164 ///
165 /// ```
166 /// use core::mem::MaybeUninit;
167 /// use static_alloc::unsync::BumpSlice;
168 ///
169 /// let mut backing = [MaybeUninit::new(0); 128];
170 /// let alloc = BumpSlice::from_mem(&mut backing)?;
171 ///
172 /// # Ok::<(), static_alloc::unsync::FromMemError>(())
173 /// ```
174 pub fn from_mem(mem: &mut [MaybeUninit<u8>]) -> Result<LeakBox<'_, Self>, FromMemError> {
175 let header = Self::header_layout();
176 let offset = mem.as_ptr().align_offset(header.align());
177 // Align the memory for the header.
178 let mem = mem.get_mut(offset..).ok_or(FromMemError { _inner: () })?;
179 let hdr = mem
180 .get_mut(..header.size())
181 .ok_or(FromMemError { _inner: () })?;
182 // Safety: `mem` is a mutable ref, and we just verified the size and align. We'd consider
183 // MaybeUninit::as_bytes` and copy instead but it's not stable.
184 unsafe { ptr::write(hdr.as_mut_ptr().cast(), Header::empty()) };
185 // Safety: we just verified the size, and pivoted to the correct alignment.
186 Ok(unsafe { Self::from_mem_unchecked(mem) })
187 }
188
189 /// Construct a bump allocator from existing memory without reinitializing.
190 ///
191 /// This allows the caller to (unsafely) fallback to manual borrow checking of the memory
192 /// region between regions of allocator use.
193 ///
194 /// # Safety
195 ///
196 /// The memory must contain data that has been previously wrapped as a `BumpSlice`, exactly. The
197 /// only endorsed sound form of obtaining such memory is [`BumpSlice::into_mem`].
198 ///
199 /// Warning: Any _use_ of the memory will have invalidated all pointers to allocated objects,
200 /// more specifically the provenance of these pointers is no longer valid! You _must_ derive
201 /// new pointers based on their offsets.
202 pub unsafe fn from_mem_unchecked(mem: &mut [MaybeUninit<u8>]) -> LeakBox<'_, Self> {
203 // Safety: memory already valid, according to the caller.
204 let raw = unsafe { Self::reinterpret_aligned_mem(mem) };
205 // Safety: we own this value in the sense that `Drop` is not called by the caller.
206 unsafe { LeakBox::from_mut_unchecked(raw) }
207 }
208
209 /// Cast pre-initialized, aligned memory into a bump allocator.
210 #[allow(unused_unsafe)]
211 unsafe fn reinterpret_aligned_mem(mem: &mut [MaybeUninit<u8>]) -> &mut Self {
212 // Safety: supposedly guaranteed by the caller.
213 unsafe { core::hint::assert_unchecked(mem.as_ptr().cast::<Header>().is_aligned()) };
214
215 let header = Self::header_layout();
216 // debug_assert!(mem.len() >= header.size());
217 // debug_assert!(mem.as_ptr().align_offset(header.align()) == 0);
218
219 let datasize = mem.len() - header.size();
220 // Round down to the header alignment! The whole struct will occupy memory according to its
221 // natural alignment. We must be prepared fro the `pad_to_align` so to speak.
222 let datasize = datasize - datasize % header.align();
223 debug_assert!(Self::layout_from_size(datasize).is_ok_and(|l| l.size() <= mem.len()));
224
225 let raw = mem.as_mut_ptr() as *mut u8;
226 // Turn it into a fat pointer with correct metadata for a `BumpSlice`.
227 // Safety:
228 // - The data is writable as we owned
229 unsafe { &mut *(ptr::slice_from_raw_parts_mut(raw, datasize) as *mut BumpSlice) }
230 }
231
232 /// Unwrap the memory owned by an unsized bump allocator.
233 ///
234 /// This releases the memory used by the allocator, similar to `Box::leak`, with the difference
235 /// of operating on unique references instead. It is necessary to own the bump allocator due to
236 /// internal state contained within the memory region that the caller can subsequently
237 /// invalidate.
238 ///
239 /// # Example
240 ///
241 /// ```rust
242 /// use core::mem::MaybeUninit;
243 /// use static_alloc::unsync::BumpSlice;
244 ///
245 /// # let mut backing = [MaybeUninit::new(0); 128];
246 /// # let alloc = BumpSlice::from_mem(&mut backing)?;
247 /// let memory: &mut [_] = BumpSlice::into_mem(alloc);
248 /// assert!(memory.len() <= 128, "Not guaranteed to use all memory");
249 ///
250 /// // Safety: We have not touched the memory itself.
251 /// unsafe { BumpSlice::from_mem_unchecked(memory) };
252 /// # Ok::<(), static_alloc::unsync::FromMemError>(())
253 /// ```
254 pub fn into_mem<'lt>(this: LeakBox<'lt, Self>) -> &'lt mut [MaybeUninit<u8>] {
255 let layout = Layout::for_value(&*this);
256 let mem_pointer = LeakBox::into_raw(this) as *mut MaybeUninit<u8>;
257 unsafe { &mut *ptr::slice_from_raw_parts_mut(mem_pointer, layout.size()) }
258 }
259
260 /// Returns the layout for the `header` of a `BumpSlice`.
261 /// The definition of `header` in this case is all the
262 /// fields that come **before** the `data` field.
263 /// If any of the fields of a BumpSlice are modified,
264 /// this function likely has to be modified too.
265 fn header_layout() -> Layout {
266 Layout::new::<Cell<usize>>()
267 }
268
269 /// Returns the layout for an array with the size of `size`
270 fn data_layout(size: usize) -> Result<Layout, LayoutError> {
271 Layout::array::<UnsafeCell<MaybeUninit<u8>>>(size)
272 }
273
274 /// Returns a layout for a BumpSlice where the length of the data field is `size`.
275 /// This relies on the two functions defined above.
276 pub(crate) fn layout_from_size(size: usize) -> Result<Layout, LayoutError> {
277 let data_tail = Self::data_layout(size)?;
278 let (layout, _) = Self::header_layout().extend(data_tail)?;
279 Ok(layout.pad_to_align())
280 }
281
282 /// Returns capacity of this `BumpSlice`.
283 /// This is how many *bytes* can be allocated
284 /// within this node.
285 pub const fn capacity(&self) -> usize {
286 self.data.get().len()
287 }
288
289 /// Get a raw pointer to the data.
290 ///
291 /// Note that *any* use of the pointer must be done with extreme care as it may invalidate
292 /// existing references into the allocated region. Furthermore, bytes may not be initialized.
293 /// The length of the valid region is [`BumpSlice::capacity`].
294 ///
295 /// Prefer [`BumpSlice::get_unchecked`] for reconstructing a prior allocation.
296 pub fn data_ptr(&self) -> NonNull<u8> {
297 NonNull::new(self.data.get() as *mut u8).expect("from a reference")
298 }
299
300 /// Allocate a region of memory.
301 ///
302 /// This is a safe alternative to [GlobalAlloc::alloc](#impl-GlobalAlloc).
303 ///
304 /// # Panics
305 /// This function will panic if the requested layout has a size of `0`. For the use in a
306 /// `GlobalAlloc` this is explicitely forbidden to request and would allow any behaviour but we
307 /// instead strictly check it.
308 ///
309 /// FIXME(breaking): this could well be a `Result<_, Failure>`.
310 pub fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
311 Some(self.try_alloc(layout)?.ptr)
312 }
313
314 /// Try to allocate some layout with a precise base location.
315 ///
316 /// The base location is the currently consumed byte count, without correction for the
317 /// alignment of the allocation. This will succeed if it can be allocate exactly at the
318 /// expected location.
319 ///
320 /// # Panics
321 /// This function may panic if the provided `level` is from a different slab.
322 pub fn alloc_at(&self, layout: Layout, level: Level) -> Result<NonNull<u8>, Failure> {
323 let Allocation { ptr, .. } = self.try_alloc_at(layout, level.0)?;
324 Ok(ptr)
325 }
326
327 /// Get an allocation for a specific type.
328 ///
329 /// It is not yet initialized but provides an interface for that initialization.
330 ///
331 /// ## Usage
332 ///
333 /// ```
334 /// # use static_alloc::unsync::Bump;
335 /// use core::cell::{Ref, RefCell};
336 ///
337 /// let slab: Bump<[Ref<'static, usize>; 1]> = Bump::uninit();
338 /// let data = RefCell::new(0xff);
339 ///
340 /// // We can place a `Ref` here but we did not yet.
341 /// let alloc = slab.get::<Ref<usize>>().unwrap();
342 /// let cell_ref = unsafe {
343 /// alloc.leak(data.borrow())
344 /// };
345 ///
346 /// assert_eq!(**cell_ref, 0xff);
347 /// ```
348 ///
349 /// FIXME(breaking): this could well be a `Result<_, Failure>`.
350 pub fn get<V>(&self) -> Option<Allocation<'_, V>> {
351 let alloc = self.try_alloc(Layout::new::<V>())?;
352 Some(Allocation {
353 lifetime: alloc.lifetime,
354 level: alloc.level,
355 ptr: alloc.ptr.cast(),
356 })
357 }
358
359 /// Get an allocation for a specific type at a specific level.
360 ///
361 /// See [`get`] for usage. This can be used to ensure that data is contiguous in concurrent
362 /// access to the allocator.
363 ///
364 /// [`get`]: #method.get
365 pub fn get_at<V>(&self, level: Level) -> Result<Allocation<'_, V>, Failure> {
366 let alloc = self.try_alloc_at(Layout::new::<V>(), level.0)?;
367 Ok(Allocation {
368 lifetime: alloc.lifetime,
369 level: alloc.level,
370 ptr: alloc.ptr.cast(),
371 })
372 }
373
374 /// Reacquire an allocation that has been performed previously.
375 ///
376 /// This call won't invalidate any other allocations.
377 ///
378 /// # Safety
379 ///
380 /// The caller must guarantee that no other pointers to this prior allocation are alive, or can
381 /// be created. This is guaranteed if the allocation was performed previously, has since been
382 /// discarded, and `reset` can not be called (for example, the caller holds a shared
383 /// reference).
384 ///
385 /// # Usage
386 ///
387 /// ```
388 /// # use core::mem::MaybeUninit;
389 /// # use static_alloc::unsync::BumpSlice;
390 /// # let mut backing = [MaybeUninit::new(0); 128];
391 /// # let alloc = BumpSlice::from_mem(&mut backing).unwrap();
392 /// // Create an initial allocation.
393 /// let level = alloc.level();
394 /// let allocation = alloc.get_at::<usize>(level)?;
395 /// let address = allocation.ptr.as_ptr() as usize;
396 /// // pretend to lose the owning pointer of the allocation.
397 /// let _ = { allocation };
398 ///
399 /// // Restore our access.
400 /// let renewed = unsafe { alloc.get_unchecked::<usize>(level) };
401 /// assert_eq!(address, renewed.ptr.as_ptr() as usize);
402 /// # Ok::<_, static_alloc::bump::Failure>(())
403 /// ```
404 ///
405 /// Crucially, you can rely on *other* allocations to stay valid. The caller is responsible of
406 /// using the returning pointer to only refer to allocations that are not referenced through
407 /// any other way.
408 ///
409 /// ```
410 /// # use core::mem::MaybeUninit;
411 /// # use static_alloc::{leaked::LeakBox, unsync::BumpSlice};
412 /// # let mut backing = [MaybeUninit::new(0); 128];
413 /// # let alloc = BumpSlice::from_mem(&mut backing).unwrap();
414 /// let level = alloc.level();
415 /// alloc.get_at::<usize>(level)?;
416 ///
417 /// let other_val = alloc.bump_box()?;
418 /// let other_val = LeakBox::write(other_val, 0usize);
419 ///
420 /// let renew = unsafe { alloc.get_unchecked::<usize>(level) };
421 /// assert_eq!(*other_val, 0); // Not UB!
422 /// # Ok::<_, static_alloc::bump::Failure>(())
423 /// ```
424 pub unsafe fn get_unchecked<V>(&self, level: Level) -> Allocation<'_, V> {
425 debug_assert!(level.0 < self.capacity());
426
427 debug_assert!(
428 level <= self.level(),
429 "Tried to access an allocation that does not yet exist"
430 );
431
432 let base_ptr = self.data_ptr().as_ptr();
433 // SAFETY: `level.0` is in bounds as assert above, or by the caller by having provided an
434 // existing allocation—all allocations we hand out are in bounds.
435 let alloc = unsafe { base_ptr.add(level.0) };
436 let ptr = NonNull::new(alloc).unwrap().cast::<V>();
437
438 debug_assert!(
439 ptr.as_ptr().is_aligned(),
440 "Tried to access an allocation with improper type"
441 );
442
443 Allocation {
444 level,
445 lifetime: AllocTime::default(),
446 ptr,
447 }
448 }
449
450 /// Allocate space for one `T` without initializing it.
451 ///
452 /// Note that the returned `MaybeUninit` can be unwrapped from `LeakBox`. Or you can store an
453 /// arbitrary value and ensure it is safely dropped before the borrow ends.
454 ///
455 /// ## Usage
456 ///
457 /// ```
458 /// # use static_alloc::unsync::Bump;
459 /// use core::cell::RefCell;
460 /// use static_alloc::leaked::LeakBox;
461 ///
462 /// let slab: Bump<[usize; 4]> = Bump::uninit();
463 /// let data = RefCell::new(0xff);
464 ///
465 /// let slot = slab.bump_box().unwrap();
466 /// let cell_box = LeakBox::write(slot, data.borrow());
467 ///
468 /// assert_eq!(**cell_box, 0xff);
469 /// drop(cell_box);
470 ///
471 /// assert!(data.try_borrow_mut().is_ok());
472 /// ```
473 ///
474 /// FIXME(breaking): should return evidence of the level (observed, and post). Something
475 /// similar to `Allocation` but containing a `LeakBox<T>` instead? Introduce that to the sync
476 /// `Bump` allocator as well.
477 ///
478 /// FIXME(breaking): align with sync `Bump::get` (probably rename get to bump_box).
479 pub fn bump_box<'bump, T: 'bump>(
480 &'bump self,
481 ) -> Result<LeakBox<'bump, MaybeUninit<T>>, Failure> {
482 let allocation = self.get_at(self.level())?;
483 Ok(unsafe { allocation.uninit() }.into())
484 }
485
486 /// Allocate space for a slice of `T`s without initializing any.
487 ///
488 /// Retrieve individual `MaybeUninit` elements and wrap them as a `LeakBox` to store values. Or
489 /// use the slice as backing memory for one of the containers from `without-alloc`. Or manually
490 /// initialize them.
491 ///
492 /// ## Usage
493 ///
494 /// Quicksort, implemented recursively, requires a maximum of `log n` stack frames in the worst
495 /// case when implemented optimally. Since each frame is quite large this is wasteful. We can
496 /// use a properly sized buffer instead and implement an iterative solution. (Left as an
497 /// exercise to the reader, or see the examples for `without-alloc` where we use such a dynamic
498 /// allocation with an inline vector as our stack).
499 pub fn bump_array<'bump, T: 'bump>(
500 &'bump self,
501 n: usize,
502 ) -> Result<LeakBox<'bump, [MaybeUninit<T>]>, Failure> {
503 let layout = Layout::array::<T>(n).map_err(|_| Failure::Exhausted)?;
504 let raw = self.alloc(layout).ok_or(Failure::Exhausted)?;
505 let slice = ptr::slice_from_raw_parts_mut(raw.cast().as_ptr(), n);
506 let uninit = unsafe { &mut *slice };
507 Ok(uninit.into())
508 }
509
510 /// Get the number of already allocated bytes.
511 pub fn level(&self) -> Level {
512 Level(self.header.index.get())
513 }
514
515 /// Reset the bump allocator.
516 ///
517 /// This requires a unique reference to the allocator hence no allocation can be alive at this
518 /// point. It will reset the internal count of used bytes to zero.
519 pub fn reset(&mut self) {
520 self.header.index.set(0)
521 }
522
523 fn try_alloc(&self, layout: Layout) -> Option<Allocation<'_>> {
524 let consumed = self.header.index.get();
525 match self.try_alloc_at(layout, consumed) {
526 Ok(alloc) => Some(alloc),
527 Err(Failure::Exhausted) => None,
528 Err(Failure::Mismatch { observed: _ }) => {
529 unreachable!("Count in Cell concurrently modified, this UB")
530 }
531 }
532 }
533
534 fn try_alloc_at(
535 &self,
536 layout: Layout,
537 expect_consumed: usize,
538 ) -> Result<Allocation<'_>, Failure> {
539 assert!(layout.size() > 0);
540 let length = mem::size_of_val(&self.data);
541 // We want to access contiguous slice, so cast to a single cell.
542 let base_ptr = self.data.get().cast::<u8>();
543
544 let alignment = layout.align();
545 let requested = layout.size();
546
547 // Ensure no overflows when calculating offets within.
548 assert!(expect_consumed <= length, "{}/{}", expect_consumed, length);
549
550 let available = length.checked_sub(expect_consumed).unwrap();
551 let ptr_to = base_ptr.wrapping_add(expect_consumed);
552 let offset = ptr_to.align_offset(alignment);
553
554 if Some(requested) > available.checked_sub(offset) {
555 return Err(Failure::Exhausted); // exhausted
556 }
557
558 // `size` can not be zero, saturation will thus always make this true.
559 assert!(offset < available);
560 let at_aligned = expect_consumed.checked_add(offset).unwrap();
561 let new_consumed = at_aligned.checked_add(requested).unwrap();
562 // new_consumed
563 // = consumed + offset + requested [lines above]
564 // <= consumed + available [bail out: exhausted]
565 // <= length [first line of loop]
566 // So it's ok to store `allocated` into `consumed`.
567 assert!(new_consumed <= length);
568 assert!(at_aligned < length);
569
570 // Try to actually allocate.
571 match self.bump(expect_consumed, new_consumed) {
572 Ok(()) => (),
573 Err(observed) => {
574 // Someone else was faster, if you want it then recalculate again.
575 return Err(Failure::Mismatch {
576 observed: Level(observed),
577 });
578 }
579 }
580
581 let aligned = unsafe {
582 // SAFETY:
583 // * `0 <= at_aligned < length` in bounds as checked above.
584 base_ptr.byte_add(at_aligned)
585 };
586
587 Ok(Allocation {
588 ptr: NonNull::new(aligned).unwrap(),
589 lifetime: AllocTime::default(),
590 level: Level(new_consumed),
591 })
592 }
593
594 fn bump(&self, expect: usize, consume: usize) -> Result<(), usize> {
595 debug_assert!(consume <= self.capacity());
596 debug_assert!(expect <= consume);
597
598 let prev = self.header.index.get();
599 if prev != expect {
600 Err(prev)
601 } else {
602 self.header.index.set(consume);
603 Ok(())
604 }
605 }
606}
607
608struct EnsureDerefIsApplicable<T>(core::marker::PhantomData<T>);
609
610impl<T> EnsureDerefIsApplicable<T> {
611 pub const ASSERT: () = {
612 if mem::offset_of!(Bump<T>, _data) != mem::size_of::<Header>() {
613 panic!(
614 // `data` follows header directly, using the macro requires a value for unsized types.
615 "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`).",
616 );
617 }
618 };
619}
620
621impl<T> ops::Deref for Bump<T> {
622 type Target = BumpSlice;
623 fn deref(&self) -> &BumpSlice {
624 // This provokes post-mono error!
625 let _: () = EnsureDerefIsApplicable::<T>::ASSERT;
626
627 let from_layout = Layout::for_value(self);
628 let data_layout = Layout::new::<MaybeUninit<T>>();
629 // Construct a point with the meta data of a slice to `data`, but pointing to the whole
630 // struct instead. This meta data is later copied to the meta data of `bump` when cast.
631 let ptr = (self as *const Self).cast::<MaybeUninit<u8>>();
632 let mem: *const [MaybeUninit<u8>] = ptr::slice_from_raw_parts(ptr, data_layout.size());
633 // Now we have a pointer to BumpSlice with length meta data of the data slice.
634 let bump = unsafe { &*(mem as *const BumpSlice) };
635 debug_assert_eq!(from_layout, Layout::for_value(bump));
636 bump
637 }
638}
639
640impl<T> ops::DerefMut for Bump<T> {
641 fn deref_mut(&mut self) -> &mut BumpSlice {
642 // This provokes post-mono error!
643 let _: () = EnsureDerefIsApplicable::<T>::ASSERT;
644
645 let from_layout = Layout::for_value(self);
646 let data_layout = Layout::new::<MaybeUninit<T>>();
647 // Construct a point with the meta data of a slice to `data`, but pointing to the whole
648 // struct instead. This meta data is later copied to the meta data of `bump` when cast.
649 let ptr = (self as *mut Self).cast::<MaybeUninit<u8>>();
650 let mem: *mut [MaybeUninit<u8>] = ptr::slice_from_raw_parts_mut(ptr, data_layout.size());
651 // Now we have a pointer to BumpSlice with length meta data of the data slice.
652 let bump = unsafe { &mut *(mem as *mut BumpSlice) };
653 debug_assert_eq!(from_layout, Layout::for_value(bump));
654 bump
655 }
656}
657
658struct Header {
659 /// An index into the data field. This index
660 /// will always be an index to an element
661 /// that has not been allocated into.
662 /// Again this is wrapped in a Cell,
663 /// to allow modification with just a
664 /// &self reference.
665 index: Cell<usize>,
666}
667
668impl Header {
669 const fn empty() -> Self {
670 Header {
671 index: Cell::new(0),
672 }
673 }
674}
675
676#[test]
677fn mem_bump_derefs_correctly() {
678 let bump = Bump::<usize>::zeroed();
679 let mem: &BumpSlice = ≎
680 assert_eq!(mem::size_of_val(&bump), mem::size_of_val(mem));
681}