use crate::{
index::{
Cursor as CursorTrait, Unordered,
storage::{Cursor as CursorImpl, IndexEntry, Overflow, Values, push_displaced},
},
translator::Translator,
};
use commonware_runtime::{
Metrics,
telemetry::metrics::{Counter, Gauge, MetricsExt as _},
};
use std::collections::{
HashMap,
hash_map::{Entry, OccupiedEntry, VacantEntry},
};
impl<K: Send + Sync, V: Send + Sync> IndexEntry<V> for OccupiedEntry<'_, K, V> {
type Key = K;
fn key(&self) -> &K {
OccupiedEntry::key(self)
}
fn get_mut(&mut self) -> &mut V {
self.get_mut()
}
fn remove(self) {
OccupiedEntry::remove(self);
}
}
pub type Cursor<'a, K, V, S> = CursorImpl<'a, K, V, OccupiedEntry<'a, K, V>, S>;
pub struct Index<T: Translator, V: Send + Sync> {
translator: T,
map: HashMap<T::Key, V, T>,
overflow: Overflow<T::Key, V, T>,
keys: Gauge,
items: Gauge,
pruned: Counter,
}
impl<T: Translator, V: Send + Sync> Index<T, V> {
fn create(keys: &Gauge, items: &Gauge, vacant: VacantEntry<'_, T::Key, V>, v: V) {
keys.inc();
items.inc();
vacant.insert(v);
}
pub fn new(ctx: impl Metrics, translator: T) -> Self {
Self {
translator: translator.clone(),
overflow: HashMap::with_hasher(translator.clone()),
map: HashMap::with_hasher(translator),
keys: ctx.gauge("keys", "Number of translated keys in the index"),
items: ctx.gauge("items", "Number of items in the index"),
pruned: ctx.counter("pruned", "Number of items pruned"),
}
}
#[commonware_macros::stability(ALPHA)]
pub(crate) fn empty(&self) -> Self {
Self {
translator: self.translator.clone(),
overflow: HashMap::with_hasher(self.translator.clone()),
map: HashMap::with_hasher(self.translator.clone()),
keys: self.keys.clone(),
items: self.items.clone(),
pruned: self.pruned.clone(),
}
}
#[commonware_macros::stability(ALPHA)]
pub(crate) fn absorb(&mut self, other: Self) {
assert!(
self.map.is_empty() && self.overflow.is_empty(),
"absorb target must be empty"
);
self.map = other.map;
self.overflow = other.overflow;
}
#[commonware_macros::stability(ALPHA)]
pub(crate) fn for_each_value(&self, mut f: impl FnMut(&V)) {
for v in self.map.values() {
f(v);
}
for chain in self.overflow.values() {
for v in chain {
f(v);
}
}
}
}
impl<T: Translator, V: Send + Sync> super::Factory for Index<T, V> {
type Translator = T;
fn new(ctx: impl commonware_runtime::Metrics, translator: T) -> Self {
Self::new(ctx, translator)
}
}
impl<T: Translator, V: Send + Sync> Unordered for Index<T, V> {
type Value = V;
type Cursor<'a>
= Cursor<'a, T::Key, V, T>
where
Self: 'a;
fn get<'a>(&'a self, key: &[u8]) -> impl Iterator<Item = &'a V> + 'a
where
V: 'a,
{
let k = self.translator.transform(key);
Values::new(self.map.get(&k), &self.overflow, k)
}
fn get_mut<'a>(&'a mut self, key: &[u8]) -> Option<Self::Cursor<'a>> {
let k = self.translator.transform(key);
match self.map.entry(k) {
Entry::Occupied(entry) => Some(Cursor::<'_, T::Key, V, T>::new(
entry,
&mut self.overflow,
&self.keys,
&self.items,
&self.pruned,
)),
Entry::Vacant(_) => None,
}
}
fn get_mut_or_insert<'a>(&'a mut self, key: &[u8], value: V) -> Option<Self::Cursor<'a>> {
let k = self.translator.transform(key);
match self.map.entry(k) {
Entry::Occupied(entry) => Some(Cursor::<'_, T::Key, V, T>::new(
entry,
&mut self.overflow,
&self.keys,
&self.items,
&self.pruned,
)),
Entry::Vacant(entry) => {
Self::create(&self.keys, &self.items, entry, value);
None
}
}
}
fn insert(&mut self, key: &[u8], v: V) {
let k = self.translator.transform(key);
match self.map.entry(k) {
Entry::Occupied(mut entry) => {
let old = std::mem::replace(entry.get_mut(), v);
push_displaced(&mut self.overflow, k, old);
self.items.inc();
}
Entry::Vacant(entry) => {
Self::create(&self.keys, &self.items, entry, v);
}
}
}
fn insert_and_retain(&mut self, key: &[u8], value: V, should_retain: impl Fn(&V) -> bool) {
let k = self.translator.transform(key);
match self.map.entry(k) {
Entry::Occupied(mut entry) => {
#[allow(clippy::map_entry)]
if !self.overflow.contains_key(&k) {
match (should_retain(entry.get()), should_retain(&value)) {
(true, true) => {
self.overflow.insert(k, vec![value]);
self.items.inc();
}
(false, true) => {
*entry.get_mut() = value;
self.pruned.inc();
}
(false, false) => {
entry.remove();
self.keys.dec();
self.items.dec();
self.pruned.inc();
}
(true, false) => {}
}
return;
}
let mut cursor = Cursor::<'_, T::Key, V, T>::new(
entry,
&mut self.overflow,
&self.keys,
&self.items,
&self.pruned,
);
cursor.retain(&should_retain);
if should_retain(&value) {
cursor.insert(value);
}
}
Entry::Vacant(entry) => {
if should_retain(&value) {
Self::create(&self.keys, &self.items, entry, value);
}
}
}
}
fn remove(&mut self, key: &[u8]) {
let k = self.translator.transform(key);
if self.map.remove(&k).is_some() {
self.keys.dec();
self.items.dec();
self.pruned.inc();
if !self.overflow.is_empty()
&& let Some(chain) = self.overflow.remove(&k)
{
self.items.dec_by(chain.len() as i64);
self.pruned.inc_by(chain.len() as u64);
}
}
}
#[cfg(test)]
fn keys(&self) -> usize {
self.keys.get() as usize
}
#[cfg(test)]
fn items(&self) -> usize {
self.items.get() as usize
}
#[cfg(test)]
fn pruned(&self) -> usize {
self.pruned.get() as usize
}
}