velesdb_core/
vector_ref.rs

1//! Zero-copy vector reference abstraction.
2//!
3//! This module provides the `VectorRef` trait for zero-copy access to vectors,
4//! eliminating heap allocations during search operations.
5//!
6//! # Performance
7//!
8//! Using `VectorRef` instead of `Vec<f32>` eliminates:
9//! - **Heap allocations**: 0 allocations per read vs ~10k for 10k vector search
10//! - **Memory copies**: Direct slice access from mmap
11//! - **Allocator pressure**: No fragmentation from repeated alloc/dealloc
12//!
13//! # EPIC-B: TS-MEM-001, TS-MEM-002
14
15use std::borrow::Cow;
16use std::ops::Deref;
17
18/// A reference to vector data that may be borrowed or owned.
19///
20/// This trait abstracts over different ways to access vector data:
21/// - `&[f32]`: Direct slice reference (zero-copy from mmap)
22/// - `Cow<[f32]>`: Copy-on-write for flexibility
23/// - `Vec<f32>`: Owned data when needed
24///
25/// # Example
26///
27/// ```rust,ignore
28/// use velesdb_core::VectorRef;
29///
30/// fn compute_distance<V: VectorRef>(a: &V, b: &V) -> f32 {
31///     let a_slice = a.as_slice();
32///     let b_slice = b.as_slice();
33///     // SIMD distance calculation on slices
34///     crate::simd::cosine_similarity_fast(a_slice, b_slice)
35/// }
36/// ```
37pub trait VectorRef {
38    /// Returns the vector data as a slice.
39    fn as_slice(&self) -> &[f32];
40
41    /// Returns the dimension of the vector.
42    fn dimension(&self) -> usize {
43        self.as_slice().len()
44    }
45
46    /// Returns true if the vector is empty.
47    fn is_empty(&self) -> bool {
48        self.as_slice().is_empty()
49    }
50}
51
52// ============================================================================
53// Implementations for common types
54// ============================================================================
55
56impl VectorRef for [f32] {
57    #[inline]
58    fn as_slice(&self) -> &[f32] {
59        self
60    }
61}
62
63impl VectorRef for Vec<f32> {
64    #[inline]
65    fn as_slice(&self) -> &[f32] {
66        self
67    }
68}
69
70impl VectorRef for &[f32] {
71    #[inline]
72    fn as_slice(&self) -> &[f32] {
73        self
74    }
75}
76
77impl VectorRef for Cow<'_, [f32]> {
78    #[inline]
79    fn as_slice(&self) -> &[f32] {
80        self
81    }
82}
83
84/// A borrowed vector reference with explicit lifetime.
85///
86/// This is useful when you need to return a reference from a function
87/// while keeping the source locked.
88#[derive(Debug, Clone, Copy)]
89pub struct BorrowedVector<'a> {
90    data: &'a [f32],
91}
92
93impl<'a> BorrowedVector<'a> {
94    /// Creates a new borrowed vector reference.
95    #[inline]
96    #[must_use]
97    pub const fn new(data: &'a [f32]) -> Self {
98        Self { data }
99    }
100
101    /// Returns the underlying slice.
102    #[inline]
103    #[must_use]
104    pub const fn data(&self) -> &'a [f32] {
105        self.data
106    }
107}
108
109impl VectorRef for BorrowedVector<'_> {
110    #[inline]
111    fn as_slice(&self) -> &[f32] {
112        self.data
113    }
114}
115
116impl Deref for BorrowedVector<'_> {
117    type Target = [f32];
118
119    #[inline]
120    fn deref(&self) -> &Self::Target {
121        self.data
122    }
123}
124
125impl AsRef<[f32]> for BorrowedVector<'_> {
126    #[inline]
127    fn as_ref(&self) -> &[f32] {
128        self.data
129    }
130}
131
132/// Guard that holds a read lock and provides vector access.
133///
134/// This is used for zero-copy access from storage while holding the lock.
135/// The guard ensures the underlying data remains valid.
136pub struct VectorGuard<'a, G> {
137    /// The lock guard (kept alive to hold the lock)
138    _guard: G,
139    /// Pointer to the vector data
140    data: &'a [f32],
141}
142
143impl<'a, G> VectorGuard<'a, G> {
144    /// Creates a new vector guard.
145    ///
146    /// # Safety
147    ///
148    /// The `data` pointer must remain valid as long as `guard` is held.
149    /// This is enforced by the lifetime parameter.
150    #[must_use]
151    pub const fn new(guard: G, data: &'a [f32]) -> Self {
152        Self {
153            _guard: guard,
154            data,
155        }
156    }
157}
158
159impl<G> VectorRef for VectorGuard<'_, G> {
160    #[inline]
161    fn as_slice(&self) -> &[f32] {
162        self.data
163    }
164}
165
166impl<G> Deref for VectorGuard<'_, G> {
167    type Target = [f32];
168
169    #[inline]
170    fn deref(&self) -> &Self::Target {
171        self.data
172    }
173}
174
175impl<G> AsRef<[f32]> for VectorGuard<'_, G> {
176    #[inline]
177    fn as_ref(&self) -> &[f32] {
178        self.data
179    }
180}
181
182// ============================================================================
183// TDD TESTS
184// ============================================================================
185
186#[cfg(test)]
187#[allow(clippy::float_cmp)]
188mod tests {
189    use super::*;
190
191    // -------------------------------------------------------------------------
192    // VectorRef trait tests
193    // -------------------------------------------------------------------------
194
195    #[test]
196    fn test_vector_ref_slice() {
197        // Arrange
198        let data: &[f32] = &[1.0, 2.0, 3.0];
199
200        // Act & Assert
201        assert_eq!(data.as_slice(), &[1.0, 2.0, 3.0]);
202        assert_eq!(data.dimension(), 3);
203        assert!(!data.is_empty());
204    }
205
206    #[test]
207    fn test_vector_ref_vec() {
208        // Arrange
209        let data: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0];
210
211        // Act & Assert
212        assert_eq!(data.as_slice(), &[1.0, 2.0, 3.0, 4.0]);
213        assert_eq!(data.dimension(), 4);
214    }
215
216    #[test]
217    fn test_vector_ref_cow_borrowed() {
218        // Arrange
219        let original = vec![1.0, 2.0];
220        let cow: Cow<[f32]> = Cow::Borrowed(&original);
221
222        // Act & Assert
223        assert_eq!(cow.as_slice(), &[1.0, 2.0]);
224        assert_eq!(cow.dimension(), 2);
225    }
226
227    #[test]
228    fn test_vector_ref_cow_owned() {
229        // Arrange
230        let cow: Cow<[f32]> = Cow::Owned(vec![1.0, 2.0, 3.0]);
231
232        // Act & Assert
233        assert_eq!(cow.as_slice(), &[1.0, 2.0, 3.0]);
234    }
235
236    #[test]
237    fn test_vector_ref_empty() {
238        // Arrange
239        let data: &[f32] = &[];
240
241        // Act & Assert
242        assert!(data.is_empty());
243        assert_eq!(data.dimension(), 0);
244    }
245
246    // -------------------------------------------------------------------------
247    // BorrowedVector tests
248    // -------------------------------------------------------------------------
249
250    #[test]
251    fn test_borrowed_vector_new() {
252        // Arrange
253        let data = [1.0f32, 2.0, 3.0];
254
255        // Act
256        let borrowed = BorrowedVector::new(&data);
257
258        // Assert
259        assert_eq!(borrowed.data(), &[1.0, 2.0, 3.0]);
260        assert_eq!(borrowed.dimension(), 3);
261    }
262
263    #[test]
264    fn test_borrowed_vector_deref() {
265        // Arrange
266        let data = [1.0f32, 2.0, 3.0];
267        let borrowed = BorrowedVector::new(&data);
268
269        // Act - use Deref trait
270        let sum: f32 = borrowed.iter().sum();
271
272        // Assert
273        assert_eq!(sum, 6.0);
274    }
275
276    #[test]
277    fn test_borrowed_vector_as_ref() {
278        // Arrange
279        let data = [1.0f32, 2.0];
280        let borrowed = BorrowedVector::new(&data);
281
282        // Act
283        let slice: &[f32] = borrowed.as_ref();
284
285        // Assert
286        assert_eq!(slice, &[1.0, 2.0]);
287    }
288
289    // -------------------------------------------------------------------------
290    // VectorGuard tests
291    // -------------------------------------------------------------------------
292
293    #[test]
294    fn test_vector_guard_basic() {
295        // Arrange - simulate a lock guard with a simple value
296        let data = [1.0f32, 2.0, 3.0, 4.0];
297        let guard = (); // Dummy guard
298
299        // Act
300        let vector_guard = VectorGuard::new(guard, &data);
301
302        // Assert
303        assert_eq!(vector_guard.as_slice(), &[1.0, 2.0, 3.0, 4.0]);
304        assert_eq!(vector_guard.dimension(), 4);
305    }
306
307    #[test]
308    fn test_vector_guard_deref() {
309        // Arrange
310        let data = [1.0f32, 2.0, 3.0];
311        let guard = VectorGuard::new((), &data);
312
313        // Act - use Deref to iterate
314        let max = guard.iter().copied().fold(f32::NEG_INFINITY, f32::max);
315
316        // Assert
317        assert_eq!(max, 3.0);
318    }
319
320    #[test]
321    fn test_vector_guard_with_real_lock() {
322        use parking_lot::RwLock;
323
324        // Arrange - use a real RwLock with static data
325        static DATA: [f32; 3] = [1.0, 2.0, 3.0];
326        let lock = RwLock::new(());
327
328        // Act - create guard that holds the lock
329        let read_guard = lock.read();
330        let vector_guard = VectorGuard::new(read_guard, &DATA);
331
332        // Assert - can access data through guard
333        assert_eq!(vector_guard.as_slice(), &[1.0, 2.0, 3.0]);
334        // Lock is held until vector_guard is dropped
335    }
336
337    // -------------------------------------------------------------------------
338    // Generic function tests
339    // -------------------------------------------------------------------------
340
341    fn generic_sum<V: VectorRef>(v: &V) -> f32 {
342        v.as_slice().iter().sum()
343    }
344
345    #[test]
346    fn test_generic_function_with_slice() {
347        let data: &[f32] = &[1.0, 2.0, 3.0];
348        assert_eq!(generic_sum(&data), 6.0);
349    }
350
351    #[test]
352    fn test_generic_function_with_vec() {
353        let data = vec![1.0f32, 2.0, 3.0, 4.0];
354        assert_eq!(generic_sum(&data), 10.0);
355    }
356
357    #[test]
358    fn test_generic_function_with_borrowed() {
359        let data = [1.0f32, 2.0];
360        let borrowed = BorrowedVector::new(&data);
361        assert_eq!(generic_sum(&borrowed), 3.0);
362    }
363
364    #[test]
365    fn test_generic_function_with_cow() {
366        let cow: Cow<[f32]> = Cow::Owned(vec![1.0, 2.0, 3.0]);
367        assert_eq!(generic_sum(&cow), 6.0);
368    }
369}