Skip to content
Closed
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
53 changes: 47 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ pub use generic_array::ArrayLength;

use generic_array::typenum::Unsigned;

mod enforcement_style {
/// A type-level choice between KeysAreValid and MayPanic
pub trait EnforcementStyle {}
}

/// Type indicator for Slots whose keys can only be used to perform non-panicking access
pub struct KeysAreValid;

impl enforcement_style::EnforcementStyle for KeysAreValid {}

/// Type indicator for Slots whose keys may become unusable (or become unusable and later refer to
/// any other item allocated in the same slot), but which can delete items based on indices in
/// return.
pub struct MayPanic;

impl enforcement_style::EnforcementStyle for MayPanic {}

pub struct Key<IT, N> {
index: usize,
_item_marker: PhantomData<IT>,
Expand Down Expand Up @@ -91,24 +108,30 @@ use entry::Entry;
// Data type that stores values and returns a key that can be used to manipulate
// the stored values.
// Values can be read by anyone but can only be modified using the key.
pub struct Slots<IT, N>
where N: ArrayLength<Entry<IT>> + Unsigned {
pub struct Slots<IT, N, E=KeysAreValid>
where N: ArrayLength<Entry<IT>> + Unsigned,
E: enforcement_style::EnforcementStyle,
{
items: GenericArray<Entry<IT>, N>,
// Could be optimized by making it just usize and relying on free_count to determine its
// validity
next_free: Option<usize>,
free_count: usize
free_count: usize,
enforcement_style: PhantomData<E>
}

impl<IT, N> Slots<IT, N>
where N: ArrayLength<Entry<IT>> + Unsigned {
impl<IT, N, E> Slots<IT, N, E>
where N: ArrayLength<Entry<IT>> + Unsigned,
E: enforcement_style::EnforcementStyle
{
pub fn new() -> Self {
let size = N::to_usize();

Self {
items: GenericArray::generate(|i| i.checked_sub(1).map(Entry::EmptyNext).unwrap_or(Entry::EmptyLast)),
next_free: size.checked_sub(1),
free_count: size
free_count: size,
enforcement_style: PhantomData,
}
}

Expand Down Expand Up @@ -180,3 +203,21 @@ impl<IT, N> Slots<IT, N>
}
}
}

impl<IT, N> Slots<IT, N, MayPanic>
where N: ArrayLength<Entry<IT>> + Unsigned,
{

pub fn try_take(&mut self, key: usize) -> Option<IT> {
if self.try_read(key, |_| ()).is_none() {
return None;
}

let taken = core::mem::replace(&mut self.items[key], Entry::EmptyLast);
self.free(key);
match taken {
Entry::Used(item) => Some(item),
_ => panic!()
}
}
}
11 changes: 11 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ fn key_can_be_used_to_read_value() {
assert_eq!(5, slots.read(&k1, |&w| w));
}

#[test]
#[should_panic(expected="explicit panic")]
fn uncheckedindex_can_be_used_to_panic() {
let mut slots: Slots<_, U8, slots::MayPanic> = Slots::new();
let k1 = slots.store(5).unwrap();
let k2 = k1.index();

assert_eq!(Some(5), slots.try_take(k2));
slots.take(k1);
}

#[test]
fn size_can_be_1() {
let mut slots: Slots<_, U1> = Slots::new();
Expand Down