Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
65 changes: 45 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,41 @@ impl<IT, N> Key<IT, N> {
}
}

/// Module to hide the Entry type which needs to be public due to the generic-array internals.
mod entry {
pub enum Entry<IT> {
Used(IT),
EmptyNext(usize),
EmptyLast
}
}

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<Option<IT>> + ArrayLength<usize> + Unsigned {
items: GenericArray<Option<IT>, N>,
free_list: GenericArray<usize, N>,
where N: ArrayLength<Entry<IT>> + Unsigned {
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
}

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

Self {
items: GenericArray::default(),
free_list: GenericArray::generate(|i: usize| size - i - 1),
// Fill back-to-front
// items: GenericArray::generate(|i: usize| if i == 0 { Entry::EmptyLast } else { Entry::EmptyNext(i - 1) }),
// next_free: size.checked_sub(1),
// Fill front-to-back
items: GenericArray::generate(|i: usize| if i == size - 1 { Entry::EmptyLast } else { Entry::EmptyNext(i + 1) }),
next_free: Some(0),
free_count: size
}
}
Expand All @@ -108,37 +125,45 @@ impl<IT, N> Slots<IT, N>
}

fn free(&mut self, idx: usize) {
self.free_list[self.free_count] = idx;
self.items[idx] = match self.next_free {
Some(n) => Entry::EmptyNext(n),
None => Entry::EmptyLast
};
self.next_free = Some(idx);
self.free_count += 1;
}

fn alloc(&mut self) -> Option<usize> {
if self.count() == self.capacity() {
None
} else {
let i = self.free_list[self.free_count - 1];
let result = self.next_free;
self.next_free = match self.items[result.expect("Count mismatch")] {
Entry::EmptyNext(n) => Some(n),
Entry::EmptyLast => None,
_ => unreachable!("Non-empty item in entry behind free chain"),
};
self.free_count -= 1;
Some(i)
result
}
}

pub fn store(&mut self, item: IT) -> Result<Key<IT, N>, IT> {
match self.alloc() {
Some(i) => {
self.items[i] = Some(item);
self.items[i] = Entry::Used(item);
Ok(Key::new(i))
}
None => Err(item)
}
}

pub fn take(&mut self, key: Key<IT, N>) -> IT {
match self.items[key.index].take() {
Some(item) => {
self.free(key.index);
item
}
None => panic!()
let taken = core::mem::replace(&mut self.items[key.index], Entry::EmptyLast);
self.free(key.index);
match taken {
Entry::Used(item) => item,
_ => panic!()
}
}

Expand All @@ -151,15 +176,15 @@ impl<IT, N> Slots<IT, N>

pub fn try_read<T, F>(&self, key: usize, function: F) -> Option<T> where F: Fn(&IT) -> T {
match &self.items[key] {
Some(item) => Some(function(&item)),
None => None
Entry::Used(item) => Some(function(&item)),
_ => None
}
}

pub fn modify<T, F>(&mut self, key: &Key<IT, N>, function: F) -> T where F: Fn(&mut IT) -> T {
match self.items[key.index] {
Some(ref mut item) => function(item),
None => panic!()
Entry::Used(ref mut item) => function(item),
_ => panic!()
}
}
}
21 changes: 21 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,24 @@ fn store_returns_err_when_full() {

assert!(k2.is_err());
}

#[test]
/// Verify some size bounds: an N long array over IT is not larger than 3 usize + N * IT (as long
/// as IT is larger than two usize and has two niches)
//
// Failes until https://github.com/rust-lang/rust/issues/46213 is resolved (possibly,
// https://github.com/rust-lang/rust/pull/70477 is sufficient)
fn is_compact() {
struct TwoNichesIn16Byte {
n1: u64,
n2: u32,
n3: u16,
n4: u8,
b: bool,
}

assert_eq!(core::mem::size_of::<TwoNichesIn16Byte>(), 16);

let slots: Slots<TwoNichesIn16Byte, U32> = Slots::new();
assert_eq!(core::mem::size_of::<Slots<TwoNichesIn16Byte, U32>>(), 32 * 16 + 3 * core::mem::size_of::<usize>());
}