use std::collections::HashMap;
use std::hash::Hash;
#[derive(Debug, Clone)]
pub(crate) struct ColumnDictionary<T: Clone + Eq + Hash> {
values: Vec<Option<T>>,
ids: HashMap<T, u32>,
refcounts: Vec<u32>,
free_ids: Vec<u32>,
}
impl<T: Clone + Eq + Hash> Default for ColumnDictionary<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Clone + Eq + Hash> ColumnDictionary<T> {
pub(crate) fn new() -> Self {
Self {
values: Vec::new(),
ids: HashMap::new(),
refcounts: Vec::new(),
free_ids: Vec::new(),
}
}
pub(crate) fn intern(&mut self, value: &T) -> u32 {
if let Some(&id) = self.ids.get(value) {
return id;
}
let id = if let Some(reused) = self.free_ids.pop() {
let idx = reused as usize;
self.values[idx] = Some(value.clone());
self.refcounts[idx] = 0;
reused
} else {
let id = self.values.len() as u32;
self.values.push(Some(value.clone()));
self.refcounts.push(0);
id
};
self.ids.insert(value.clone(), id);
id
}
pub(crate) fn get_id(&self, value: &T) -> Option<u32> {
self.ids.get(value).copied()
}
pub(crate) fn resolve(&self, id: u32) -> Option<&T> {
self.values.get(id as usize).and_then(Option::as_ref)
}
pub(crate) fn retain(&mut self, id: u32) {
debug_assert!(
self.resolve(id).is_some(),
"retain of an id with no live value"
);
if let Some(rc) = self.refcounts.get_mut(id as usize) {
*rc += 1;
}
}
pub(crate) fn release(&mut self, id: u32) {
let idx = id as usize;
let rc = match self.refcounts.get_mut(idx) {
Some(rc) => rc,
None => return,
};
debug_assert!(*rc > 0, "release of an id with no outstanding reference");
if *rc == 0 {
return;
}
*rc -= 1;
if *rc == 0 {
if let Some(value) = self.values[idx].take() {
self.ids.remove(&value);
}
self.free_ids.push(id);
}
}
}
impl<T: Clone + Eq + Hash + std::fmt::Display> ColumnDictionary<T> {
pub(crate) fn size_estimate(&self) -> usize {
use std::mem::size_of;
let structural = self.values.capacity() * size_of::<Option<T>>()
+ self.ids.capacity() * (size_of::<T>() + size_of::<u32>())
+ self.refcounts.capacity() * size_of::<u32>()
+ self.free_ids.capacity() * size_of::<u32>();
let string_bytes: usize = self
.values
.iter()
.filter_map(Option::as_ref)
.map(|v| v.to_string().len())
.sum();
structural + string_bytes
}
}
#[cfg(test)]
impl<T: Clone + Eq + Hash> ColumnDictionary<T> {
pub(crate) fn slot_count(&self) -> usize {
self.values.len()
}
pub(crate) fn live_count(&self) -> usize {
self.ids.len()
}
pub(crate) fn free_count(&self) -> usize {
self.free_ids.len()
}
}