Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 151 additions & 2 deletions static-alloc/src/bump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ pub struct Level(pub(crate) usize);
///
/// [`Level`]: struct.Level.html
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Allocation<'a, T = u8> {
pub struct Allocation<'a, T: ?Sized = u8> {
/// Pointer to the uninitialized region with specified layout.
pub ptr: NonNull<T>,

Expand Down Expand Up @@ -570,6 +570,48 @@ impl<T> Bump<T> {
self.as_view().get_at(level)
}

/// Get an allocation for a slice of a type.
///
/// Returns `None` if the allocation fails (see [`Self::get`]) or if the slice layout can not be
/// computed due to an overflow with this size.
///
/// # Examples
///
/// ```
/// # use static_alloc::bump::Bump;
///
/// let slab: Bump<[usize; 6]> = Bump::uninit();
///
/// let first = slab.get_slice::<usize>(4).unwrap();
/// let second = slab.get_slice::<usize>(2).unwrap();
/// assert!(slab.get_slice::<usize>(1).is_none());
///
/// assert_eq!(first.ptr.len(), 4);
/// assert_eq!(second.ptr.len(), 2);
/// ```
///
/// ```
/// # use static_alloc::bump::Bump;
///
/// let slab: Bump<[usize; 1]> = Bump::uninit();
///
/// let lots_of_empty = slab.get_slice::<()>(usize::MAX).unwrap();
/// assert_eq!(lots_of_empty.ptr.len(), usize::MAX);
/// ```
///
/// ```
/// # use static_alloc::bump::Bump;
///
/// let slab: Bump<[usize; 1]> = Bump::uninit();
///
/// let _exhaust = slab.get_slice::<usize>(1).unwrap();
/// assert!(slab.get_slice::<usize>(1).is_none());
/// let empty_slice = slab.get_slice::<usize>(0).unwrap();
/// ```
pub fn get_slice<V>(&self, len: usize) -> Option<Allocation<'_, [V]>> {
self.as_view().get_slice(len)
}

/// Move a value into an owned allocation.
///
/// For safely initializing a value _after_ a successful allocation, see [`LeakBox::write`].
Expand Down Expand Up @@ -920,6 +962,51 @@ impl BumpSlice {
self.as_view().get_at(level)
}

/// Get an allocation for a slice of a type.
///
/// Returns `None` if the allocation fails (see [`Self::get`]) or if the slice layout can not be
/// computed due to an overflow with this size.
///
/// # Examples
///
/// ```
/// # use static_alloc::bump::{Bump, BumpSlice};
///
/// let backing: Bump<[usize; 6]> = Bump::uninit();
/// let slab = backing.as_bump_slice().unwrap();
///
/// let first = slab.get_slice::<usize>(4).unwrap();
/// let second = slab.get_slice::<usize>(2).unwrap();
/// assert!(slab.get_slice::<usize>(1).is_none());
///
/// assert_eq!(first.ptr.len(), 4);
/// assert_eq!(second.ptr.len(), 2);
/// ```
///
/// ```
/// # use static_alloc::bump::{Bump, BumpSlice};
///
/// let backing: Bump<[usize; 1]> = Bump::uninit();
/// let slab = backing.as_bump_slice().unwrap();
///
/// let lots_of_empty = slab.get_slice::<()>(usize::MAX).unwrap();
/// assert_eq!(lots_of_empty.ptr.len(), usize::MAX);
/// ```
///
/// ```
/// # use static_alloc::bump::{Bump, BumpSlice};
///
/// let backing: Bump<[usize; 1]> = Bump::uninit();
/// let slab = backing.as_bump_slice().unwrap();
///
/// let _exhaust = slab.get_slice::<usize>(1).unwrap();
/// assert!(slab.get_slice::<usize>(1).is_none());
/// let empty_slice = slab.get_slice::<usize>(0).unwrap();
/// ```
pub fn get_slice<V>(&self, len: usize) -> Option<Allocation<'_, [V]>> {
self.as_view().get_slice(len)
}

/// Move a value into an owned allocation.
///
/// For safely initializing a value _after_ a successful allocation, see [`LeakBox::write`].
Expand Down Expand Up @@ -1211,6 +1298,27 @@ impl<'lt> BumpView<'lt> {
})
}

