#![no_std]
#[cfg(feature = "alloc")]
pub mod vec;
pub mod slot;
use core::fmt::{Debug, Display, Formatter};
use core::marker::PhantomData;
use crate::slot::Slot;
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub struct Id(usize);
pub struct MemoryStorage<T, U>
where
U: AsRef<[Slot<T>]> + AsMut<[Slot<T>]>, {
pub(crate) storage: U,
pub(crate) next_free_slot: Option<usize>,
pub(crate) last_free_slot: Option<usize>,
pub(crate) taken_slots: usize,
pub(crate) capacity: usize,
pub(crate) _marker: PhantomData<T>,
}
impl<T, U> MemoryStorage<T, U>
where
U: AsRef<[Slot<T>]> + AsMut<[Slot<T>]>, {
pub fn clear(&mut self) {
let capacity = self.capacity;
if capacity == 0 {
return;
}
for i in 0..capacity {
self.storage
.as_mut()[i] = Slot::NextFreeSlot(Some(i + 1));
}
self.storage
.as_mut()[capacity - 1] = Slot::NextFreeSlot(None);
self.next_free_slot = Some(0);
self.last_free_slot = Some(capacity - 1);
self.taken_slots = 0;
}
pub fn insert(&mut self, item: T) -> Result<Id, InternalStorageFullError<T>> {
match self.next_free_slot {
None => Err(InternalStorageFullError(item)),
Some(next_free_slot) => Ok(self.fill_free_slot(next_free_slot, item)),
}
}
fn fill_free_slot(&mut self, free_slot: usize, item: T) -> Id {
let next_free_slot = if let Slot::NextFreeSlot(next_free_slot) = self.storage.as_ref()[free_slot] {
next_free_slot
} else {
unreachable!("Slot wasn't free!");
};
self.taken_slots += 1;
match next_free_slot {
None => {
self.next_free_slot = None;
self.last_free_slot = None;
}
Some(_) =>
self.next_free_slot = next_free_slot,
}
self.storage.as_mut()[free_slot] = Slot::Taken(item);
Id(free_slot)
}
pub fn remove(&mut self, id: Id) -> T {
let id = id.0;
let slot = core::mem::replace(&mut self.storage.as_mut()[id], Slot::NextFreeSlot(None));
if slot.is_free() {
panic!("No item stored at index!");
}
self.taken_slots -= 1;
match self.last_free_slot {
Some(free_slot) => {
if let Some(slot) = self.storage.as_mut().get_mut(free_slot) {
if let Slot::NextFreeSlot(next_free_slot) = slot {
next_free_slot.replace(id);
}
} else {
unreachable!("Slot should exist!")
}
self.last_free_slot = Some(id);
},
None => {
self.next_free_slot = Some(id);
self.last_free_slot = Some(id);
},
}
slot.unwrap_taken()
}
pub fn get(&self, id: Id) -> Option<&T> {
self.storage
.as_ref()
.get(id.0)?
.taken()
}
pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
self.storage
.as_mut()
.get_mut(id.0)?
.taken_mut()
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn taken_slots(&self) -> usize {
self.taken_slots
}
pub fn storage_ref(&self) -> &U {
&self.storage
}
pub fn storage(self) -> U {
self.storage
}
}
pub type SlotArray<T, const S: usize> = [Slot<T>; S];
pub fn new_with_array<T, const S: usize>() -> MemoryStorage<T, SlotArray<T, S>> {
let array = initiate_array::<T, S>();
let next_free_slot;
let last_free_slot;
if S == 0 {
next_free_slot = None;
last_free_slot = None;
} else {
next_free_slot = Some(0);
last_free_slot = Some(S - 1);
}
MemoryStorage {
storage: array,
next_free_slot,
last_free_slot,
taken_slots: 0,
capacity: S,
_marker: Default::default(),
}
}
fn initiate_array<T, const S: usize>() -> SlotArray<T, S> {
let mut array: [Slot<T>; S] = core::array::from_fn(|i| {
Slot::NextFreeSlot(Some(i + 1))
});
if S != 0 {
array[S - 1] = Slot::NextFreeSlot(None);
}
array
}
pub struct InternalStorageFullError<T>(pub T);
impl<T> InternalStorageFullError<T> {
pub fn value(self) -> T {
self.0
}
}
impl<T> Debug for InternalStorageFullError<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Internal storage is full!")
}
}
impl<T> Display for InternalStorageFullError<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Debug::fmt(self, f)
}
}
#[cfg(test)]
mod tests {
use crate::{initiate_array, new_with_array};
#[test]
fn test_array() {
let mut ms = new_with_array::<(), 3>();
let _ = ms.insert(());
let id = ms.insert(()).expect("I need this ID!");
let _ = ms.insert(());
ms.remove(id);
let _ = ms.insert(());
assert_eq!(ms.taken_slots, 3);
ms.clear();
assert_eq!(ms.taken_slots, 0);
}
#[test]
fn test_initiate_array() {
let array = initiate_array::<(), 3>();
assert_eq!(array[0].next_free(), Some(1));
assert_eq!(array[1].next_free(), Some(2));
assert_eq!(array[2].next_free(), None);
}
}