use alloc::vec::Vec;
use vize_l0::FxHashMap;
use crate::id::NodeId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SideTable<T> {
entries: FxHashMap<NodeId, T>,
}
impl<T> Default for SideTable<T> {
fn default() -> Self {
Self {
entries: FxHashMap::default(),
}
}
}
impl<T> SideTable<T> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
entries: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
}
}
pub fn insert(&mut self, id: NodeId, value: T) -> Option<T> {
self.entries.insert(id, value)
}
#[must_use]
pub fn get(&self, id: NodeId) -> Option<&T> {
self.entries.get(&id)
}
pub fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
self.entries.get_mut(&id)
}
pub fn remove(&mut self, id: NodeId) -> Option<T> {
self.entries.remove(&id)
}
#[must_use]
pub fn contains_id(&self, id: NodeId) -> bool {
self.entries.contains_key(&id)
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (NodeId, &T)> {
self.entries.iter().map(|(id, value)| (*id, value))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut T)> {
self.entries.iter_mut().map(|(id, value)| (*id, value))
}
#[must_use]
pub fn sorted_entries(&self) -> Vec<(NodeId, &T)> {
let mut entries: Vec<(NodeId, &T)> = self.iter().collect();
entries.sort_unstable_by_key(|(id, _)| *id);
entries
}
}
impl<T> FromIterator<(NodeId, T)> for SideTable<T> {
fn from_iter<I: IntoIterator<Item = (NodeId, T)>>(iter: I) -> Self {
Self {
entries: iter.into_iter().collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::SideTable;
use crate::id::NodeId;
use alloc::vec;
use alloc::vec::Vec;
fn id(index: u32) -> NodeId {
NodeId::from_index(index).expect("test indices are far below u32::MAX")
}
#[test]
fn a_fresh_table_holds_nothing() {
let table: SideTable<u8> = SideTable::new();
assert_eq!(table.len(), 0);
assert!(table.is_empty());
assert_eq!(table.get(id(0)), None);
assert!(!table.contains_id(id(0)));
}
#[test]
fn insert_attaches_and_replaces() {
let mut table = SideTable::new();
assert_eq!(table.insert(id(3), "first"), None);
assert_eq!(table.get(id(3)), Some(&"first"));
assert_eq!(table.insert(id(3), "second"), Some("first"));
assert_eq!(table.get(id(3)), Some(&"second"));
assert_eq!(table.len(), 1);
}
#[test]
fn remove_detaches_exactly_one_fact() {
let mut table = SideTable::new();
table.insert(id(1), 10);
table.insert(id(2), 20);
assert_eq!(table.remove(id(1)), Some(10));
assert_eq!(table.remove(id(1)), None);
assert_eq!(table.get(id(2)), Some(&20));
assert_eq!(table.len(), 1);
}
#[test]
fn get_mut_edits_in_place() {
let mut table = SideTable::new();
table.insert(id(0), 1u32);
*table.get_mut(id(0)).expect("the fact was just inserted") += 41;
assert_eq!(table.get(id(0)), Some(&42));
}
#[test]
fn clear_drops_every_fact() {
let mut table = SideTable::new();
table.insert(id(0), 1);
table.insert(id(9), 2);
table.clear();
assert!(table.is_empty());
assert_eq!(table.get(id(0)), None);
}
#[test]
fn sorted_entries_orders_by_id_whatever_the_insertion_order() {
let mut table = SideTable::new();
for index in [7u32, 0, 3, 12, 1] {
table.insert(id(index), index);
}
let sorted: Vec<(u32, u32)> = table
.sorted_entries()
.into_iter()
.map(|(node, value)| (node.index(), *value))
.collect();
assert_eq!(sorted, vec![(0, 0), (1, 1), (3, 3), (7, 7), (12, 12)]);
}
#[test]
fn iter_visits_every_fact_exactly_once() {
let mut table = SideTable::new();
for index in 0..8u32 {
table.insert(id(index), index);
}
let mut seen: Vec<u32> = table.iter().map(|(node, _)| node.index()).collect();
seen.sort_unstable();
assert_eq!(seen, (0..8).collect::<Vec<u32>>());
}
#[test]
fn from_iter_builds_the_same_table_as_repeated_insert() {
let built: SideTable<u32> = [(id(0), 0u32), (id(4), 4)].into_iter().collect();
let mut inserted = SideTable::new();
inserted.insert(id(0), 0);
inserted.insert(id(4), 4);
assert_eq!(built, inserted);
}
}