pub trait NodeFetcher {
fn dim(&self) -> usize;
fn prefetch_batch(&mut self, indices: &[u32]);
fn fetch_fp32(&mut self, idx: u32) -> Option<Vec<f32>>;
}
pub struct InMemoryFetcher {
dim: usize,
vectors: Vec<Vec<f32>>,
}
impl InMemoryFetcher {
pub fn new(dim: usize, vectors: Vec<Vec<f32>>) -> Self {
Self { dim, vectors }
}
pub fn from_slice(dim: usize, vectors: &[Vec<f32>]) -> Self {
Self {
dim,
vectors: vectors.to_vec(),
}
}
pub fn len(&self) -> usize {
self.vectors.len()
}
pub fn is_empty(&self) -> bool {
self.vectors.is_empty()
}
}
impl NodeFetcher for InMemoryFetcher {
fn dim(&self) -> usize {
self.dim
}
fn prefetch_batch(&mut self, _indices: &[u32]) {}
fn fetch_fp32(&mut self, idx: u32) -> Option<Vec<f32>> {
self.vectors.get(idx as usize).cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn in_memory_fetcher_returns_correct_vector() {
let vecs = vec![
vec![1.0_f32, 2.0, 3.0],
vec![4.0_f32, 5.0, 6.0],
vec![7.0_f32, 8.0, 9.0],
];
let mut f = InMemoryFetcher::new(3, vecs.clone());
assert_eq!(f.dim(), 3);
assert_eq!(f.fetch_fp32(0), Some(vecs[0].clone()));
assert_eq!(f.fetch_fp32(2), Some(vecs[2].clone()));
}
#[test]
fn in_memory_fetcher_oob_returns_none() {
let mut f = InMemoryFetcher::new(2, vec![vec![1.0, 2.0]]);
assert!(f.fetch_fp32(1).is_none());
assert!(f.fetch_fp32(100).is_none());
}
#[test]
fn in_memory_fetcher_empty() {
let mut f = InMemoryFetcher::new(4, vec![]);
assert!(f.is_empty());
assert!(f.fetch_fp32(0).is_none());
}
#[test]
fn prefetch_batch_is_noop() {
let mut f = InMemoryFetcher::new(2, vec![vec![1.0, 2.0], vec![3.0, 4.0]]);
f.prefetch_batch(&[0, 1]);
assert_eq!(f.fetch_fp32(0), Some(vec![1.0, 2.0]));
}
}