Skip to content

Commit bbb7cdb

Browse files
committed
Add Bump::get_unchecked to collect an iterator
1 parent 26322c6 commit bbb7cdb

File tree

1 file changed

+100
-64
lines changed

1 file changed

+100
-64
lines changed

static-alloc/src/bump.rs

Lines changed: 100 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use core::alloc::{GlobalAlloc, Layout};
88
use core::cell::UnsafeCell;
99
use core::mem::{self, MaybeUninit};
10-
use core::ptr::{NonNull, null_mut};
10+
use core::ptr::{null_mut, NonNull};
1111

1212
#[cfg(not(feature = "polyfill"))]
1313
use core::sync::atomic::{AtomicUsize, Ordering};
@@ -205,12 +205,13 @@ pub struct LeakError<T> {
205205
/// };
206206
///
207207
/// let mut level: Level = BUMP.level();
208-
/// let mut begin: *mut u64;
208+
/// let mut start: Level = BUMP.level();
209209
/// let mut count;
210210
///
211211
/// match BUMP.leak_at(first, level) {
212-
/// Ok((first, first_level)) => {
213-
/// begin = first;
212+
/// Ok((_first, first_level)) => {
213+
/// // Note we must throw away the first pointer. Its provenance does not
214+
/// // cover the other fields. Only its level can be used.
214215
/// level = first_level;
215216
/// count = 1;
216217
/// },
@@ -227,10 +228,12 @@ pub struct LeakError<T> {
227228
/// });
228229
///
229230
/// unsafe {
231+
/// // Safety: we have an allocation here
232+
/// let begin = BUMP.get_unchecked(start);
230233
/// // SAFETY: all `count` allocations are contiguous, begin is well aligned and no
231234
/// // reference is currently pointing at any of the values. The lifetime is `'static` as
232235
/// // the BUMP itself is static.
233-
/// slice::from_raw_parts_mut(begin, count)
236+
/// slice::from_raw_parts_mut(begin.ptr.as_ptr(), count)
234237
/// }
235238
/// }
236239
///
@@ -247,7 +250,7 @@ pub struct Level(pub(crate) usize);
247250
///
248251
/// [`Level`]: struct.Level.html
249252
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
250-
pub struct Allocation<'a, T=u8> {
253+
pub struct Allocation<'a, T = u8> {
251254
/// Pointer to the uninitialized region with specified layout.
252255
pub ptr: NonNull<T>,
253256

@@ -372,10 +375,12 @@ impl<T> Bump<T> {
372375
///
373376
/// # Panics
374377
/// This function may panic if the provided `level` is from a different slab.
375-
pub fn alloc_at(&self, layout: Layout, level: Level)
376-
-> Result<Allocation, Failure>
377-
{
378-
let Allocation { ptr, lifetime, level } = self.try_alloc_at(layout, level.0)?;
378+
pub fn alloc_at(&self, layout: Layout, level: Level) -> Result<Allocation, Failure> {
379+
let Allocation {
380+
ptr,
381+
lifetime,
382+
level,
383+
} = self.try_alloc_at(layout, level.0)?;
379384

380385
Ok(Allocation {
381386
ptr: ptr.cast(),
@@ -390,9 +395,7 @@ impl<T> Bump<T> {
390395
/// bound by the lifetime of the reference to the allocator.
391396
///
392397
/// [`Uninit`]: ../uninit/struct.Uninit.html
393-
pub fn get_layout(&self, layout: Layout)
394-
-> Option<Allocation<'_>>
395-
{
398+
pub fn get_layout(&self, layout: Layout) -> Option<Allocation<'_>> {
396399
self.try_alloc(layout)
397400
}
398401

@@ -405,9 +408,7 @@ impl<T> Bump<T> {
405408
/// this allocation with the preceding or succeeding one.
406409
///
407410
/// [`Uninit`]: ../uninit/struct.Uninit.html
408-
pub fn get_layout_at(&self, layout: Layout, at: Level)
409-
-> Result<Allocation<'_>, Failure>
410-
{
411+
pub fn get_layout_at(&self, layout: Layout, at: Level) -> Result<Allocation<'_>, Failure> {
411412
self.try_alloc_at(layout, at.0)
412413
}
413414

@@ -438,7 +439,11 @@ impl<T> Bump<T> {
438439
}
439440

440441
let layout = Layout::new::<V>();
441-
let Allocation { ptr, lifetime, level, } = self.try_alloc(layout)?;
442+
let Allocation {
443+
ptr,
444+
lifetime,
445+
level,
446+
} = self.try_alloc(layout)?;
442447

443448
Some(Allocation {
444449
ptr: ptr.cast(),
@@ -465,7 +470,11 @@ impl<T> Bump<T> {
465470
}
466471

467472
let layout = Layout::new::<V>();
468-
let Allocation { ptr, lifetime, level, } = self.try_alloc_at(layout, level.0)?;
473+
let Allocation {
474+
ptr,
475+
lifetime,
476+
level,
477+
} = self.try_alloc_at(layout, level.0)?;
469478

470479
Ok(Allocation {
471480
// It has exactly size and alignment for `V` as requested.
@@ -531,9 +540,7 @@ impl<T> Bump<T> {
531540
/// ```
532541
pub fn leak_box<V>(&self, val: V) -> Option<LeakBox<'_, V>> {
533542
let Allocation { ptr, lifetime, .. } = self.get::<V>()?;
534-
Some(unsafe {
535-
LeakBox::new_from_raw_non_null(ptr, val, lifetime)
536-
})
543+
Some(unsafe { LeakBox::new_from_raw_non_null(ptr, val, lifetime) })
537544
}
538545

539546
/// Move a value into an owned allocation.
@@ -543,9 +550,7 @@ impl<T> Bump<T> {
543550
/// [`leak_box`]: #method.leak_box
544551
pub fn leak_box_at<V>(&self, val: V, level: Level) -> Result<LeakBox<'_, V>, Failure> {
545552
let Allocation { ptr, lifetime, .. } = self.get_at::<V>(level)?;
546-
Ok(unsafe {
547-
LeakBox::new_from_raw_non_null(ptr, val, lifetime)
548-
})
553+
Ok(unsafe { LeakBox::new_from_raw_non_null(ptr, val, lifetime) })
549554
}
550555

551556
/// Observe the current level.
@@ -558,16 +563,51 @@ impl<T> Bump<T> {
558563
Level(self.consumed.load(Ordering::SeqCst))
559564
}
560565

561-
fn try_alloc(&self, layout: Layout)
562-
-> Option<Allocation<'_>>
563-
{
566+
/// Get a pointer to an existing allocation at a specific level.
567+
///
568+
/// The resulting pointer may be used to access an arbitrary allocation starting at the pointer
569+
/// (i.e. including additional allocations immediately afterwards) but the caller is
570+
/// responsible for ensuring that these accesses do not overlap other accesses. There must be
571+
/// no more life [`LeakBox`] to any allocation being accessed this way.
572+
///
573+
/// # Safety
574+
///
575+
/// - The level must refer to an existing allocation, i.e. it must previously have been
576+
/// returned in [`Allocation::level`].
577+
/// - As a corollary, particular it must be in-bounds of the allocator's memory.
578+
/// - Another consequence, the result pointer must be aligned for the requested type.
579+
pub unsafe fn get_unchecked<V>(&self, level: Level) -> Allocation<'_, V> {
580+
debug_assert!(level.0 <= mem::size_of_val(&self.storage));
581+
582+
debug_assert!(
583+
level <= self.level(),
584+
"Tried to access an allocation that does not yet exist"
585+
);
586+
587+
let base_ptr = self.storage.get() as *mut T as *mut u8;
588+
let object = base_ptr.add(level.0);
589+
let nonnull = NonNull::new_unchecked(object).cast::<V>();
590+
591+
debug_assert!(
592+
nonnull.as_ptr().is_aligned(),
593+
"Tried to access an allocation with improper type"
594+
);
595+
596+
Allocation {
597+
ptr: nonnull,
598+
lifetime: AllocTime::default(),
599+
level,
600+
}
601+
}
602+
603+
fn try_alloc(&self, layout: Layout) -> Option<Allocation<'_>> {
564604
// Guess zero, this will fail when we try to access it and it isn't.
565605
let mut consumed = 0;
566606
loop {
567607
match self.try_alloc_at(layout, consumed) {
568608
Ok(alloc) => return Some(alloc),
569609
Err(Failure::Exhausted) => return None,
570-
Err(Failure::Mismatch{ observed }) => consumed = observed.0,
610+
Err(Failure::Mismatch { observed }) => consumed = observed.0,
571611
}
572612
}
573613
}
@@ -580,14 +620,14 @@ impl<T> Bump<T> {
580620
///
581621
/// # Panics
582622
/// This function panics if `expect_consumed` is larger than `length`.
583-
fn try_alloc_at(&self, layout: Layout, expect_consumed: usize)
584-
-> Result<Allocation<'_>, Failure>
585-
{
623+
fn try_alloc_at(
624+
&self,
625+
layout: Layout,
626+
expect_consumed: usize,
627+
) -> Result<Allocation<'_>, Failure> {
586628
assert!(layout.size() > 0);
587629
let length = mem::size_of::<T>();
588-
let base_ptr = self.storage.get()
589-
as *mut T
590-
as *mut u8;
630+
let base_ptr = self.storage.get() as *mut T as *mut u8;
591631

592632
let alignment = layout.align();
593633
let requested = layout.size();
@@ -620,8 +660,10 @@ impl<T> Bump<T> {
620660
Ok(()) => (),
621661
Err(observed) => {
622662
// Someone else was faster, if you want it then recalculate again.
623-
return Err(Failure::Mismatch { observed: Level(observed) });
624-
},
663+
return Err(Failure::Mismatch {
664+
observed: Level(observed),
665+
});
666+
}
625667
}
626668

627669
let aligned = unsafe {
@@ -741,9 +783,7 @@ impl<T> Bump<T> {
741783
/// resource on failure.
742784
///
743785
// #[deprecated = "Use leak_box_at and initialize it with the value. This does not move the value in the failure case."]
744-
pub fn leak_at<V>(&self, val: V, level: Level)
745-
-> Result<(&mut V, Level), LeakError<V>>
746-
{
786+
pub fn leak_at<V>(&self, val: V, level: Level) -> Result<(&mut V, Level), LeakError<V>> {
747787
let alloc = match self.get_at::<V>(level) {
748788
Ok(alloc) => alloc,
749789
Err(err) => return Err(LeakError::new(val, err)),
@@ -777,12 +817,14 @@ impl<T> Bump<T> {
777817
assert!(expect_consumed <= new_consumed);
778818
assert!(new_consumed <= mem::size_of::<T>());
779819

780-
self.consumed.compare_exchange(
781-
expect_consumed,
782-
new_consumed,
783-
Ordering::SeqCst,
784-
Ordering::SeqCst,
785-
).map(drop)
820+
self.consumed
821+
.compare_exchange(
822+
expect_consumed,
823+
new_consumed,
824+
Ordering::SeqCst,
825+
Ordering::SeqCst,
826+
)
827+
.map(drop)
786828
}
787829
}
788830

@@ -844,14 +886,14 @@ impl<'alloc, T> Allocation<'alloc, T> {
844886
Allocation {
845887
ptr: NonNull::from(alloc).cast(),
846888
lifetime: AllocTime::default(),
847-
level: level,
889+
level,
848890
}
849891
}
850892
}
851893

852894
impl<T> LeakError<T> {
853895
fn new(val: T, failure: Failure) -> Self {
854-
LeakError { val, failure, }
896+
LeakError { val, failure }
855897
}
856898

857899
/// Inspect the cause of this error.
@@ -866,7 +908,7 @@ impl<T> LeakError<T> {
866908
}
867909

868910
// SAFETY: at most one thread gets a pointer to each chunk of data.
869-
unsafe impl<T> Sync for Bump<T> { }
911+
unsafe impl<T> Sync for Bump<T> {}
870912

871913
unsafe impl<T> GlobalAlloc for Bump<T> {
872914
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
@@ -875,12 +917,7 @@ unsafe impl<T> GlobalAlloc for Bump<T> {
875917
.unwrap_or_else(null_mut)
876918
}
877919

878-
unsafe fn realloc(
879-
&self,
880-
ptr: *mut u8,
881-
current: Layout,
882-
new_size: usize,
883-
) -> *mut u8 {
920+
unsafe fn realloc(&self, ptr: *mut u8, current: Layout, new_size: usize) -> *mut u8 {
884921
let current = NonZeroLayout::from_layout(current.into()).unwrap();
885922
// As guaranteed, `new_size` is greater than 0.
886923
let new_size = core::num::NonZeroUsize::new_unchecked(new_size);
@@ -907,23 +944,22 @@ unsafe impl<T> GlobalAlloc for Bump<T> {
907944
}
908945
}
909946

910-
fn layout_reallocated(layout: NonZeroLayout, target: core::num::NonZeroUsize)
911-
-> Option<NonZeroLayout>
912-
{
947+
fn layout_reallocated(
948+
layout: NonZeroLayout,
949+
target: core::num::NonZeroUsize,
950+
) -> Option<NonZeroLayout> {
913951
// This may not be a valid layout.
914952
let layout = Layout::from_size_align(target.get(), layout.align()).ok()?;
915953
// This must succeed though, as the size was non-zero.
916954
Some(NonZeroLayout::from_layout(layout.into()).unwrap())
917955
}
918956

919957
unsafe impl<'alloc, T> LocalAlloc<'alloc> for Bump<T> {
920-
fn alloc(&'alloc self, layout: NonZeroLayout)
921-
-> Option<alloc_traits::Allocation<'alloc>>
922-
{
958+
fn alloc(&'alloc self, layout: NonZeroLayout) -> Option<alloc_traits::Allocation<'alloc>> {
923959
let raw_alloc = Bump::get_layout(self, layout.into())?;
924960
Some(alloc_traits::Allocation {
925961
ptr: raw_alloc.ptr,
926-
layout: layout,
962+
layout,
927963
lifetime: AllocTime::default(),
928964
})
929965
}
@@ -945,8 +981,7 @@ unsafe impl<'alloc, T> LocalAlloc<'alloc> for Bump<T> {
945981
alloc: alloc_traits::Allocation<'alloc>,
946982
layout: NonZeroLayout,
947983
) -> Option<alloc_traits::Allocation<'alloc>> {
948-
if alloc.ptr.as_ptr() as usize % layout.align() == 0
949-
&& alloc.layout.size() >= layout.size()
984+
if alloc.ptr.as_ptr() as usize % layout.align() == 0 && alloc.layout.size() >= layout.size()
950985
{
951986
// Obvious fit, nothing to do.
952987
return Some(alloc_traits::Allocation {
@@ -963,7 +998,8 @@ unsafe impl<'alloc, T> LocalAlloc<'alloc> for Bump<T> {
963998
core::ptr::copy_nonoverlapping(
964999
alloc.ptr.as_ptr(),
9651000
new_alloc.ptr.as_ptr(),
966-
layout.size().min(alloc.layout.size()).into());
1001+
layout.size().min(alloc.layout.size()).into(),
1002+
);
9671003
// No dealloc.
9681004
return Some(new_alloc);
9691005
}

0 commit comments

Comments
 (0)