-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from PolyMeilex/drop-generational-arena
Drop generational-arena dependency
- Loading branch information
Showing
6 changed files
with
195 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,71 +1,204 @@ | ||
use generational_arena::{Arena, Index}; | ||
use std::marker::PhantomData; | ||
|
||
use std::{cmp::PartialEq, fmt, marker::PhantomData}; | ||
enum Slot<T> { | ||
Free, | ||
Occupied { generation: usize, value: T }, | ||
} | ||
|
||
pub struct TypedIndex<T>(Index, PhantomData<T>); | ||
impl<T> Slot<T> { | ||
fn is_free(&self) -> bool { | ||
matches!(self, Self::Free) | ||
} | ||
|
||
impl<T> From<Index> for TypedIndex<T> { | ||
fn from(id: Index) -> Self { | ||
Self(id, PhantomData) | ||
fn is_occupied(&self) -> bool { | ||
!self.is_free() | ||
} | ||
} | ||
|
||
impl<T> fmt::Display for TypedIndex<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "{:?}", self.0) | ||
pub struct Index<T> { | ||
id: usize, | ||
generation: usize, | ||
_ph: PhantomData<fn() -> T>, | ||
} | ||
|
||
impl<T> PartialEq for Index<T> { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.id == other.id && self.generation == other.generation | ||
} | ||
} | ||
|
||
impl<T> fmt::Debug for TypedIndex<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
self.0.fmt(f) | ||
impl<T> Eq for Index<T> {} | ||
|
||
impl<T> std::fmt::Debug for Index<T> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_struct("Index") | ||
.field("id", &self.id) | ||
.field("generation", &self.generation) | ||
.finish() | ||
} | ||
} | ||
|
||
impl<T> Clone for TypedIndex<T> { | ||
impl<T> Copy for Index<T> {} | ||
impl<T> Clone for Index<T> { | ||
fn clone(&self) -> Self { | ||
Self(self.0, PhantomData) | ||
*self | ||
} | ||
} | ||
|
||
impl<T> Copy for TypedIndex<T> {} | ||
pub(crate) struct Arena<T> { | ||
slots: Vec<Slot<T>>, | ||
generation: usize, | ||
} | ||
|
||
impl<T> PartialEq for TypedIndex<T> { | ||
fn eq(&self, other: &TypedIndex<T>) -> bool { | ||
self.0 == other.0 | ||
impl<T> Default for Arena<T> { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
pub(crate) struct TypedArena<T>(Arena<T>); | ||
|
||
impl<T> TypedArena<T> { | ||
impl<T> Arena<T> { | ||
pub fn new() -> Self { | ||
Self(Arena::new()) | ||
Self { | ||
slots: Vec::new(), | ||
generation: 0, | ||
} | ||
} | ||
|
||
pub fn insert(&mut self, value: T) -> TypedIndex<T> { | ||
self.0.insert(value).into() | ||
pub fn insert(&mut self, value: T) -> Index<T> { | ||
let entry = self.slots.iter_mut().enumerate().find(|(_, e)| e.is_free()); | ||
|
||
match entry { | ||
Some((id, entry)) => { | ||
*entry = Slot::Occupied { | ||
generation: self.generation, | ||
value, | ||
}; | ||
|
||
Index { | ||
id, | ||
generation: self.generation, | ||
_ph: PhantomData, | ||
} | ||
} | ||
None => { | ||
// No free slots found, insert new one | ||
|
||
let id = self.slots.len(); | ||
|
||
self.slots.push(Slot::Occupied { | ||
generation: self.generation, | ||
value, | ||
}); | ||
|
||
Index { | ||
id, | ||
generation: self.generation, | ||
_ph: PhantomData, | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn get(&self, index: TypedIndex<T>) -> Option<&T> { | ||
self.0.get(index.0) | ||
pub fn len(&self) -> usize { | ||
self.slots.iter().filter(|v| v.is_occupied()).count() | ||
} | ||
|
||
pub fn remove(&mut self, index: TypedIndex<T>) -> Option<T> { | ||
self.0.remove(index.0) | ||
pub fn get(&self, index: Index<T>) -> Option<&T> { | ||
match self.slots.get(index.id)? { | ||
Slot::Occupied { generation, value } if *generation == index.generation => Some(value), | ||
_ => None, | ||
} | ||
} | ||
|
||
pub fn remove(&mut self, index: Index<T>) -> Option<T> { | ||
match self.slots.get_mut(index.id)? { | ||
Slot::Occupied { generation, .. } if *generation == index.generation => { | ||
let Slot::Occupied { value, .. } = | ||
std::mem::replace(&mut self.slots[index.id], Slot::Free) | ||
else { | ||
unreachable!(); | ||
}; | ||
|
||
self.generation += 1; | ||
Some(value) | ||
} | ||
_ => None, | ||
} | ||
} | ||
} | ||
|
||
impl<T> std::ops::Deref for TypedArena<T> { | ||
type Target = Arena<T>; | ||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn insert() { | ||
let mut arena = Arena::new(); | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
let a = arena.insert(1); | ||
let b = arena.insert(2); | ||
let c = arena.insert(3); | ||
|
||
assert_eq!((a.generation, a.id), (0, 0)); | ||
assert_eq!((b.generation, b.id), (0, 1)); | ||
assert_eq!((c.generation, c.id), (0, 2)); | ||
|
||
assert_eq!(arena.get(a), Some(&1)); | ||
assert_eq!(arena.get(b), Some(&2)); | ||
assert_eq!(arena.get(c), Some(&3)); | ||
} | ||
} | ||
|
||
impl<T> std::ops::DerefMut for TypedArena<T> { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
&mut self.0 | ||
#[test] | ||
fn remove() { | ||
let mut arena = Arena::new(); | ||
|
||
let a = arena.insert(1); | ||
let b = arena.insert(2); | ||
let c = arena.insert(3); | ||
|
||
assert_eq!((a.generation, a.id), (0, 0)); | ||
assert_eq!((b.generation, b.id), (0, 1)); | ||
assert_eq!((c.generation, c.id), (0, 2)); | ||
|
||
assert_eq!(arena.remove(a), Some(1)); | ||
assert_eq!(arena.remove(b), Some(2)); | ||
assert_eq!(arena.remove(c), Some(3)); | ||
|
||
assert_eq!(arena.get(a), None); | ||
assert_eq!(arena.get(b), None); | ||
assert_eq!(arena.get(c), None); | ||
} | ||
|
||
#[test] | ||
fn remove_and_reinsert() { | ||
let mut arena = Arena::new(); | ||
|
||
let a = arena.insert(1); | ||
let b = arena.insert(2); | ||
let c = arena.insert(3); | ||
|
||
assert_eq!((a.generation, a.id), (0, 0)); | ||
assert_eq!((b.generation, b.id), (0, 1)); | ||
assert_eq!((c.generation, c.id), (0, 2)); | ||
|
||
assert_eq!(arena.remove(a), Some(1)); | ||
assert_eq!(arena.remove(b), Some(2)); | ||
assert_eq!(arena.remove(c), Some(3)); | ||
|
||
let aa = arena.insert(1); | ||
let bb = arena.insert(2); | ||
let cc = arena.insert(3); | ||
|
||
assert_eq!((aa.generation, aa.id), (3, 0)); | ||
assert_eq!((bb.generation, bb.id), (3, 1)); | ||
assert_eq!((cc.generation, cc.id), (3, 2)); | ||
|
||
assert_eq!(arena.get(a), None); | ||
assert_eq!(arena.get(b), None); | ||
assert_eq!(arena.get(c), None); | ||
|
||
assert_eq!(arena.get(aa), Some(&1)); | ||
assert_eq!(arena.get(bb), Some(&2)); | ||
assert_eq!(arena.get(cc), Some(&3)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.