pub fn get_slice<V>(&self, len: usize) -> Option<Allocation<'lt, [V]>> {
if len == 0 {
return Some(Allocation::for_empty_slice(self.level()));
}

let (layout, _) = Layout::new::<V>().repeat(len).ok()?;

if layout.size() == 0 {
// Synthesize the slice for this ZST.
return Some(Allocation::for_zst_slice(len, self.level()));
};

let alloc = self.get_layout(layout)?;

Some(Allocation {
ptr: NonNull::slice_from_raw_parts(alloc.ptr.cast(), len),
lifetime: alloc.lifetime,
level: alloc.level,
})
}

pub fn leak_box<V>(self, val: V) -> Option<LeakBox<'lt, V>> {
let Allocation { ptr, lifetime, .. } = self.get::<V>()?;
Some(unsafe { LeakBox::new_from_raw_non_null(ptr, val, lifetime) })
Expand All @@ -1232,7 +1340,7 @@ impl<'lt> BumpView<'lt> {
/// - As a corollary, particular it must be in-bounds of the allocator's memory.
/// - Another consequence, the result pointer must be aligned for the requested type.
pub unsafe fn get_unchecked<V>(self, level: Level) -> Allocation<'lt, V> {
debug_assert!(level.0 <= mem::size_of_val(&self.storage));
debug_assert!(level.0 <= mem::size_of_val(self.storage));

debug_assert!(
level <= self.level(),
Expand All @@ -1257,6 +1365,7 @@ impl<'lt> BumpView<'lt> {
}
}

// FIXME: should take `NonZeroLayout`.
fn try_alloc(self, layout: Layout) -> Option<Allocation<'lt>> {
// Guess zero, this will fail when we try to access it and it isn't.
let mut consumed = 0;
Expand All @@ -1277,6 +1386,7 @@ impl<'lt> BumpView<'lt> {
///
/// # Panics
/// This function panics if `expect_consumed` is larger than `length`.
/// FIXME: should take `NonZeroLayout`.
fn try_alloc_at(
self,
layout: Layout,
Expand Down Expand Up @@ -1442,6 +1552,27 @@ impl<'alloc, T> Allocation<'alloc, T> {
level,
}
}

pub(crate) fn for_zst_slice(len: usize, level: Level) -> Allocation<'alloc, [T]> {
assert!(mem::size_of::<T>() == 0);
let alloc: &[T; 0] = &[];

Allocation {
ptr: NonNull::slice_from_raw_parts(NonNull::from(alloc).cast(), len),
lifetime: AllocTime::default(),
level,
}
}

pub(crate) fn for_empty_slice(level: Level) -> Allocation<'alloc, [T]> {
let alloc: &[T; 0] = &[];

Allocation {
ptr: NonNull::from(alloc),
lifetime: AllocTime::default(),
level,
}
}
}

impl<T> LeakError<T> {
Expand Down Expand Up @@ -1485,6 +1616,24 @@ unsafe impl<T> GlobalAlloc for Bump<T> {
}
}

unsafe impl GlobalAlloc for &'static BumpSlice {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// Safety: just handing over arguments exactly as is. These two allocators are 'compatible'
// in the sense they hold onto the same value handles.
unsafe { GlobalAlloc::alloc(&self.as_view(), layout) }
}

unsafe fn realloc(&self, ptr: *mut u8, current: Layout, new_size: usize) -> *mut u8 {
// Safety: just handing over arguments exactly as is. These two allocators are 'compatible'
// in the sense they hold onto the same value handles.
unsafe { GlobalAlloc::realloc(&self.as_view(), ptr, current, new_size) }
}

unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
// We are a slab allocator and do not deallocate.
}
}

unsafe impl GlobalAlloc for BumpView<'_> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
BumpView::alloc(*self, layout)
Expand Down
33 changes: 15 additions & 18 deletions static-alloc/src/leaked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
//!
//! FIXME(breaking): Naming. `leaking` implies the `Drop` of the value as well but we do the
//! precise opposite.
use core::pin::Pin;
use alloc_traits::AllocTime;
use core::pin::Pin;

