mod cursor;
mod partition;
pub use self::cursor::Cursor;
use self::partition::Partition;
use crate::{
index::{
partitioned::partition_index_and_sub_key, Cursor as CursorTrait, Factory, Ordered,
Unordered,
},
translator::Translator,
};
use commonware_runtime::{
telemetry::metrics::{Counter, Gauge, MetricsExt as _},
Metrics,
};
use std::{
collections::{btree_map, hash_map, BTreeMap, HashMap},
ops::Bound,
};
const SPILL_THRESHOLD: usize = 512;
pub struct Index<T: Translator, V: Send + Sync, const P: usize> {
translator: T,
partitions: Box<[Partition<T::Key, V>]>,
spilled: HashMap<usize, BTreeMap<T::Key, Vec<V>>>,
threshold: usize,
keys: Gauge,
items: Gauge,
pruned: Counter,
}
impl<T: Translator, V: Send + Sync, const P: usize> Index<T, V, P> {
pub fn new(ctx: impl Metrics, translator: T) -> Self {
const {
assert!(P > 0 && P <= 3, "P must be in 1..=3");
}
let count = 1usize << (P * 8);
let partitions = (0..count)
.map(|_| Partition::default())
.collect::<Vec<_>>()
.into_boxed_slice();
Self {
translator,
partitions,
spilled: HashMap::new(),
threshold: SPILL_THRESHOLD,
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"),
}
}
#[cfg(test)]
pub(crate) fn with_threshold(ctx: impl Metrics, translator: T, threshold: usize) -> Self {
assert!(threshold > 0, "spill threshold must be at least 1");
let mut index = Self::new(ctx, translator);
index.threshold = threshold;
index
}
fn maybe_spill(&mut self, i: usize) {
if self.partitions[i].len() < self.threshold {
return;
}
let inner: BTreeMap<T::Key, Vec<V>> = self.partitions[i].drain_runs().into_iter().collect();
self.spilled.insert(i, inner);
}
fn spilled_partition(&self, i: usize) -> Option<&BTreeMap<T::Key, Vec<V>>> {
if self.spilled.is_empty() {
return None;
}
self.spilled.get(&i)
}
fn partition_values(&self, i: usize, k: &T::Key) -> &[V] {
if !self.partitions[i].is_empty() {
return self.partitions[i].values(k);
}
self.spilled_partition(i)
.and_then(|inner| inner.get(k))
.map_or(&[], Vec::as_slice)
}
fn partition_first(&self, i: usize) -> Option<&[V]> {
self.partitions[i].first_values().or_else(|| {
self.spilled_partition(i)?
.first_key_value()
.map(|(_, v)| v.as_slice())
})
}
fn partition_last(&self, i: usize) -> Option<&[V]> {
self.partitions[i].last_values().or_else(|| {
self.spilled_partition(i)?
.last_key_value()
.map(|(_, v)| v.as_slice())
})
}
fn is_empty(&self) -> bool {
self.keys.get() == 0
}
fn partition_next_after(&self, i: usize, k: &T::Key) -> Option<&[V]> {
self.partitions[i].next_values_after(k).or_else(|| {
self.spilled_partition(i)?
.range((Bound::Excluded(*k), Bound::Unbounded))
.next()
.map(|(_, v)| v.as_slice())
})
}
fn partition_prev_before(&self, i: usize, k: &T::Key) -> Option<&[V]> {
self.partitions[i].prev_values_before(k).or_else(|| {
self.spilled_partition(i)?
.range((Bound::Unbounded, Bound::Excluded(*k)))
.next_back()
.map(|(_, v)| v.as_slice())
})
}
#[cfg(test)]
pub(crate) fn spilled_count(&self) -> usize {
self.spilled.len()
}
}
impl<T: Translator, V: Send + Sync, const P: usize> Factory<T> for Index<T, V, P> {
fn new(ctx: impl Metrics, translator: T) -> Self {
Self::new(ctx, translator)
}
}
impl<T: Translator, V: Send + Sync, const P: usize> Unordered for Index<T, V, P> {
type Value = V;
type Cursor<'a>
= Cursor<'a, T::Key, V>
where
Self: 'a;
fn get<'a>(&'a self, key: &[u8]) -> impl Iterator<Item = &'a V> + Send + 'a
where
V: 'a,
{
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
self.partition_values(i, &k).iter()
}
fn get_many<'a, K: AsRef<[u8]>>(&'a self, keys: &[K], mut visit: impl FnMut(usize, &'a V))
where
V: 'a,
{
let mut order: Vec<(usize, T::Key, usize)> = keys
.iter()
.enumerate()
.map(|(key_idx, key)| {
let (partition, sub) = partition_index_and_sub_key::<P>(key.as_ref());
(partition, self.translator.transform(sub), key_idx)
})
.collect();
order.sort_unstable();
for (partition, translated, key_idx) in order {
for value in self.partition_values(partition, &translated) {
visit(key_idx, value);
}
}
}
fn get_mut<'a>(&'a mut self, key: &[u8]) -> Option<Self::Cursor<'a>> {
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
self.maybe_spill(i);
if !self.partitions[i].is_empty() {
let run = self.partitions[i].run_range(&k);
if run.is_empty() {
return None;
}
return Some(Cursor::soa(
&mut self.partitions[i],
k,
run,
&self.keys,
&self.items,
&self.pruned,
));
}
if self
.spilled_partition(i)
.is_some_and(|inner| inner.contains_key(&k))
{
return Some(Cursor::spilled(
&mut self.spilled,
i,
k,
&self.keys,
&self.items,
&self.pruned,
));
}
None
}
fn get_mut_or_insert<'a>(
&'a mut self,
key: &[u8],
value: Self::Value,
) -> Option<Self::Cursor<'a>> {
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
self.maybe_spill(i);
if !self.partitions[i].is_empty() {
let run = self.partitions[i].run_range(&k);
if !run.is_empty() {
return Some(Cursor::soa(
&mut self.partitions[i],
k,
run,
&self.keys,
&self.items,
&self.pruned,
));
}
self.partitions[i].insert_at(run.end, k, value);
self.keys.inc();
self.items.inc();
self.maybe_spill(i);
return None;
}
if let Some(inner) = self.spilled_partition(i) {
if inner.contains_key(&k) {
return Some(Cursor::spilled(
&mut self.spilled,
i,
k,
&self.keys,
&self.items,
&self.pruned,
));
}
self.spilled.get_mut(&i).unwrap().insert(k, vec![value]);
self.keys.inc();
self.items.inc();
return None;
}
self.partitions[i].insert_at(0, k, value);
self.keys.inc();
self.items.inc();
self.maybe_spill(i);
None
}
fn insert(&mut self, key: &[u8], value: Self::Value) {
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
self.maybe_spill(i);
if !self.partitions[i].is_empty() {
let run = self.partitions[i].run_range(&k);
let new_key = run.is_empty();
self.partitions[i].insert_at(run.end, k, value);
self.items.inc();
if new_key {
self.keys.inc();
}
self.maybe_spill(i);
return;
}
if !self.spilled.is_empty() {
if let hash_map::Entry::Occupied(mut partition) = self.spilled.entry(i) {
match partition.get_mut().entry(k) {
btree_map::Entry::Occupied(mut run) => run.get_mut().push(value),
btree_map::Entry::Vacant(run) => {
run.insert(vec![value]);
self.keys.inc();
}
}
self.items.inc();
return;
}
}
self.partitions[i].insert_at(0, k, value);
self.items.inc();
self.keys.inc();
self.maybe_spill(i);
}
fn insert_and_retain(
&mut self,
key: &[u8],
value: Self::Value,
should_retain: impl Fn(&Self::Value) -> bool,
) {
let (i, _) = partition_index_and_sub_key::<P>(key);
if let Some(mut cursor) = self.get_mut(key) {
cursor.retain(&should_retain);
if should_retain(&value) {
cursor.insert(value);
}
} else if should_retain(&value) {
self.insert(key, value);
}
self.maybe_spill(i);
}
fn remove(&mut self, key: &[u8]) {
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
self.maybe_spill(i);
if !self.partitions[i].is_empty() {
let run = self.partitions[i].run_range(&k);
if run.is_empty() {
return;
}
let n = run.len();
self.partitions[i].remove_run(run);
self.keys.dec();
self.items.dec_by(n as i64);
self.pruned.inc_by(n as u64);
return;
}
if !self.spilled.is_empty() {
if let hash_map::Entry::Occupied(mut partition) = self.spilled.entry(i) {
if let Some(vals) = partition.get_mut().remove(&k) {
let n = vals.len();
self.keys.dec();
self.items.dec_by(n as i64);
self.pruned.inc_by(n as u64);
if partition.get().is_empty() {
partition.remove();
}
}
}
}
}
#[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
}
}
impl<T: Translator, V: Send + Sync, const P: usize> Ordered for Index<T, V, P> {
fn prev_translated_key<'a>(
&'a self,
key: &[u8],
) -> Option<(impl Iterator<Item = &'a V> + Send + 'a, bool)>
where
V: 'a,
{
if self.is_empty() {
return None;
}
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
if let Some(vals) = self.partition_prev_before(i, &k) {
return Some((vals.iter(), false));
}
for p in (0..i).rev() {
if let Some(vals) = self.partition_last(p) {
return Some((vals.iter(), false));
}
}
for p in (0..self.partitions.len()).rev() {
if let Some(vals) = self.partition_last(p) {
return Some((vals.iter(), true));
}
}
None
}
fn next_translated_key<'a>(
&'a self,
key: &[u8],
) -> Option<(impl Iterator<Item = &'a V> + Send + 'a, bool)>
where
V: 'a,
{
if self.is_empty() {
return None;
}
let (i, sub) = partition_index_and_sub_key::<P>(key);
let k = self.translator.transform(sub);
if let Some(vals) = self.partition_next_after(i, &k) {
return Some((vals.iter(), false));
}
for p in i + 1..self.partitions.len() {
if let Some(vals) = self.partition_first(p) {
return Some((vals.iter(), false));
}
}
for p in 0..self.partitions.len() {
if let Some(vals) = self.partition_first(p) {
return Some((vals.iter(), true));
}
}
None
}
fn first_translated_key<'a>(&'a self) -> Option<impl Iterator<Item = &'a V> + Send + 'a>
where
V: 'a,
{
if self.is_empty() {
return None;
}
for p in 0..self.partitions.len() {
if let Some(vals) = self.partition_first(p) {
return Some(vals.iter());
}
}
None
}
fn last_translated_key<'a>(&'a self) -> Option<impl Iterator<Item = &'a V> + Send + 'a>
where
V: 'a,
{
if self.is_empty() {
return None;
}
for p in (0..self.partitions.len()).rev() {
if let Some(vals) = self.partition_last(p) {
return Some(vals.iter());
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::translator::OneCap;
use commonware_formatting::hex;
use commonware_macros::test_traced;
use commonware_runtime::{deterministic, Runner};
fn new_index(context: deterministic::Context) -> Index<OneCap, u64, 1> {
Index::new(context, OneCap)
}
fn new_index_spilling(context: deterministic::Context) -> Index<OneCap, u64, 1> {
Index::with_threshold(context, OneCap, 2)
}
#[test_traced]
fn test_empty_and_sparse_nav() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
assert!(index.first_translated_key().is_none());
assert!(index.last_translated_key().is_none());
assert!(index.prev_translated_key(&[0x80, 0x00]).is_none());
assert!(index.next_translated_key(&[0x80, 0x00]).is_none());
index.insert(&[0x05, 0x01], 1);
index.insert(&[0xF0, 0x02], 2);
assert_eq!(index.keys(), 2);
assert_eq!(
index
.first_translated_key()
.unwrap()
.copied()
.collect::<Vec<_>>(),
vec![1]
);
assert_eq!(
index
.last_translated_key()
.unwrap()
.copied()
.collect::<Vec<_>>(),
vec![2]
);
let (it, wrapped) = index.next_translated_key(&[0x05, 0x01]).unwrap();
assert_eq!((it.copied().collect::<Vec<_>>(), wrapped), (vec![2], false));
let (it, wrapped) = index.next_translated_key(&[0xF0, 0x02]).unwrap();
assert_eq!((it.copied().collect::<Vec<_>>(), wrapped), (vec![1], true));
let (it, wrapped) = index.prev_translated_key(&[0xF0, 0x02]).unwrap();
assert_eq!((it.copied().collect::<Vec<_>>(), wrapped), (vec![1], false));
let (it, wrapped) = index.prev_translated_key(&[0x05, 0x01]).unwrap();
assert_eq!((it.copied().collect::<Vec<_>>(), wrapped), (vec![2], true));
let (it, wrapped) = index.prev_translated_key(&[0x80, 0x00]).unwrap();
assert_eq!((it.copied().collect::<Vec<_>>(), wrapped), (vec![1], false));
let (it, wrapped) = index.next_translated_key(&[0x80, 0x00]).unwrap();
assert_eq!((it.copied().collect::<Vec<_>>(), wrapped), (vec![2], false));
index.remove(&[0x05, 0x01]);
index.remove(&[0xF0, 0x02]);
assert_eq!(index.keys(), 0);
assert!(index.prev_translated_key(&[0x80, 0x00]).is_none());
assert!(index.next_translated_key(&[0x80, 0x00]).is_none());
});
}
#[test_traced]
fn test_spill_transition() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
index.insert(&[0x10, 0x01], 1);
assert_eq!(index.spilled_count(), 0);
index.insert(&[0x10, 0x02], 2); assert_eq!(index.spilled_count(), 1);
index.insert(&[0x10, 0x03], 3); assert_eq!(index.spilled_count(), 1);
assert_eq!(index.keys(), 3);
assert_eq!(index.items(), 3);
assert_eq!(
index.get(&[0x10, 0x01]).copied().collect::<Vec<_>>(),
vec![1]
);
index.insert(&[0x10, 0x02], 22);
assert_eq!(
index.get(&[0x10, 0x02]).copied().collect::<Vec<_>>(),
vec![2, 22]
);
assert_eq!(index.items(), 4);
index.insert(&[0x20, 0x05], 5);
assert_eq!(index.spilled_count(), 1);
assert_eq!(
index.get(&[0x20, 0x05]).copied().collect::<Vec<_>>(),
vec![5]
);
});
}
#[test_traced]
fn test_spill_after_cursor_growth() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
let key = [0x10, 0x01];
index.insert(&key, 1);
{
let mut cursor = index.get_mut(&key).unwrap();
assert_eq!(cursor.next().copied(), Some(1));
assert_eq!(cursor.next(), None);
cursor.insert(2);
}
assert_eq!(index.spilled_count(), 0);
index.insert(&key, 3);
assert_eq!(index.spilled_count(), 1);
assert_eq!(index.get(&key).copied().collect::<Vec<_>>(), vec![1, 2, 3]);
let other = [0x20, 0x01];
index.insert(&other, 4);
index.insert_and_retain(&other, 5, |_| true);
assert_eq!(index.spilled_count(), 2);
assert_eq!(index.get(&other).copied().collect::<Vec<_>>(), vec![4, 5]);
let third = [0x30, 0x01];
index.insert(&third, 6);
{
let mut cursor = index.get_mut(&third).unwrap();
assert_eq!(cursor.next().copied(), Some(6));
assert_eq!(cursor.next(), None);
cursor.insert(7);
}
assert_eq!(index.spilled_count(), 2);
{
let mut cursor = index.get_mut(&third).unwrap();
assert_eq!(cursor.next().copied(), Some(6));
assert_eq!(cursor.next().copied(), Some(7));
assert_eq!(cursor.next(), None);
}
assert_eq!(index.spilled_count(), 3);
assert_eq!(index.get(&third).copied().collect::<Vec<_>>(), vec![6, 7]);
let fourth = [0x40, 0x01];
index.insert(&fourth, 8);
{
let mut cursor = index.get_mut(&fourth).unwrap();
assert_eq!(cursor.next().copied(), Some(8));
assert_eq!(cursor.next(), None);
cursor.insert(9);
}
assert_eq!(index.spilled_count(), 3);
index.remove(&[0x40, 0x02]);
assert_eq!(index.spilled_count(), 4);
assert_eq!(index.get(&fourth).copied().collect::<Vec<_>>(), vec![8, 9]);
});
}
#[test_traced]
fn test_spill_after_get_mut_or_insert_cursor_growth() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
let key = [0x10, 0x01];
index.insert(&key, 1);
{
let mut cursor = index.get_mut_or_insert(&key, 2).unwrap();
assert_eq!(cursor.next().copied(), Some(1));
assert_eq!(cursor.next(), None);
cursor.insert(2);
}
assert_eq!(index.spilled_count(), 0);
assert!(index.get_mut_or_insert(&key, 3).is_some());
assert_eq!(index.spilled_count(), 1);
assert_eq!(index.get(&key).copied().collect::<Vec<_>>(), vec![1, 2]);
});
}
#[test_traced]
fn test_spill_nav() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
index.insert(&[0x10, 0x01], 1);
index.insert(&[0x10, 0x02], 2);
index.insert(&[0x20, 0x05], 5);
index.insert(&[0x30, 0x07], 7);
index.insert(&[0x30, 0x08], 8);
assert_eq!(index.spilled_count(), 2);
assert_eq!(index.first_translated_key().unwrap().next(), Some(&1));
assert_eq!(index.last_translated_key().unwrap().next(), Some(&8));
let (mut it, wrapped) = index.next_translated_key(&[0x10, 0x01]).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&2));
let (mut it, wrapped) = index.next_translated_key(&[0x10, 0x02]).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&5));
let (mut it, wrapped) = index.next_translated_key(&[0x20, 0x05]).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&7));
let (mut it, wrapped) = index.prev_translated_key(&[0x30, 0x07]).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&5));
let (mut it, wrapped) = index.prev_translated_key(&[0x20, 0x05]).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&2));
let (mut it, wrapped) = index.next_translated_key(&[0x30, 0x08]).unwrap();
assert!(wrapped);
assert_eq!(it.next(), Some(&1));
});
}
#[test_traced]
fn test_spill_despill_on_full_drain() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
index.insert(&[0x10, 0x01], 1);
index.insert(&[0x10, 0x02], 2); assert_eq!(index.spilled_count(), 1);
index.remove(&[0x10, 0x01]);
assert_eq!(index.spilled_count(), 1); index.remove(&[0x10, 0x02]);
assert_eq!(index.spilled_count(), 0); assert_eq!(index.keys(), 0);
index.insert(&[0x10, 0x09], 9);
assert_eq!(index.spilled_count(), 0);
assert_eq!(
index.get(&[0x10, 0x09]).copied().collect::<Vec<_>>(),
vec![9]
);
});
}
#[test_traced]
fn test_spill_full_lifecycle() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
assert_eq!(index.spilled_count(), 0);
assert_eq!(index.keys(), 0);
assert_eq!(index.items(), 0);
index.insert(&[0x10, 0x01], 1);
assert_eq!(index.spilled_count(), 0);
index.insert(&[0x10, 0x02], 2);
assert_eq!(index.spilled_count(), 1);
assert_eq!(index.keys(), 2);
assert_eq!(index.items(), 2);
{
let mut cursor = index.get_mut(&[0x10, 0x01]).unwrap();
assert_eq!(cursor.next().copied(), Some(1));
cursor.delete();
}
assert_eq!(index.spilled_count(), 1); {
let mut cursor = index.get_mut(&[0x10, 0x02]).unwrap();
assert_eq!(cursor.next().copied(), Some(2));
cursor.delete();
}
assert_eq!(index.spilled_count(), 0); assert_eq!(index.keys(), 0);
assert_eq!(index.items(), 0);
index.insert(&[0x10, 0x03], 3);
assert_eq!(index.spilled_count(), 0);
index.insert(&[0x10, 0x04], 4);
assert_eq!(index.spilled_count(), 1);
assert_eq!(
index.get(&[0x10, 0x03]).copied().collect::<Vec<_>>(),
vec![3]
);
assert_eq!(
index.get(&[0x10, 0x04]).copied().collect::<Vec<_>>(),
vec![4]
);
index.remove(&[0x10, 0x03]);
assert_eq!(index.spilled_count(), 1); index.remove(&[0x10, 0x04]);
assert_eq!(index.spilled_count(), 0);
assert_eq!(index.keys(), 0);
assert_eq!(index.items(), 0);
assert_eq!(index.pruned(), 4);
});
}
#[test_traced]
fn test_spill_get_mut_or_insert() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
index.insert(&[0x10, 0x01], 1);
index.insert(&[0x10, 0x02], 2); assert_eq!(index.spilled_count(), 1);
assert_eq!(index.keys(), 2);
assert_eq!(index.items(), 2);
{
let mut cursor = index.get_mut_or_insert(&[0x10, 0x01], 99).unwrap();
assert_eq!(cursor.next().copied(), Some(1));
assert!(cursor.next().is_none());
}
assert_eq!(index.keys(), 2);
assert_eq!(index.items(), 2);
assert_eq!(
index.get(&[0x10, 0x01]).copied().collect::<Vec<_>>(),
vec![1]
);
assert!(index.get_mut_or_insert(&[0x10, 0x03], 3).is_none());
assert_eq!(index.spilled_count(), 1);
assert_eq!(index.keys(), 3);
assert_eq!(index.items(), 3);
assert_eq!(
index.get(&[0x10, 0x03]).copied().collect::<Vec<_>>(),
vec![3]
);
});
}
#[test_traced]
#[should_panic(expected = "must call Cursor::next()")]
fn test_spill_cursor_delete_before_next_panics() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index_spilling(context);
index.insert(&[0x10, 0x01], 1);
index.insert(&[0x10, 0x02], 2); let mut cursor = index.get_mut(&[0x10, 0x01]).unwrap(); cursor.delete();
});
}
#[test_traced]
fn test_soa_basic() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
assert_eq!(index.keys(), 0);
let key = b"duplicate".as_slice();
index.insert(key, 1);
index.insert(key, 2);
index.insert(key, 3);
assert_eq!(index.keys(), 1);
assert_eq!(index.items(), 3);
assert_eq!(index.get(key).copied().collect::<Vec<_>>(), vec![1, 2, 3]);
{
let mut cursor = index.get_mut(key).unwrap();
assert_eq!(*cursor.next().unwrap(), 1);
assert_eq!(*cursor.next().unwrap(), 2);
assert_eq!(*cursor.next().unwrap(), 3);
assert!(cursor.next().is_none());
}
index.insert(key, 3);
index.insert(key, 4);
index.retain(key, |i| *i != 3);
assert_eq!(index.get(key).copied().collect::<Vec<_>>(), vec![1, 2, 4]);
index.retain(key, |_| false);
assert_eq!(
index.get(key).copied().collect::<Vec<_>>(),
Vec::<u64>::new()
);
assert_eq!(index.keys(), 0);
assert!(index.get_mut(key).is_none());
index.retain(key, |_| false);
});
}
#[test_traced]
fn test_soa_cursor_find() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
let key = b"test_key";
for v in [10u64, 20, 30, 40] {
index.insert(key, v);
}
{
let mut cursor = index.get_mut(key).unwrap();
assert!(cursor.find(|&v| v == 30));
cursor.update(35);
}
let values: Vec<u64> = index.get(key).copied().collect();
assert!(values.contains(&35) && !values.contains(&30));
{
let mut cursor = index.get_mut(key).unwrap();
assert!(!cursor.find(|&v| v == 100));
assert!(cursor.next().is_none());
}
{
let mut cursor = index.get_mut(key).unwrap();
assert!(cursor.find(|&v| v == 20));
cursor.delete();
}
let values: Vec<u64> = index.get(key).copied().collect();
assert!(!values.contains(&20));
assert_eq!(values.len(), 3);
});
}
#[test_traced]
fn test_soa_get_many_and_partitions() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
index.insert(b"ab", 1);
index.insert(b"ab", 2);
index.insert(b"abX", 3);
index.insert(b"zz", 4);
let keys: Vec<&[u8]> = vec![b"zz", b"missing", b"ab", b"zz"];
let mut visits: Vec<Vec<u64>> = vec![Vec::new(); keys.len()];
index.get_many(&keys, |key_idx, value| visits[key_idx].push(*value));
assert_eq!(visits[0], vec![4]);
assert!(visits[1].is_empty());
assert_eq!(visits[2], vec![1, 2, 3]);
assert_eq!(visits[3], vec![4]);
});
}
#[test_traced]
fn test_soa_insert_and_retain() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
index.insert(b"k", 1u64);
index.insert_and_retain(b"k", 2, |_| true);
assert_eq!(index.get(b"k").copied().collect::<Vec<_>>(), vec![1, 2]);
index.insert_and_retain(b"k", 9, |v| *v != 9);
assert_eq!(index.get(b"k").copied().collect::<Vec<_>>(), vec![1, 2]);
index.insert_and_retain(b"k", 9, |_| false);
assert!(index.get_mut(b"k").is_none());
assert_eq!(index.keys(), 0);
index.insert_and_retain(b"new", 7, |_| true);
assert_eq!(index.get(b"new").copied().collect::<Vec<_>>(), vec![7]);
assert_eq!(index.keys(), 1);
});
}
#[test_traced]
fn test_soa_remove() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
index.insert(b"k", 1u64);
index.insert(b"k", 2);
index.insert(b"other", 3);
assert_eq!(index.items(), 3);
assert_eq!(index.keys(), 2);
index.remove(b"k");
assert!(index.get_mut(b"k").is_none());
assert_eq!(index.keys(), 1);
assert_eq!(index.items(), 1);
assert_eq!(index.pruned(), 2);
assert_eq!(index.get(b"other").copied().collect::<Vec<_>>(), vec![3]);
index.remove(b"missing"); assert_eq!(index.keys(), 1);
});
}
#[test_traced]
fn test_soa_ordered() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
assert!(index.first_translated_key().is_none());
assert!(index.last_translated_key().is_none());
assert!(index.next_translated_key(b"key").is_none());
assert!(index.prev_translated_key(b"key").is_none());
let k1 = &hex!("0x0b02AA"); let k2 = &hex!("0x1c04CC"); let k2_collides = &hex!("0x1c0411"); let k3 = &hex!("0x2d06EE"); index.insert(k1, 1);
index.insert(k2, 21);
index.insert(k2_collides, 22);
index.insert(k3, 3);
assert_eq!(index.keys(), 3);
assert_eq!(index.first_translated_key().unwrap().next(), Some(&1));
assert_eq!(index.last_translated_key().unwrap().next(), Some(&3));
let (mut it, wrapped) = index.next_translated_key(&[0x00]).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&1));
assert_eq!(it.next(), None);
let (mut it, wrapped) = index.next_translated_key(&hex!("0x0b02F2")).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&21));
assert_eq!(it.next(), Some(&22));
assert_eq!(it.next(), None);
let (mut it, wrapped) = index.next_translated_key(k3).unwrap();
assert!(wrapped);
assert_eq!(it.next(), Some(&1));
let (mut it, wrapped) = index.prev_translated_key(k1).unwrap();
assert!(wrapped);
assert_eq!(it.next(), Some(&3));
let (mut it, wrapped) = index.prev_translated_key(&hex!("0x1d0102")).unwrap();
assert!(!wrapped);
assert_eq!(it.next(), Some(&21));
assert_eq!(it.next(), Some(&22));
assert_eq!(it.next(), None);
});
}
#[test_traced]
fn test_soa_ordered_exhaustive_traversal() {
deterministic::Runner::default().start(|context| async move {
let mut index = new_index(context);
let prefixes = [0x00u8, 0x05, 0xAA, 0xFF];
let subkeys = [0x00u8, 0x80, 0xFF];
let mut keys: Vec<[u8; 2]> = Vec::new();
for &p in &prefixes {
for &s in &subkeys {
keys.push([p, s]);
}
}
let value_of = |k: &[u8; 2]| ((k[0] as u64) << 8) | k[1] as u64;
let n = keys.len();
let mut scrambled = keys.clone();
scrambled.reverse();
scrambled.rotate_left(5);
for k in &scrambled {
index.insert(k, value_of(k));
}
assert_eq!(index.keys(), n);
assert_eq!(
index.first_translated_key().unwrap().next(),
Some(&value_of(&keys[0]))
);
assert_eq!(
index.last_translated_key().unwrap().next(),
Some(&value_of(&keys[n - 1]))
);
for i in 0..n {
let next = value_of(&keys[(i + 1) % n]);
let (mut it, wrapped) = index.next_translated_key(&keys[i]).unwrap();
assert_eq!(wrapped, i + 1 == n, "next wrap at index {i}");
assert_eq!(it.next(), Some(&next), "next at {i}");
assert_eq!(it.next(), None);
let prev = value_of(&keys[(i + n - 1) % n]);
let (mut it, wrapped) = index.prev_translated_key(&keys[i]).unwrap();
assert_eq!(wrapped, i == 0, "prev wrap at index {i}");
assert_eq!(it.next(), Some(&prev), "prev at {i}");
assert_eq!(it.next(), None);
}
});
}
}