use core::marker::PhantomData;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
use crate::Incrementable;
#[derive(Debug)]
pub struct Allocator<G = usize, I = usize, GenIndex = (I, G)> {
next_index: Option<I>,
avail_gen_indexes: Vec<GenIndex>,
last_dealloc_next_gen: Option<GenIndex>,
gen_ty: PhantomData<G>,
}
impl<G, I, GenIndex> Allocator<G, I, GenIndex> {
#[must_use]
pub fn new() -> Self
where
I: Default,
{
Self::default()
}
pub fn with_initial_index(index: I) -> Self {
Self {
next_index: Some(index),
avail_gen_indexes: Vec::default(),
last_dealloc_next_gen: None,
gen_ty: PhantomData,
}
}
}
impl<G, I, GenIndex> Default for Allocator<G, I, GenIndex>
where
I: Default,
{
fn default() -> Self {
Self {
next_index: Some(I::default()),
avail_gen_indexes: Vec::default(),
last_dealloc_next_gen: None,
gen_ty: PhantomData,
}
}
}
impl<G, I, GenIndex> Allocator<G, I, GenIndex>
where
GenIndex: From<(I, G)>,
{
#[must_use]
pub fn alloc(&mut self) -> Option<GenIndex>
where
G: Default,
I: Incrementable,
{
self.avail_gen_indexes.pop().or_else(|| {
if let Some(index) = self.next_index.take() {
self.next_index = index.next();
Some(GenIndex::from((index, G::default())))
} else {
None
}
})
}
pub fn dealloc(&mut self, gen_index: GenIndex) -> Option<&GenIndex>
where
GenIndex: Into<(I, G)>,
G: Incrementable,
{
let gen_index: (I, G) = gen_index.into();
if let Some(new_gen) = gen_index.1.next() {
if Some(&new_gen) == G::max().as_ref() {
let avail_gen_index = GenIndex::from((gen_index.0, new_gen));
self.last_dealloc_next_gen = Some(avail_gen_index);
self.last_dealloc_next_gen.as_ref()
} else {
let avail_gen_index = GenIndex::from((gen_index.0, new_gen));
self.avail_gen_indexes.push(avail_gen_index);
self.avail_gen_indexes.last()
}
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use core::cmp::Ordering;
use super::*;
#[test]
fn test_alloc_with_tuple() {
let mut gen_idx_alloc = Allocator::<u32>::default();
let gen_idx_0 = gen_idx_alloc.alloc();
let gen_idx_0 = gen_idx_0.unwrap();
{
assert_eq!(gen_idx_0.0, 0);
assert_eq!(gen_idx_0.1, 0);
}
{
let gen_idx_1 = gen_idx_alloc.alloc();
let gen_idx_1 = gen_idx_1.unwrap();
assert_eq!(gen_idx_1.0, 1);
assert_eq!(gen_idx_1.1, 0);
}
gen_idx_alloc.dealloc(gen_idx_0);
{
let gen_idx_0_again = gen_idx_alloc.alloc();
let gen_idx_0_again = gen_idx_0_again.unwrap();
assert_eq!(gen_idx_0_again.0, 0);
assert_eq!(gen_idx_0_again.1, 1);
}
}
#[test]
fn test_alloc_with_custom_type() {
struct MyIndex {
index: usize,
gen: u32,
}
impl From<(usize, u32)> for MyIndex {
fn from(value: (usize, u32)) -> Self {
Self {
index: value.0,
gen: value.1,
}
}
}
impl From<MyIndex> for (usize, u32) {
fn from(value: MyIndex) -> Self {
(value.index, value.gen)
}
}
let mut gen_idx_alloc = Allocator::<u32, usize, MyIndex>::default();
let gen_idx_0 = gen_idx_alloc.alloc();
let gen_idx_0 = gen_idx_0.unwrap();
{
assert_eq!(gen_idx_0.index, 0);
assert_eq!(gen_idx_0.gen, 0);
}
{
let gen_idx_1 = gen_idx_alloc.alloc();
let gen_idx_1 = gen_idx_1.unwrap();
assert_eq!(gen_idx_1.index, 1);
assert_eq!(gen_idx_1.gen, 0);
}
gen_idx_alloc.dealloc(gen_idx_0);
{
let gen_idx_0_again = gen_idx_alloc.alloc();
let gen_idx_0_again = gen_idx_0_again.unwrap();
assert_eq!(gen_idx_0_again.index, 0);
assert_eq!(gen_idx_0_again.gen, 1);
}
}
#[test]
fn test_alloc_use_all_generations() {
let mut gen_idx_alloc = Allocator::<u8>::default();
let mut gen_index = (0, 0);
for n in 0..(u8::MAX) {
gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, n), gen_index);
let next_gen_index = gen_idx_alloc.dealloc(gen_index).unwrap();
assert_eq!(&(0, n + 1), next_gen_index);
}
assert_eq!((0, 254), gen_index);
let gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((1, 0), gen_index);
}
#[test]
fn test_safety_multiple_deallocs() {
let mut gen_idx_alloc = Allocator::<u8>::default();
let orig_gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, 0), orig_gen_index);
gen_idx_alloc.dealloc(orig_gen_index).unwrap();
gen_idx_alloc.dealloc(orig_gen_index).unwrap();
let gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, 1), gen_index);
let gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, 1), gen_index);
}
#[test]
fn test_safety_multiple_deallocs_after_allocing() {
let mut gen_idx_alloc = Allocator::<u8>::default();
let orig_gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, 0), orig_gen_index);
gen_idx_alloc.dealloc(orig_gen_index).unwrap();
let gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, 1), gen_index);
gen_idx_alloc.dealloc(orig_gen_index).unwrap();
let gen_index = gen_idx_alloc.alloc().unwrap();
assert_eq!((0, 1), gen_index);
}
#[test]
fn test_ordering_of_tuple() {
assert_eq!((0usize, 0u8).cmp(&(1usize, 0u8)), Ordering::Less);
assert_eq!((0usize, 0u8).cmp(&(0usize, 1u8)), Ordering::Less);
assert_eq!((0usize, 1u8).cmp(&(1usize, 0u8)), Ordering::Less);
}
}