use core::{
alloc::Layout,
fmt,
hash,
fmt, hash,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
Expand Down Expand Up @@ -50,10 +49,7 @@ impl<T> Alloca<T> {
/// llvm might still reserve stack space for all variants including a probe and thus
/// prematurely assume we have hit the bottom of the available stack space. This is not very
/// likely to occur in practice.
pub fn run<R>(
&self,
run: impl FnOnce(&mut [MaybeUninit<T>]) -> R
) -> R {
pub fn run<R>(&self, run: impl FnOnce(&mut [MaybeUninit<T>]) -> R) -> R {
// Required size to surely have enough space for an aligned allocation.
let required_size = self.padded_layout().size();

Expand Down Expand Up @@ -98,10 +94,7 @@ impl<T> Alloca<T> {
}
}

fn run_with<I, R, F:FnOnce(&mut [MaybeUninit<T>]) -> R>(
&self,
run: F
) -> R {
fn run_with<I, R, F: FnOnce(&mut [MaybeUninit<T>]) -> R>(&self, run: F) -> R {
use crate::unsync::Bump;
let mem = Bump::<I>::uninit();
let slot = mem.bump_array::<T>(self.len).unwrap();
Expand Down Expand Up @@ -175,7 +168,7 @@ impl<'ctx, T> LeakBox<'ctx, T> {
// * It is valid for write as it is the only pointer to it.
// * The allocation lives for at least `'ctx`.
unsafe { core::ptr::write(pointer.as_ptr(), val) };
Self { pointer, lifetime, }
Self { pointer, lifetime }
}
}

Expand Down Expand Up @@ -236,12 +229,15 @@ impl<'ctx, T: ?Sized> LeakBox<'ctx, T> {
/// Dropping this `LeakBox` will drop the instance, which the caller must also guarantee to be
/// sound.
pub unsafe fn from_raw(pointer: *mut T) -> Self {
debug_assert!(!pointer.is_null(), "Null pointer passed to LeakBox::from_raw");
debug_assert!(
!pointer.is_null(),
"Null pointer passed to LeakBox::from_raw"
);

LeakBox {
lifetime: AllocTime::default(),
// Safety: caller guarantees this points to a valid instance. Null never does that.
pointer: unsafe { NonNull::new_unchecked(pointer) },
pointer: unsafe { NonNull::new_unchecked(pointer) },
}
}

Expand Down Expand Up @@ -315,7 +311,8 @@ impl<'ctx, T: ?Sized> LeakBox<'ctx, T> {
/// # Some(()) }
/// ```
pub fn leak<'a>(this: Self) -> &'a mut T
where 'ctx: 'a
where
'ctx: 'a,
{
let pointer = LeakBox::into_raw(this);
// SAFETY:
Expand All @@ -341,7 +338,7 @@ impl<T: 'static> LeakBox<'static, T> {
/// Consider this example:
///
/// ```compile_fail
/// use static_alloc::{Bump, leaked::LeakBox};
/// use static_alloc::{Bump, leaked::LeakBox};
///
/// async fn example(x: usize) -> usize {
/// // Holding reference across yield point.
Expand All @@ -365,7 +362,7 @@ impl<T: 'static> LeakBox<'static, T> {
/// use of a macro or unsafe on the caller's part. Now, with the correct usage of `into_pin`:
///
/// ```
/// use static_alloc::{Bump, leaked::LeakBox};
/// use static_alloc::{Bump, leaked::LeakBox};
///
/// async fn example(x: usize) -> usize {
/// // Holding reference across yield point.
Expand Down Expand Up @@ -442,7 +439,7 @@ impl<'ctx, T> LeakBox<'ctx, T> {
/// about any actual use case.
pub fn from_mut(val: &'ctx mut T) -> Self
where
T: Copy
T: Copy,
{
// SAFETY:
// * Is valid instance
Expand Down
2 changes: 1 addition & 1 deletion static-alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub mod unsync;

// Can't use the macro-call itself within the `doc` attribute. So force it to eval it as part of
// the macro invocation.
//
//
// The inspiration for the macro and implementation is from
// <https://github.com/GuillaumeGomez/doc-comment>
//
Expand Down
Loading
Loading