nodedb_vector/segment_backing/
plain.rs1use std::sync::Arc;
7
8use crate::mmap_segment::MmapVectorSegment;
9
10use super::VectorSegmentBacking;
11
12pub struct PlainMmapBacking {
43 inner: Arc<MmapVectorSegment>,
44}
45
46impl PlainMmapBacking {
47 pub fn new(seg: MmapVectorSegment) -> Self {
49 Self {
50 inner: Arc::new(seg),
51 }
52 }
53
54 pub fn from_arc(seg: Arc<MmapVectorSegment>) -> Self {
60 Self { inner: seg }
61 }
62
63 pub fn segment(&self) -> &Arc<MmapVectorSegment> {
65 &self.inner
66 }
67}
68
69impl VectorSegmentBacking for PlainMmapBacking {
70 #[inline]
71 fn len(&self) -> usize {
72 self.inner.count()
73 }
74
75 #[inline]
76 fn dim(&self) -> usize {
77 self.inner.dim()
78 }
79
80 #[inline]
81 fn get_vector(&self, id: u32) -> Option<&[f32]> {
82 self.inner.get_vector(id)
83 }
84
85 #[inline]
86 fn get_surrogate(&self, id: u32) -> Option<u64> {
87 self.inner.get_surrogate_id(id)
88 }
89
90 #[inline]
91 fn prefetch(&self, id: u32) {
92 self.inner.prefetch(id);
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use tempfile::tempdir;
99
100 use super::*;
101 use crate::mmap_segment::MmapVectorSegment;
102 use crate::test_support::test_memory;
103
104 fn make_backing(dim: usize, vecs: &[Vec<f32>]) -> PlainMmapBacking {
105 let dir = tempdir().unwrap();
106
107 let refs: Vec<&[f32]> = vecs.iter().map(|v| v.as_slice()).collect();
108 let surrogates: Vec<u64> = (0..vecs.len() as u64).collect();
109
110 let seg = MmapVectorSegment::create_with_surrogates(
111 dir.path(),
112 "test.ndvs",
113 dim,
114 &refs,
115 &surrogates,
116 &test_memory(),
117 )
118 .unwrap();
119
120 drop(dir);
124
125 PlainMmapBacking::new(seg)
126 }
127
128 #[test]
129 fn plain_backing_basic_roundtrip() {
130 let dim = 4;
131 let vecs = vec![
132 vec![1.0_f32, 2.0, 3.0, 4.0],
133 vec![5.0_f32, 6.0, 7.0, 8.0],
134 vec![9.0_f32, 10.0, 11.0, 12.0],
135 ];
136
137 let backing = make_backing(dim, &vecs);
138
139 assert_eq!(backing.len(), 3);
140 assert_eq!(backing.dim(), 4);
141 assert!(!backing.is_empty());
142
143 for (i, expected) in vecs.iter().enumerate() {
144 let got = backing
145 .get_vector(i as u32)
146 .expect("vector must be present");
147 assert_eq!(got, expected.as_slice(), "vector {i} mismatch");
148
149 let sid = backing
150 .get_surrogate(i as u32)
151 .expect("surrogate must be present");
152 assert_eq!(sid, i as u64, "surrogate {i} mismatch");
153 }
154
155 backing.prefetch(0);
157 backing.prefetch(1);
158 backing.prefetch(2);
159 }
160
161 #[test]
163 fn plain_backing_is_send_sync() {
164 fn assert_send_sync<T: Send + Sync>(_: &T) {}
165
166 let dir = tempdir().unwrap();
167 let seg = MmapVectorSegment::create(
168 dir.path(),
169 "check.ndvs",
170 2,
171 &[&[1.0_f32, 2.0]],
172 &test_memory(),
173 )
174 .unwrap();
175 let backing = PlainMmapBacking::new(seg);
176
177 assert_send_sync(&backing);
178 }
179
180 #[test]
181 fn plain_backing_out_of_bounds_returns_none() {
182 let backing = make_backing(3, &[vec![1.0_f32, 2.0, 3.0]]);
183
184 assert!(
185 backing.get_vector(1).is_none(),
186 "id=1 must be out of bounds"
187 );
188 assert!(
189 backing.get_surrogate(1).is_none(),
190 "id=1 surrogate must be out of bounds"
191 );
192 backing.prefetch(1);
194 }
195
196 #[test]
197 fn plain_backing_empty_segment() {
198 let dir = tempdir().unwrap();
199 let seg =
200 MmapVectorSegment::create(dir.path(), "empty.ndvs", 4, &[], &test_memory()).unwrap();
201 let backing = PlainMmapBacking::new(seg);
202
203 assert_eq!(backing.len(), 0);
204 assert!(backing.is_empty());
205 assert!(backing.get_vector(0).is_none());
206 assert!(backing.get_surrogate(0).is_none());
207 backing.prefetch(0);
208 }
209}