use std::cmp::Ordering;
use std::collections::btree_map::Entry;
use std::collections::btree_map::VacantEntry;
use std::collections::BTreeMap;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use std::ops::Bound;
use gaps::RangeGappable as _;
use crate::ser::id::Id as SerId;
use crate::ser::ToSerde;
#[derive(Debug)]
#[repr(transparent)]
pub struct Id<T> {
id: NonZeroUsize,
_phantom: PhantomData<T>,
}
impl<T> Display for Id<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Display::fmt(&self.id, f)
}
}
impl<T> Id<T> {
pub fn from_unique_id(id: NonZeroUsize) -> Self {
Self {
id,
_phantom: PhantomData,
}
}
pub fn get(&self) -> NonZeroUsize {
self.id
}
}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
Self {
id: self.id,
_phantom: PhantomData,
}
}
}
impl<T> Copy for Id<T> {}
impl<T> PartialOrd<Self> for Id<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.id.partial_cmp(&other.id)
}
}
impl<T> Ord for Id<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
impl<T> PartialEq<Self> for Id<T> {
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id)
}
}
impl<T> Eq for Id<T> {}
impl<T> Hash for Id<T> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.id.hash(state)
}
}
impl<T> ToSerde for Id<T> {
type Output = SerId<T>;
fn to_serde(&self) -> Self::Output {
SerId::new(self.get())
}
}
pub trait AllocId<T> {
type Id;
type Entry<'e>
where
Self: 'e;
fn allocate_id(&mut self) -> (Self::Id, Self::Entry<'_>);
fn try_reserve_id(&mut self, id: usize) -> Option<(Self::Id, Self::Entry<'_>)>;
fn reserve_id(&mut self, id: usize) -> (Self::Id, Self::Entry<'_>) {
self
.try_reserve_id(id)
.unwrap_or_else(|| panic!("ID {id} is already in use"))
}
fn free_id(&mut self, id: Self::Id);
}
impl<T, V> AllocId<T> for BTreeMap<usize, V> {
type Id = Id<T>;
type Entry<'e> = VacantEntry<'e, usize, V>
where
V: 'e;
fn allocate_id(&mut self) -> (Self::Id, Self::Entry<'_>) {
let mut gaps = self.gaps(1..=usize::MAX);
let gap = gaps.next().expect("available ID space is exhausted");
let id = match gap {
(Bound::Included(lower), _) => lower,
(Bound::Excluded(lower), _) => lower + 1,
(Bound::Unbounded, _) => {
unreachable!()
},
};
match self.entry(id) {
Entry::Vacant(vacancy) => {
let id = NonZeroUsize::new(id).unwrap();
(Id::from_unique_id(id), vacancy)
},
Entry::Occupied(_) => panic!("ID {id} already present"),
}
}
fn try_reserve_id(&mut self, id: usize) -> Option<(Self::Id, Self::Entry<'_>)> {
let id = NonZeroUsize::new(id)?;
match self.entry(id.get()) {
Entry::Vacant(vacancy) => Some((Id::from_unique_id(id), vacancy)),
Entry::Occupied(_) => None,
}
}
fn reserve_id(&mut self, id: usize) -> (Self::Id, Self::Entry<'_>) {
self
.try_reserve_id(id)
.unwrap_or_else(|| panic!("ID {id} is already in use"))
}
fn free_id(&mut self, id: Self::Id) {
let _removed = self.remove(&id.get().get());
debug_assert!(_removed.is_some());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allocate_and_free_id_btreemap() {
let mut map = BTreeMap::<usize, &'static str>::new();
assert_eq!(map.get(&1), None);
let (id1, entry) = AllocId::<()>::allocate_id(&mut map);
let _value_ref = entry.insert("foobar");
assert_eq!(id1.get().get(), 1);
assert_eq!(map.get(&1), Some(&"foobar"));
assert_eq!(map.get(&2), None);
let (id2, entry) = AllocId::<()>::allocate_id(&mut map);
let _value_ref = entry.insert("alloc'd");
assert_eq!(id2.get().get(), 2);
assert_eq!(map.get(&2), Some(&"alloc'd"));
let () = AllocId::<()>::free_id(&mut map, id1);
assert_eq!(map.get(&1), None);
let () = map.free_id(id2);
assert_eq!(map.get(&2), None);
assert!(map.is_empty());
}
#[test]
fn reserve_free_id_btreemap() {
let mut map = BTreeMap::<usize, &'static str>::new();
let (_id1, _entry) = AllocId::<()>::reserve_id(&mut map, 1);
let (id1, entry) = AllocId::<()>::reserve_id(&mut map, 1);
let _value_ref = entry.insert("foo");
assert_eq!(id1.get().get(), 1);
assert_eq!(map.get(&1), Some(&"foo"));
assert!(AllocId::<()>::try_reserve_id(&mut map, 1).is_none());
let () = AllocId::<()>::free_id(&mut map, id1);
assert_eq!(map.get(&1), None);
assert!(map.is_empty());
let (id1, entry) = AllocId::<()>::try_reserve_id(&mut map, 1).unwrap();
let _value_ref = entry.insert("success");
assert_eq!(id1.get().get(), 1);
assert_eq!(map.get(&1), Some(&"success"));
let () = AllocId::<()>::free_id(&mut map, id1);
assert_eq!(map.get(&1), None);
assert!(map.is_empty());
}
}