use std::{cmp::Ordering, fmt, hash, marker::PhantomData, num::NonZeroU32};
type Index = NonZeroU32;
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[cfg_attr(
any(feature = "serialize", feature = "deserialize"),
serde(transparent)
)]
pub struct Handle<T> {
index: Index,
#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))]
marker: PhantomData<T>,
}
impl<T> Clone for Handle<T> {
fn clone(&self) -> Self {
Handle {
index: self.index,
marker: self.marker,
}
}
}
impl<T> Copy for Handle<T> {}
impl<T> PartialEq for Handle<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
impl<T> Eq for Handle<T> {}
impl<T> PartialOrd for Handle<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.index.partial_cmp(&other.index)
}
}
impl<T> Ord for Handle<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.index.cmp(&other.index)
}
}
impl<T> fmt::Debug for Handle<T> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "Handle({})", self.index)
}
}
impl<T> hash::Hash for Handle<T> {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
self.index.hash(hasher)
}
}
impl<T> Handle<T> {
#[cfg(test)]
pub const DUMMY: Self = Handle {
index: unsafe { NonZeroU32::new_unchecked(!0) },
marker: PhantomData,
};
pub(crate) fn new(index: Index) -> Self {
Handle {
index,
marker: PhantomData,
}
}
pub fn index(self) -> usize {
let index = self.index.get() - 1;
index as usize
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
#[cfg_attr(
any(feature = "serialize", feature = "deserialize"),
serde(transparent)
)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Arena<T> {
data: Vec<T>,
}
impl<T> Default for Arena<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Arena<T> {
pub fn new() -> Self {
Arena { data: Vec::new() }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = (Handle<T>, &T)> {
self.data.iter().enumerate().map(|(i, v)| {
let position = i + 1;
let index = unsafe { Index::new_unchecked(position as u32) };
(Handle::new(index), v)
})
}
pub fn append(&mut self, value: T) -> Handle<T> {
let position = self.data.len() + 1;
let index = unsafe { Index::new_unchecked(position as u32) };
self.data.push(value);
Handle::new(index)
}
pub fn fetch_if<F: Fn(&T) -> bool>(&self, fun: F) -> Option<Handle<T>> {
self.data
.iter()
.position(fun)
.map(|index| Handle::new(unsafe { Index::new_unchecked((index + 1) as u32) }))
}
pub fn fetch_if_or_append<F: Fn(&T, &T) -> bool>(&mut self, value: T, fun: F) -> Handle<T> {
if let Some(index) = self.data.iter().position(|d| fun(d, &value)) {
let index = unsafe { Index::new_unchecked((index + 1) as u32) };
Handle::new(index)
} else {
self.append(value)
}
}
pub fn fetch_or_append(&mut self, value: T) -> Handle<T>
where
T: PartialEq,
{
self.fetch_if_or_append(value, T::eq)
}
pub fn try_get(&self, handle: Handle<T>) -> Option<&T> {
self.data.get(handle.index.get() as usize - 1)
}
pub fn get_mut(&mut self, handle: Handle<T>) -> &mut T {
self.data.get_mut(handle.index.get() as usize - 1).unwrap()
}
}
impl<T> std::ops::Index<Handle<T>> for Arena<T> {
type Output = T;
fn index(&self, handle: Handle<T>) -> &T {
let index = handle.index.get() - 1;
&self.data[index as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn append_non_unique() {
let mut arena: Arena<u8> = Arena::new();
let t1 = arena.append(0);
let t2 = arena.append(0);
assert!(t1 != t2);
assert!(arena[t1] == arena[t2]);
}
#[test]
fn append_unique() {
let mut arena: Arena<u8> = Arena::new();
let t1 = arena.append(0);
let t2 = arena.append(1);
assert!(t1 != t2);
assert!(arena[t1] != arena[t2]);
}
#[test]
fn fetch_or_append_non_unique() {
let mut arena: Arena<u8> = Arena::new();
let t1 = arena.fetch_or_append(0);
let t2 = arena.fetch_or_append(0);
assert!(t1 == t2);
assert!(arena[t1] == arena[t2])
}
#[test]
fn fetch_or_append_unique() {
let mut arena: Arena<u8> = Arena::new();
let t1 = arena.fetch_or_append(0);
let t2 = arena.fetch_or_append(1);
assert!(t1 != t2);
assert!(arena[t1] != arena[t2]);
}
}