use std::{ borrow::{Borrow, ToOwned}, collections::HashMap, hash::Hash, }; use crate::Index; /// Maps user keys to internal `Index` handles. /// /// Renamed from the former `IndexMap` to avoid colliding with the `indexmap` /// crate. Power users can promote `&K` to `Index` via `get_or_create` and /// skip the lookup on subsequent hot-path calls. #[derive(Debug)] pub struct KeyTable { forward: HashMap, /// Reverse mapping, indexed by `Index.0`. /// /// Indices are handed out densely and sequentially, so position *is* the /// index and `key()` is a lookup rather than a scan over every entry. reverse: Vec, } impl KeyTable where K: Eq + Hash + Clone, { #[must_use] pub fn new() -> Self { Self { forward: HashMap::new(), reverse: Vec::new(), } } pub fn get(&self, k: &Q) -> Option where K: Borrow, { self.forward.get(k).cloned() } pub fn get_or_create>(&mut self, k: &Q) -> Index where K: Borrow, { if let Some(idx) = self.forward.get(k) { *idx } else { let idx = Index::from(self.reverse.len()); let owned = k.to_owned(); self.reverse.push(owned.clone()); self.forward.insert(owned, idx); idx } } #[must_use] pub fn key(&self, idx: Index) -> Option<&K> { self.reverse.get(idx.0) } pub fn keys(&self) -> impl Iterator { self.forward.keys() } #[must_use] pub fn len(&self) -> usize { self.reverse.len() } #[must_use] pub fn is_empty(&self) -> bool { self.reverse.is_empty() } } impl Default for KeyTable where K: Eq + Hash + Clone, { fn default() -> Self { KeyTable::new() } }