use crate::Id;
pub(crate) struct IdMap<T> {
objects: Vec<Item<T>>,
}
enum Item<T> {
Empty,
Reserved,
Occupied(T),
}
impl<T> Item<T> {
fn is_empty(&self) -> bool {
matches!(self, Item::Empty)
}
fn as_ref(&self) -> Option<&T> {
match self {
Item::Occupied(ref object) => Some(object),
_ => None,
}
}
}
impl<T> IdMap<T> {
pub(crate) fn new() -> IdMap<T> {
Self {
objects: Vec::new(),
}
}
#[allow(unused)]
pub(crate) fn insert(&mut self, object: T) -> Id {
let item = self.objects.iter().enumerate().find(|(_, o)| o.is_empty());
if let Some((id, _)) = item {
self.objects[id] = Item::Occupied(object);
id as Id
} else {
self.objects.push(Item::Occupied(object));
(self.objects.len() - 1) as Id
}
}
pub(crate) fn insert_at(&mut self, id: Id, object: T) {
assert!(self.objects.len() > id as usize);
match &mut self.objects[id as usize] {
val @ Item::Reserved => *val = Item::Occupied(object),
_ => unreachable!("IdMap insert should only be into a reserved location"),
}
}
pub(crate) fn reserve(&mut self) -> Id {
let item = self.objects.iter().enumerate().find(|(_, o)| o.is_empty());
if let Some((id, _)) = item {
self.objects[id] = Item::Reserved;
id as Id
} else {
self.objects.push(Item::Reserved);
(self.objects.len() - 1) as Id
}
}
pub(crate) fn remove(&mut self, id: Id) {
if self.objects.len() <= id as usize {
return;
}
self.objects[id as usize] = Item::Empty;
}
pub(crate) fn clear(&mut self) {
self.objects.clear();
}
pub(crate) fn get(&self, id: Id) -> Option<&T> {
if (id as usize) < self.objects.len() {
self.objects[id as usize].as_ref()
} else {
None
}
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (Id, &T)> {
self.objects
.iter()
.enumerate()
.filter_map(|(i, obj)| obj.as_ref().map(|o| (i as Id, o)))
}
}
#[test]
fn test_id_map() {
let mut map = IdMap::new();
assert_eq!(map.insert(0), 0);
assert_eq!(map.insert(1), 1);
map.remove(2);
assert_eq!(map.get(0), Some(&0));
assert_eq!(map.get(1), Some(&1));
assert_eq!(map.get(2), None);
map.remove(0);
assert_eq!(map.get(0), None);
assert_eq!(map.get(1), Some(&1));
assert_eq!(map.insert(2), 0);
assert_eq!(map.get(0), Some(&2));
assert_eq!(map.get(1), Some(&1));
assert_eq!(map.reserve(), 2);
map.remove(1);
assert_eq!(map.get(0), Some(&2));
assert_eq!(map.get(1), None);
assert_eq!(map.get(2), None);
}