use std::{fmt::Debug, hash::Hash, sync::Arc};
use diskann_utils::{Reborrow, future::SendFuture};
use hashbrown::hash_map;
use crate::{
error::{RankedError, ToRanked, TransientError},
graph::glue,
provider::Accessor,
};
use super::{AsWorkingSet, Fill};
#[derive(Debug)]
pub struct Map<K, V, P: Projection = Reborrowed<V>> {
map: hashbrown::HashMap<K, Generation<V>>,
overlay: Option<Overlay<K, P>>,
capacity: Option<usize>,
generation: u32,
}
impl<K, V, P: Projection> Map<K, V, P> {
fn new_generation(&mut self) {
self.generation = self.generation.wrapping_add(1);
}
}
impl<K, V, P> Map<K, V, P>
where
K: Hash + Eq,
P: Projection,
{
fn promote<Itr>(&mut self, itr: Itr) -> usize
where
Itr: ExactSizeIterator<Item = K>,
{
itr.filter(|k| {
if let Some(tagged) = self.map.get_mut(k) {
tagged.generation = self.generation;
true
} else {
false
}
})
.count()
}
fn evict(&mut self, count: usize) -> usize {
if count == 0 {
return 0;
}
let mut remaining = count;
self.map.retain(|_, v| {
if remaining == 0 {
true
} else if v.generation != self.generation {
remaining -= 1;
false
} else {
true
}
});
count - remaining
}
pub fn prepare<Itr>(&mut self, itr: Itr)
where
Itr: ExactSizeIterator<Item = K>,
{
self.new_generation();
if let Some(capacity) = self.capacity {
if capacity == 0 {
self.map.clear();
} else {
let len = itr.len();
let promoted = self.promote(itr);
let extra_needed = len - promoted;
let final_size = self.map.len().saturating_add(extra_needed);
if let Some(to_evict) = final_size.checked_sub(capacity) {
self.evict(to_evict);
}
}
}
}
}
impl<K, V, P> Map<K, V, P>
where
K: Copy + Hash + Eq + Send + Sync,
V: Send + Sync,
P: Projection,
{
pub fn fill<'a, A, Itr>(
&'a mut self,
accessor: &'a mut A,
itr: Itr,
) -> impl SendFuture<Result<View<'a, K, V, P>, <A::GetError as ToRanked>::Error>>
where
A: for<'b> Accessor<Id = K, ElementRef<'b>: Into<V>>,
Itr: ExactSizeIterator<Item = K> + Clone + Send + Sync,
{
self.prepare(itr.clone());
self.fill_with(accessor, itr, |element| element.into())
}
pub fn fill_with<'a, A, Itr, F>(
&'a mut self,
accessor: &'a mut A,
itr: Itr,
f: F,
) -> impl SendFuture<Result<View<'a, K, V, P>, <A::GetError as ToRanked>::Error>>
where
A: Accessor<Id = K>,
Itr: ExactSizeIterator<Item = K> + Send + Sync,
F: Fn(A::ElementRef<'_>) -> V + Send,
{
async move {
for i in itr {
match self.entry(i) {
Entry::Seeded(_) | Entry::Occupied(_) => { }
Entry::Vacant(vacant) => match accessor.get_element(i).await {
Ok(element) => {
vacant.insert(f(element.reborrow()));
}
Err(local_error) => match local_error.to_ranked() {
RankedError::Transient(transient) => {
transient.acknowledge(
"transient error during fill; element will be absent from working set",
);
}
RankedError::Error(critical) => {
return Err(critical);
}
},
},
}
}
Ok(self.view())
}
}
}
impl<K, V, P> Map<K, V, P>
where
K: Hash + Eq,
P: Projection,
{
pub fn get(&self, key: &K) -> Option<&V> {
self.map.get(key).map(|v| v.value())
}
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
self.map.get_mut(key).map(|v| {
v.generation = self.generation;
v.value_mut()
})
}
pub fn clear(&mut self) {
self.map.clear()
}
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
if self.overlay.as_ref().is_some_and(|s| s.contains_key(&key)) {
return None;
}
self.map
.insert(key, Generation::new(value, self.generation))
.map(|v| v.into_inner())
}
pub fn contains_key(&self, key: &K) -> bool {
self.overlay.as_ref().is_some_and(|s| s.contains_key(key)) || self.map.contains_key(key)
}
pub fn entry(&mut self, key: K) -> Entry<'_, K, V, P> {
if let Some(element) = self.overlay.as_ref().and_then(|s| s.get(&key)) {
return Entry::Seeded(element);
}
let generation = self.generation;
match self.map.entry(key) {
hash_map::Entry::Occupied(mut o) => {
o.get_mut().generation = self.generation;
Entry::Occupied(OccupiedEntry { entry: o })
}
hash_map::Entry::Vacant(v) => Entry::Vacant(VacantEntry {
entry: v,
generation,
}),
}
}
pub fn view(&self) -> View<'_, K, V, P> {
View { map: self }
}
#[cfg(test)]
fn generation_of(&self, k: K) -> u32 {
self.map.get(&k).unwrap().generation
}
}
#[derive(Debug)]
struct Generation<T> {
generation: u32,
value: T,
}
impl<T> Generation<T> {
fn new(value: T, generation: u32) -> Self {
Self { generation, value }
}
fn value(&self) -> &T {
&self.value
}
fn value_mut(&mut self) -> &mut T {
&mut self.value
}
fn into_inner(self) -> T {
self.value
}
}
pub enum Entry<'a, K, V, P: Projection> {
Seeded(P::Element<'a>),
Occupied(OccupiedEntry<'a, K, V>),
Vacant(VacantEntry<'a, K, V>),
}
pub struct OccupiedEntry<'a, K, V> {
entry: hash_map::OccupiedEntry<'a, K, Generation<V>>,
}
impl<'a, K, V> OccupiedEntry<'a, K, V> {
pub fn get(&self) -> &V {
self.entry.get().value()
}
pub fn get_mut(&mut self) -> &mut V {
self.entry.get_mut().value_mut()
}
pub fn into_mut(self) -> &'a mut V {
self.entry.into_mut().value_mut()
}
}
pub struct VacantEntry<'a, K, V> {
entry: hash_map::VacantEntry<'a, K, Generation<V>>,
generation: u32,
}
impl<K, V> VacantEntry<'_, K, V> {
pub fn insert(self, value: V)
where
K: Hash + Eq,
{
self.entry.insert(Generation::new(value, self.generation));
}
pub fn key(&self) -> &K {
self.entry.key()
}
}
impl<A, V, P> Fill<Map<A::Id, V, P>> for A
where
P: Projection,
A: for<'a> Accessor<Id: Hash + Eq, ElementRef<'a> = P::ElementRef<'a>>,
V: Project<P> + Send + Sync + 'static,
for<'a> A::ElementRef<'a>: Into<V>,
{
type Error = <A::GetError as ToRanked>::Error;
type View<'a>
= View<'a, A::Id, V, P>
where
Self: 'a;
fn fill<'a, Itr>(
&'a mut self,
map: &'a mut Map<A::Id, V, P>,
itr: Itr,
) -> impl SendFuture<Result<Self::View<'a>, Self::Error>>
where
Itr: ExactSizeIterator<Item = A::Id> + Clone + Send + Sync,
Self: 'a,
{
map.fill(self, itr)
}
}
pub trait Projection: Send + Sync + 'static {
type Element<'a>: for<'b> Reborrow<'b, Target = Self::ElementRef<'b>> + Send;
type ElementRef<'a>;
}
#[derive(Debug)]
pub struct Ref<T: ?Sized>(std::marker::PhantomData<T>);
impl<T> Clone for Ref<T>
where
T: ?Sized,
{
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Ref<T> where T: ?Sized {}
impl<T> Projection for Ref<T>
where
T: ?Sized + Send + Sync + 'static,
{
type Element<'a> = &'a T;
type ElementRef<'a> = &'a T;
}
#[derive(Debug)]
pub struct Reborrowed<T>(std::marker::PhantomData<T>)
where
T: ?Sized;
impl<T> Clone for Reborrowed<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Reborrowed<T> {}
impl<T> Projection for Reborrowed<T>
where
T: for<'a> Reborrow<'a> + ?Sized + Send + Sync + 'static,
{
type Element<'a> = AsReborrowed<'a, T>;
type ElementRef<'a> = <T as Reborrow<'a>>::Target;
}
#[derive(Debug)]
pub struct AsReborrowed<'a, T>(&'a T)
where
T: ?Sized;
impl<T> Clone for AsReborrowed<'_, T>
where
T: ?Sized,
{
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for AsReborrowed<'_, T> where T: ?Sized {}
impl<'a, T> Reborrow<'a> for AsReborrowed<'_, T>
where
T: Reborrow<'a> + ?Sized,
{
type Target = T::Target;
fn reborrow(&'a self) -> Self::Target {
self.0.reborrow()
}
}
impl<T> Project<Reborrowed<T>> for T
where
T: for<'a> Reborrow<'a> + ?Sized + Send + Sync + 'static,
{
fn project(&self) -> AsReborrowed<'_, T> {
AsReborrowed(self)
}
}
pub trait Project<P>
where
P: Projection,
{
fn project(&self) -> P::Element<'_>;
}
impl<T> Project<Ref<T>> for Box<T>
where
T: Send + Sync + 'static + ?Sized,
{
fn project(&self) -> &T {
self
}
}
impl<T> Project<Ref<[T]>> for Vec<T>
where
T: Send + Sync + 'static,
{
fn project(&self) -> &[T] {
self
}
}
#[derive(Debug)]
pub struct Overlay<K, P: Projection> {
overlay: Arc<dyn MapLike<K, P>>,
}
impl<K, P: Projection> Clone for Overlay<K, P> {
fn clone(&self) -> Self {
Self {
overlay: self.overlay.clone(),
}
}
}
impl<K, P> Overlay<K, P>
where
P: Projection,
{
pub fn new(overlay: Arc<dyn MapLike<K, P>>) -> Self {
Self { overlay }
}
pub fn get(&self, key: &K) -> Option<P::Element<'_>> {
self.overlay.get(key)
}
pub fn contains_key(&self, key: &K) -> bool {
self.overlay.contains_key(key)
}
}
impl<K, P> Overlay<K, P>
where
K: Hash + Eq + Send + Sync + 'static,
P: Projection,
{
pub fn from_batch<B, I>(batch: Arc<B>, ids: I) -> Self
where
I: IntoIterator<Item = K, IntoIter: ExactSizeIterator>,
B: for<'a> glue::Batch<Element<'a>: Into<P::Element<'a>>> + Debug,
K: Debug,
{
let indices: hashbrown::HashMap<K, usize> =
ids.into_iter().enumerate().map(|(i, id)| (id, i)).collect();
Self {
overlay: Arc::new(BatchOverlay { batch, indices }),
}
}
}
pub trait MapLike<K, P: Projection>: Debug + Send + Sync {
fn get(&self, key: &K) -> Option<P::Element<'_>>;
fn contains_key(&self, key: &K) -> bool;
}
impl<K, V, P> MapLike<K, P> for hashbrown::HashMap<K, V>
where
K: Hash + Eq + Debug + Send + Sync,
V: Project<P> + Debug + Send + Sync,
P: Projection,
{
fn get(&self, key: &K) -> Option<P::Element<'_>> {
hashbrown::HashMap::get(self, key).map(|v| v.project())
}
fn contains_key(&self, key: &K) -> bool {
hashbrown::HashMap::contains_key(self, key)
}
}
#[derive(Debug)]
struct BatchOverlay<K, B> {
batch: Arc<B>,
indices: hashbrown::HashMap<K, usize>,
}
impl<K, B, P> MapLike<K, P> for BatchOverlay<K, B>
where
K: Hash + Eq + Debug + Send + Sync,
B: for<'a> glue::Batch<Element<'a>: Into<P::Element<'a>>> + Debug,
P: Projection,
{
fn get(&self, key: &K) -> Option<P::Element<'_>> {
self.indices.get(key).map(|&idx| self.batch.get(idx).into())
}
fn contains_key(&self, key: &K) -> bool {
self.indices.contains_key(key)
}
}
#[derive(Debug)]
pub struct Builder<K, P: Projection> {
overlay: Option<Overlay<K, P>>,
capacity: Capacity,
}
impl<K, P: Projection> Builder<K, P> {
pub fn new(capacity: Capacity) -> Self {
Self {
overlay: None,
capacity,
}
}
pub fn with_overlay(mut self, overlay: Overlay<K, P>) -> Self {
self.overlay = Some(overlay);
self
}
pub fn build<V>(self, default_capacity: usize) -> Map<K, V, P> {
let capacity = self.capacity.resolve(default_capacity);
Map {
map: hashbrown::HashMap::with_capacity(capacity.unwrap_or(0)),
overlay: self.overlay,
capacity,
generation: 0,
}
}
}
impl<K, P: Projection> Clone for Builder<K, P> {
fn clone(&self) -> Self {
Self {
overlay: self.overlay.clone(),
capacity: self.capacity,
}
}
}
impl<K, V, P> AsWorkingSet<Map<K, V, P>> for Builder<K, P>
where
P: Projection,
{
fn as_working_set(&self, capacity: usize) -> Map<K, V, P> {
self.clone().build(capacity)
}
}
#[derive(Debug, Clone, Copy)]
pub enum Capacity {
None,
Default,
Some(usize),
Unbounded,
}
impl Capacity {
pub fn resolve(self, default: usize) -> Option<usize> {
match self {
Self::None => Some(0),
Self::Default => Some(default),
Self::Some(actual) => Some(actual),
Self::Unbounded => Option::None,
}
}
}
#[derive(Debug)]
pub struct View<'a, K, V, P: Projection = Reborrowed<V>> {
map: &'a Map<K, V, P>,
}
impl<K, V, P> super::View<K> for View<'_, K, V, P>
where
K: Hash + Eq,
P: Projection,
V: Project<P> + Send + Sync + 'static,
{
type ElementRef<'a> = P::ElementRef<'a>;
type Element<'a>
= P::Element<'a>
where
Self: 'a;
fn get(&self, id: K) -> Option<Self::Element<'_>> {
self.map
.overlay
.as_ref()
.and_then(|s| s.get(&id))
.or_else(|| self.map.map.get(&id).map(|v| v.value().project()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{borrow::Cow, sync::Arc};
use diskann_utils::views::Matrix;
use crate::graph::{
test::provider::Accessor as TestAccessor, workingset::View as WorkingSetView,
};
type TestMap = Map<u32, Box<[f32]>, Ref<[f32]>>;
type TestMapProjected = Map<u32, Box<[f32]>, TestProjection>;
fn unbounded_map() -> TestMap {
Builder::new(Capacity::Unbounded).build(0)
}
fn unbounded_map_projected() -> TestMapProjected {
Builder::new(Capacity::Unbounded).build(0)
}
fn seeded_map(overlay: Overlay<u32, Ref<[f32]>>, capacity: Capacity) -> TestMap {
Builder::new(capacity).with_overlay(overlay).build(16)
}
fn seeded_map_projected(
overlay: Overlay<u32, TestProjection>,
capacity: Capacity,
) -> TestMapProjected {
Builder::new(capacity).with_overlay(overlay).build(16)
}
#[derive(Debug, Clone, Copy)]
struct TestProjection;
#[derive(Debug, PartialEq, Clone, Copy)]
enum Source {
Project,
Into,
}
#[derive(Debug, PartialEq)]
struct Wrapped<'a> {
data: &'a [f32],
source: Source,
}
impl<'a> Wrapped<'a> {
fn new(data: &'a [f32], source: Source) -> Self {
Self { data, source }
}
}
impl<'a> Reborrow<'a> for Wrapped<'_> {
type Target = &'a [f32];
fn reborrow(&'a self) -> Self::Target {
self.data
}
}
impl Projection for TestProjection {
type Element<'a> = Wrapped<'a>;
type ElementRef<'a> = &'a [f32];
}
impl Project<TestProjection> for Box<[f32]> {
fn project(&self) -> Wrapped<'_> {
Wrapped {
data: self,
source: Source::Project,
}
}
}
impl<'a> From<&'a [f32]> for Wrapped<'a> {
fn from(data: &'a [f32]) -> Self {
Self {
data,
source: Source::Into,
}
}
}
fn test_matrix() -> Matrix<f32> {
Matrix::try_from(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0].into_boxed_slice(), 3, 2).unwrap()
}
type TestOverlay = Overlay<u32, Ref<[f32]>>;
fn test_overlay() -> (Arc<Matrix<f32>>, TestOverlay) {
let batch = Arc::new(test_matrix());
let ids = [10u32, 20, 30];
let overlay = Overlay::from_batch(batch.clone(), ids);
(batch, overlay)
}
fn test_overlay_projected() -> (Arc<Matrix<f32>>, Overlay<u32, TestProjection>) {
let batch = Arc::new(test_matrix());
let ids = [10u32, 20, 30];
let overlay = Overlay::from_batch(batch.clone(), ids);
(batch, overlay)
}
#[test]
fn test_capacity() {
let c = Capacity::None;
assert_eq!(c.resolve(0), Some(0));
assert_eq!(c.resolve(50), Some(0));
assert_eq!(c.resolve(100), Some(0));
let c = Capacity::Default;
assert_eq!(c.resolve(0), Some(0));
assert_eq!(c.resolve(50), Some(50));
assert_eq!(c.resolve(100), Some(100));
for i in [5, 10, 15] {
let c = Capacity::Some(i);
assert_eq!(c.resolve(0), Some(i));
assert_eq!(c.resolve(50), Some(i));
assert_eq!(c.resolve(100), Some(i));
}
let c = Capacity::Unbounded;
assert_eq!(c.resolve(0), None);
assert_eq!(c.resolve(50), None);
assert_eq!(c.resolve(100), None);
}
#[test]
fn insert_and_get() {
let mut map = unbounded_map();
assert_eq!(map.generation, 0);
assert!(map.get(&1).is_none());
assert!(map.get(&2).is_none());
assert!(map.get(&3).is_none());
assert!(!map.contains_key(&1));
assert!(!map.contains_key(&2));
assert!(!map.contains_key(&3));
assert!(map.insert(1, vec![1.0, 2.0].into_boxed_slice()).is_none());
assert_eq!(&**map.get(&1).unwrap(), &[1.0, 2.0]);
assert!(map.get(&2).is_none());
assert!(map.get(&3).is_none());
assert!(map.contains_key(&1));
assert!(!map.contains_key(&2));
assert!(!map.contains_key(&3));
assert_eq!(map.generation_of(1), 0);
map.new_generation();
assert_eq!(map.generation, 1);
assert!(map.insert(3, vec![2.0, 3.0].into_boxed_slice()).is_none());
assert_eq!(&**map.get(&1).unwrap(), &[1.0, 2.0]);
assert!(map.get(&2).is_none());
assert_eq!(&**map.get(&3).unwrap(), &[2.0, 3.0]);
assert!(map.contains_key(&1));
assert!(!map.contains_key(&2));
assert!(map.contains_key(&3));
assert_eq!(map.generation_of(1), 0);
assert_eq!(map.generation_of(3), 1);
}
#[test]
fn insert_overwrites_existing() {
let mut map = unbounded_map();
map.insert(1, Box::new([1.0]));
assert_eq!(map.generation_of(1), 0);
map.new_generation();
let old = map.insert(1, Box::new([2.0]));
assert_eq!(&*old.unwrap(), &[1.0]);
assert_eq!(&**map.get(&1).unwrap(), &[2.0]);
assert_eq!(map.generation_of(1), 1);
}
#[test]
fn get_mut_modifies_in_place() {
let mut map = unbounded_map();
map.insert(1, Box::new([0.0, 0.0]));
assert_eq!(map.generation_of(1), 0);
map.new_generation();
map.get_mut(&1).unwrap()[0] = 42.0;
assert_eq!(&**map.get(&1).unwrap(), &[42.0, 0.0]);
assert_eq!(
map.generation_of(1),
1,
"`get_mut` implicitly bumps the generation"
);
}
#[test]
fn clear_empties_map() {
let mut map = unbounded_map();
map.insert(1, Box::new([1.0]));
map.insert(2, Box::new([2.0]));
map.clear();
assert!(!map.contains_key(&1));
assert!(!map.contains_key(&2));
}
#[test]
fn entry_vacant_then_occupied() {
let mut map = unbounded_map();
map.new_generation();
match map.entry(1) {
Entry::Vacant(v) => {
assert_eq!(*v.key(), 1);
v.insert(Box::new([1.0]));
}
_ => panic!("expected Vacant"),
}
match map.entry(1) {
Entry::Occupied(o) => {
assert_eq!(&**o.get(), &[1.0]);
}
_ => panic!("expected Occupied"),
}
assert_eq!(
map.generation_of(1),
1,
"vacant inserts match current generation"
);
}
#[test]
fn occupied_entry_mut() {
let mut map = unbounded_map();
map.insert(1, Box::new([0.0]));
assert_eq!(map.generation_of(1), 0);
map.new_generation();
match map.entry(1) {
Entry::Occupied(o) => {
let val = o.into_mut();
val[0] = 99.0;
}
_ => panic!("expected Occupied"),
}
assert_eq!(&**map.get(&1).unwrap(), &[99.0]);
assert_eq!(
map.generation_of(1),
1,
"OccupiedEntry implicitly increments generation"
);
map.new_generation();
match map.entry(1) {
Entry::Occupied(mut o) => {
let val = o.get_mut();
val[0] = 42.0;
}
_ => panic!("expected Occupied"),
}
assert_eq!(&**map.get(&1).unwrap(), &[42.0]);
assert_eq!(
map.generation_of(1),
2,
"OccupiedEntry implicitly increments generation"
);
}
#[test]
fn view_returns_projected_element() {
{
let mut map = unbounded_map();
map.insert(1, Box::new([1.0, 2.0]));
map.new_generation();
let view = map.view();
let element = view.get(1).unwrap();
assert_eq!(element, &[1.0, 2.0]);
assert!(view.get(999).is_none());
}
{
let mut map = unbounded_map_projected();
map.insert(1, Box::new([1.0, 2.0]));
map.new_generation();
let view = map.view();
let element = view.get(1).unwrap();
assert_eq!(element, Wrapped::new(&[1.0, 2.0], Source::Project));
assert!(view.get(999).is_none());
}
}
#[test]
fn overlay_from_batch_lookups() {
{
let (_batch, overlay) = test_overlay();
assert_eq!(overlay.get(&10).unwrap(), &[1.0, 2.0]);
assert_eq!(overlay.get(&20).unwrap(), &[3.0, 4.0]);
assert_eq!(overlay.get(&30).unwrap(), &[5.0, 6.0]);
assert!(overlay.contains_key(&10));
assert!(overlay.contains_key(&20));
assert!(overlay.get(&99).is_none());
assert!(!overlay.contains_key(&99));
}
{
let (_batch, overlay) = test_overlay_projected();
let source = Source::Into;
assert_eq!(overlay.get(&10).unwrap(), Wrapped::new(&[1.0, 2.0], source));
assert_eq!(overlay.get(&20).unwrap(), Wrapped::new(&[3.0, 4.0], source));
assert_eq!(overlay.get(&30).unwrap(), Wrapped::new(&[5.0, 6.0], source));
assert!(overlay.contains_key(&10));
assert!(overlay.contains_key(&20));
assert!(overlay.get(&99).is_none());
assert!(!overlay.contains_key(&99));
}
}
#[test]
fn overlay_is_cheap_to_clone() {
let (_batch, overlay) = test_overlay();
let base = overlay.get(&10).unwrap();
assert_eq!(base, &[1.0, 2.0]);
let cloned = overlay.clone();
let from_cloned = cloned.get(&10).unwrap();
assert_eq!(from_cloned, &[1.0, 2.0]);
assert_eq!(
from_cloned.as_ptr(),
base.as_ptr(),
"underlying data should be the exact same"
);
}
#[test]
fn overlay_from_batch_zero_copy() {
let batch = Arc::new(test_matrix());
let overlay = Overlay::<u32, Ref<[f32]>>::from_batch(batch.clone(), [10u32, 20, 30]);
let from_overlay: &[f32] = overlay.get(&10).unwrap();
let from_batch: &[f32] = batch.row(0);
assert!(std::ptr::eq(from_overlay, from_batch));
}
#[test]
fn seeded_map_entry_seeded() {
let (_batch, overlay) = test_overlay_projected();
let mut map = seeded_map_projected(overlay, Capacity::Default);
assert_eq!(map.generation, 0);
match map.entry(99) {
Entry::Vacant(v) => {
assert_eq!(*v.key(), 99);
}
_ => panic!("expected Vacant for key not in overlay or map"),
}
match map.entry(10) {
Entry::Seeded(element) => {
assert_eq!(element, Wrapped::new(&[1.0, 2.0], Source::Into));
}
_ => panic!("expected Seeded for key present in overlay"),
}
match map.entry(99) {
Entry::Vacant(v) => {
v.insert(Box::new([5.0]));
}
_ => panic!("expected Vacant"),
}
assert_eq!(&**map.get(&99).unwrap(), &[5.0]);
assert_eq!(map.generation_of(99), 0);
map.new_generation();
match map.entry(99) {
Entry::Occupied(v) => {
*v.into_mut() = Box::new([10.0]);
}
_ => panic!("expected Occupied"),
}
assert_eq!(&**map.get(&99).unwrap(), &[10.0]);
assert_eq!(map.generation_of(99), 1);
}
#[test]
fn seeded_map_insert_skips_seeded_key() {
let (_batch, overlay) = test_overlay_projected();
let mut map = seeded_map_projected(overlay, Capacity::Default);
assert!(
map.insert(10, Box::new([99.0])).is_none(),
"The key 10 should not exist"
);
let view = map.view();
assert_eq!(
view.get(10).unwrap(),
Wrapped::new(&[1.0, 2.0], Source::Into)
);
assert!(
map.insert(10, Box::new([99.0])).is_none(),
"The key 10 should not still exist"
);
}
#[test]
fn seeded_map_insert_works_for_non_seeded() {
let (_batch, overlay) = test_overlay_projected();
let mut map = seeded_map_projected(overlay, Capacity::Default);
map.new_generation();
map.insert(99, Box::new([7.0, 8.0]));
assert_eq!(map.generation_of(99), 1);
assert!(map.contains_key(&10));
assert!(map.contains_key(&20));
assert!(map.contains_key(&30));
assert!(map.contains_key(&99));
assert!(!map.contains_key(&1));
assert!(map.get(&10).is_none());
assert!(map.get(&20).is_none());
assert!(map.get(&30).is_none());
assert_eq!(
map.get(&99).unwrap().as_ref(),
&[7.0, 8.0],
"get does not check the overlay"
);
assert!(!map.contains_key(&1));
}
#[test]
fn view_seed_first_priority() {
let (_batch, overlay) = test_overlay_projected();
let mut map = seeded_map_projected(overlay, Capacity::Default);
map.insert(99, Box::new([7.0, 8.0]));
let view = map.view();
assert_eq!(
view.get(10).unwrap(),
Wrapped::new(&[1.0, 2.0], Source::Into)
);
assert_eq!(
view.get(99).unwrap(),
Wrapped::new(&[7.0, 8.0], Source::Project)
);
assert!(view.get(1).is_none());
}
#[test]
fn view_after_clear_still_has_seed() {
let (_batch, overlay) = test_overlay();
let mut map = seeded_map(overlay, Capacity::Default);
map.insert(99, Box::new([7.0]));
map.clear();
let view = map.view();
assert_eq!(view.get(10).unwrap(), &[1.0, 2.0]);
assert!(view.get(99).is_none());
}
#[test]
fn test_promote() {
let mut map: TestMap = Builder::new(Capacity::Default).build(5);
map.insert(1, Box::new([1.0]));
map.insert(2, Box::new([2.0]));
map.insert(3, Box::new([3.0]));
map.new_generation();
assert_eq!(map.promote([5, 6, 7].into_iter()), 0);
for i in [1, 2, 3] {
assert_eq!(
map.generation_of(i),
0,
"generation should remain unchanged"
);
}
map.new_generation();
assert_eq!(map.promote([5, 2, 6, 3, 7, 1].into_iter()), 3);
for i in [1, 2, 3] {
assert_eq!(map.generation_of(i), 2);
}
map.new_generation();
assert_eq!(map.promote([1, 4, 2, 10].into_iter()), 2);
assert_eq!(map.generation_of(1), 3);
assert_eq!(map.generation_of(2), 3);
assert_eq!(map.generation_of(3), 2, "this entry was not promoted");
map.new_generation();
assert_eq!(map.promote([1, 3].into_iter()), 2);
assert_eq!(map.generation_of(1), 4);
assert_eq!(map.generation_of(2), 3, "this entry was not promoted");
assert_eq!(map.generation_of(3), 4);
}
#[test]
fn test_evict() {
let mut map: TestMap = Builder::new(Capacity::Default).build(5);
fn reset(map: &mut TestMap) {
map.clear();
map.insert(1, Box::new([1.0]));
map.insert(2, Box::new([2.0]));
map.insert(3, Box::new([3.0]));
}
reset(&mut map);
for count in 0..10 {
assert_eq!(
map.evict(count),
0,
"all items belong to the current generation"
);
assert_eq!(&**map.get(&1).unwrap(), [1.0]);
assert_eq!(&**map.get(&2).unwrap(), [2.0]);
assert_eq!(&**map.get(&3).unwrap(), [3.0]);
}
map.new_generation(); assert_eq!(map.evict(0), 0);
assert_eq!(map.map.len(), 3);
let _ = map.get_mut(&2).unwrap();
assert_eq!(map.generation_of(2), 1);
assert_eq!(map.evict(1), 1);
assert_eq!(map.map.len(), 2);
assert!(map.contains_key(&2));
reset(&mut map);
map.new_generation(); let _ = map.get_mut(&2).unwrap();
assert_eq!(map.evict(2), 2);
assert_eq!(map.map.len(), 1);
assert!(map.contains_key(&2));
reset(&mut map);
map.new_generation(); let _ = map.get_mut(&1).unwrap();
assert_eq!(map.evict(10), 2);
assert_eq!(map.map.len(), 1);
assert!(map.contains_key(&1));
reset(&mut map);
map.new_generation(); assert_eq!(map.evict(10), 3);
assert!(map.map.is_empty());
}
#[test]
fn test_prepare_no_capacity() {
let mut map: TestMap = Builder::new(Capacity::None).build(100);
map.insert(1, Box::new([1.0]));
map.insert(2, Box::new([2.0]));
map.insert(3, Box::new([3.0]));
map.prepare([].into_iter());
assert!(map.map.is_empty());
}
#[test]
fn test_prepare_unbounded() {
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
map.insert(1, Box::new([1.0]));
map.insert(2, Box::new([2.0]));
map.insert(3, Box::new([3.0]));
map.prepare(1..100);
assert_eq!(map.map.len(), 3, "unbounded maps skip preparation");
}
#[test]
fn test_prepare_bounded() {
let mut map: TestMap = Builder::new(Capacity::Default).build(4);
fn reset(map: &mut TestMap) {
map.clear();
map.insert(1, Box::new([1.0]));
map.insert(2, Box::new([2.0]));
map.insert(3, Box::new([3.0]));
map.insert(4, Box::new([4.0]));
assert_eq!(map.map.len(), 4);
}
reset(&mut map);
map.prepare([1, 2, 5, 6].into_iter());
assert_eq!(map.generation, 1, "prepare should increment generation");
assert_eq!(map.map.len(), 2, "evicted 2 old-gen entries to make room");
assert_eq!(map.generation_of(1), 1);
assert_eq!(map.generation_of(2), 1);
assert!(map.get(&3).is_none(), "old-gen entry 3 should be evicted");
assert!(map.get(&4).is_none(), "old-gen entry 4 should be evicted");
reset(&mut map);
map.prepare([1, 2].into_iter());
assert_eq!(map.generation, 2);
assert_eq!(
map.map.len(),
4,
"since we don't exceed capacity - no eviction was necessary"
);
assert_eq!(map.generation_of(1), 2);
assert_eq!(map.generation_of(2), 2);
assert_eq!(map.generation_of(3), 1);
assert_eq!(map.generation_of(4), 1);
reset(&mut map);
map.prepare([1, 2, 10, 11, 12, 13].into_iter());
assert_eq!(map.generation, 3);
assert_eq!(map.map.len(), 2, "keep items present in the iterator");
reset(&mut map);
map.prepare([].into_iter());
assert_eq!(map.generation, 4);
assert_eq!(map.map.len(), 4, "at capacity — old entries survive");
assert_eq!(map.generation_of(1), 3);
assert_eq!(map.generation_of(2), 3);
assert_eq!(map.generation_of(3), 3);
assert_eq!(map.generation_of(4), 3);
reset(&mut map);
map.insert(5, Box::new([5.0]));
map.insert(6, Box::new([6.0]));
map.insert(7, Box::new([7.0]));
map.prepare([].into_iter());
assert_eq!(map.generation, 5);
assert_eq!(map.map.len(), 4, "evict down to capacity");
reset(&mut map);
map.prepare([20, 21, 22].into_iter());
assert_eq!(map.generation, 6);
assert_eq!(map.map.len(), 1, "evicted 1 old-gen entry to make room");
reset(&mut map);
map.prepare([20, 21, 22, 23, 24].into_iter());
assert_eq!(map.generation, 7);
assert!(map.map.is_empty());
}
#[test]
fn project_ref_box_slice() {
let value: Box<[f32]> = Box::new([1.0, 2.0, 3.0]);
let projected: &[f32] = <_ as Project<Ref<[f32]>>>::project(&value);
assert_eq!(projected, &[1.0, 2.0, 3.0]);
}
#[test]
fn project_ref_vec() {
let value = vec![4.0f32, 5.0];
let projected: &[f32] = <_ as Project<Ref<[f32]>>>::project(&value);
assert_eq!(projected, &[4.0, 5.0]);
}
#[test]
fn project_reborrowed_box_slice() {
let value: Box<[f32]> = Box::new([1.0, 2.0]);
let projected = <_ as Project<Reborrowed<Box<[f32]>>>>::project(&value);
let reborrowed: &[f32] = projected.reborrow();
assert_eq!(reborrowed, &[1.0, 2.0]);
}
#[test]
fn hashmap_as_maplike() {
let mut hm = hashbrown::HashMap::<u32, Box<[f32]>>::new();
hm.insert(1, Box::new([10.0, 20.0]));
hm.insert(2, Box::new([30.0]));
{
let map_like: &dyn MapLike<u32, Ref<[f32]>> = &hm;
assert_eq!(map_like.get(&1).unwrap(), &[10.0, 20.0]);
assert_eq!(map_like.get(&2).unwrap(), &[30.0]);
assert!(map_like.get(&3).is_none());
assert!(map_like.contains_key(&1));
assert!(!map_like.contains_key(&3));
let overlay = Overlay::<u32, Ref<[f32]>>::new(Arc::new(hm.clone()));
assert_eq!(overlay.get(&1).unwrap(), &[10.0, 20.0]);
assert_eq!(overlay.get(&2).unwrap(), &[30.0]);
assert!(overlay.get(&3).is_none());
assert!(overlay.contains_key(&1));
assert!(!overlay.contains_key(&3));
}
{
let map_like: &dyn MapLike<u32, TestProjection> = &hm;
let source = Source::Project;
assert_eq!(
map_like.get(&1).unwrap(),
Wrapped::new(&[10.0, 20.0], source)
);
assert_eq!(map_like.get(&2).unwrap(), Wrapped::new(&[30.0], source));
assert!(map_like.get(&3).is_none());
assert!(map_like.contains_key(&1));
assert!(!map_like.contains_key(&3));
let overlay = Overlay::<u32, TestProjection>::new(Arc::new(hm));
assert_eq!(
overlay.get(&1).unwrap(),
Wrapped::new(&[10.0, 20.0], source)
);
assert_eq!(overlay.get(&2).unwrap(), Wrapped::new(&[30.0], source));
assert!(overlay.get(&3).is_none());
assert!(overlay.contains_key(&1));
assert!(!overlay.contains_key(&3));
}
}
#[test]
fn builder_unseeded_creates_empty_map() {
let ws: TestMap = Builder::new(Capacity::Default).build(32);
assert!(!ws.contains_key(&1));
let view = ws.view();
assert!(view.get(1).is_none());
}
#[test]
fn builder_as_working_set() {
let ws: TestMap = Builder::new(Capacity::Default).as_working_set(32);
assert!(!ws.contains_key(&1));
let view = ws.view();
assert!(view.get(1).is_none());
}
#[test]
fn overlay_from_batch_empty() {
let batch = Arc::new(Matrix::try_from(Box::new([]), 0, 2).unwrap());
let overlay = Overlay::<u32, Ref<[f32]>>::from_batch(batch, std::iter::empty());
assert!(overlay.get(&0).is_none());
assert!(!overlay.contains_key(&0));
}
#[test]
fn overlay_from_batch_single_element() {
let batch = Arc::new(Matrix::try_from(Box::new([1.0, 2.0]), 1, 2).unwrap());
let overlay = Overlay::<u32, Ref<[f32]>>::from_batch(batch, [42u32]);
assert_eq!(overlay.get(&42).unwrap(), &[1.0, 2.0]);
}
#[test]
fn overlay_from_batch_duplicate_ids_last_wins() {
let batch = Arc::new(test_matrix()); let overlay = Overlay::<u32, Ref<[f32]>>::from_batch(batch, [10u32, 10, 10]);
assert_eq!(overlay.get(&10).unwrap(), &[5.0, 6.0]);
}
#[test]
#[expect(clippy::clone_on_copy)]
fn ref_clone() {
let r = Ref::<[f32]>(std::marker::PhantomData);
let _ = r.clone();
}
#[test]
#[expect(clippy::clone_on_copy)]
fn reborrowed_clone() {
let r = Reborrowed::<Box<[f32]>>(std::marker::PhantomData);
let _ = r.clone();
}
#[test]
#[expect(clippy::clone_on_copy, reason = "explicitly testing Clone impl")]
fn as_reborrowed_clone() {
let value: Box<[f32]> = Box::new([3.0, 4.0]);
let ar = AsReborrowed(&*value);
let _ = ar.clone();
}
fn fill_provider() -> crate::graph::test::provider::Provider {
use crate::graph::test::synthetic::Grid;
crate::graph::test::provider::Provider::grid(Grid::One, 5).unwrap()
}
#[tokio::test(flavor = "current_thread")]
async fn fill_happy_path() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
let current = map.generation;
let view = map
.fill(&mut accessor, [0u32, 1, 2].into_iter())
.await
.unwrap();
assert_eq!(view.get(0).unwrap(), &[0.0]);
assert_eq!(view.get(1).unwrap(), &[1.0]);
assert_eq!(view.get(2).unwrap(), &[2.0]);
assert!(view.get(99).is_none());
assert_eq!(
map.generation,
current + 1,
"`fill` should bump the generation"
);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_clears_previous_entries() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: Map<u32, Box<[f32]>, Ref<[f32]>> = Builder::new(Capacity::None).build(0);
let view = map
.fill(&mut accessor, [0u32, 1].into_iter())
.await
.unwrap();
assert!(view.get(0).is_some());
assert!(view.get(1).is_some());
assert!(view.get(2).is_none());
let view = map.fill(&mut accessor, [2u32].into_iter()).await.unwrap();
assert!(view.get(0).is_none(), "fill should have cleared id 0");
assert!(view.get(1).is_none(), "fill should have cleared id 1");
assert_eq!(view.get(2).unwrap(), &[2.0]);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_with_preserves_entries() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
let view = map
.fill_with(&mut accessor, [0u32].into_iter(), |e| e.into())
.await
.unwrap();
assert!(view.get(0).is_some());
let view = map
.fill_with(&mut accessor, [1u32].into_iter(), |e| e.into())
.await
.unwrap();
assert_eq!(
view.get(0).unwrap(),
&[0.0],
"fill_with should preserve id 0"
);
assert_eq!(view.get(1).unwrap(), &[1.0]);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_skips_transient_errors() {
let provider = fill_provider();
let mut accessor =
TestAccessor::flaky(&provider, Cow::Owned(std::collections::HashSet::from([1])));
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
let view = map
.fill(&mut accessor, [0u32, 1, 2].into_iter())
.await
.unwrap();
assert_eq!(view.get(0).unwrap(), &[0.0]);
assert!(view.get(1).is_none(), "transient ID should be absent");
assert_eq!(view.get(2).unwrap(), &[2.0]);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_propagates_critical_errors() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
let err = map
.fill(&mut accessor, [0u32, 99].into_iter())
.await
.unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("99"),
"error should mention the invalid id: {msg}"
);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_with_skips_occupied_entries() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
map.insert(0, Box::new([99.0]));
let view = map
.fill_with(&mut accessor, [0u32, 1].into_iter(), |e| e.into())
.await
.unwrap();
assert_eq!(
view.get(0).unwrap(),
&[99.0],
"occupied entry should be preserved"
);
assert_eq!(view.get(1).unwrap(), &[1.0]);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_with_skips_seeded_entries() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let batch = Arc::new(Matrix::try_from(Box::new([99.0, 88.0]), 2, 1).unwrap());
let overlay = Overlay::<u32, Ref<[f32]>>::from_batch(batch, [0u32, 1].into_iter());
let mut map = seeded_map(overlay, Capacity::Unbounded);
let view = map
.fill_with(&mut accessor, [0u32, 2].into_iter(), |e| e.into())
.await
.unwrap();
assert_eq!(view.get(0).unwrap(), &[99.0]);
assert_eq!(view.get(2).unwrap(), &[2.0]);
assert!(
map.get(&0).is_none(),
"ID 0 should only be in the seed, not the fill layer"
);
}
#[tokio::test(flavor = "current_thread")]
async fn fill_empty_iterator() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
let view = map.fill(&mut accessor, std::iter::empty()).await.unwrap();
assert!(view.get(0).is_none());
}
#[tokio::test(flavor = "current_thread")]
async fn blanket_fill_trait() {
let provider = fill_provider();
let mut accessor = TestAccessor::new(&provider);
let mut map: TestMap = Builder::new(Capacity::Unbounded).build(0);
let view = <_ as Fill<TestMap>>::fill(&mut accessor, &mut map, [0u32, 1, 2].into_iter())
.await
.unwrap();
assert_eq!(view.get(0).unwrap(), &[0.0]);
assert_eq!(view.get(1).unwrap(), &[1.0]);
assert_eq!(view.get(2).unwrap(), &[2.0]);
}
}