vecdb/variants/raw/zerocopy/mod.rs
1use rawdb::Reader;
2
3use crate::{AnyStoredVec, Format, HEADER_OFFSET, VecIndex, impl_vec_wrapper};
4
5use super::RawVecInner;
6
7mod iterators;
8mod strategy;
9mod value;
10
11pub use iterators::*;
12pub use strategy::*;
13pub use value::*;
14
15/// Raw storage vector using zerocopy for direct memory mapping in native byte order.
16///
17/// Uses the `zerocopy` crate for direct memory-mapped access without copying, providing
18/// the fastest possible performance. Values are stored in **NATIVE byte order**.
19///
20/// Like `BytesVec`, this wraps `RawVecInner` and supports:
21/// - Holes (deleted indices)
22/// - Updated values (modifications to stored data)
23/// - Push/rollback operations
24///
25/// The only difference from `BytesVec` is the serialization strategy:
26/// - `ZeroCopyVec`: Native byte order, faster but not portable
27/// - `BytesVec`: Explicit little-endian, portable across architectures
28///
29/// # Portability Warning
30///
31/// **NOT portable across systems with different endianness.** Data written on a
32/// little-endian system (x86) cannot be read correctly on a big-endian system.
33/// For portable storage, use `BytesVec` instead.
34///
35/// Use `ZeroCopyVec` when:
36/// - Maximum performance is critical
37/// - Data stays on the same architecture
38///
39/// Use `BytesVec` when:
40/// - Cross-platform compatibility is needed
41/// - Sharing data between different architectures
42#[derive(Debug, Clone)]
43#[must_use = "Vector should be stored to keep data accessible"]
44pub struct ZeroCopyVec<I, T>(pub(crate) RawVecInner<I, T, ZeroCopyStrategy<T>>);
45
46impl<I, T> ZeroCopyVec<I, T>
47where
48 I: VecIndex,
49 T: ZeroCopyVecValue,
50{
51 /// The size of T in bytes.
52 pub const SIZE_OF_T: usize = size_of::<T>();
53
54 /// Returns a reference to the value directly from the memory-mapped file without copying.
55 /// Very efficient for large types or frequent reads.
56 ///
57 /// Returns `None` if:
58 /// - Index is marked as a hole (deleted)
59 /// - Index is beyond stored length (might be in pushed layer)
60 /// - Index has an updated value (in the updated map, not on disk)
61 #[inline]
62 pub fn read_ref<'a>(&self, index: I, reader: &'a Reader) -> Option<&'a T> {
63 self.read_ref_at(index.to_usize(), reader)
64 }
65
66 /// Returns a reference to the value at the given usize index directly from the memory-mapped file.
67 #[inline]
68 pub fn read_ref_at<'a>(&self, index: usize, reader: &'a Reader) -> Option<&'a T> {
69 // Cannot return ref for holes
70 if !self.holes().is_empty() && self.holes().contains(&index) {
71 return None;
72 }
73
74 let stored_len = self.stored_len();
75
76 // Cannot return ref for pushed values (they're in a Vec, not mmap)
77 if index >= stored_len {
78 return None;
79 }
80
81 // Cannot return ref for updated values (they're in a BTreeMap, not mmap)
82 if !self.updated().is_empty() && self.updated().contains_key(&index) {
83 return None;
84 }
85
86 self.unchecked_read_ref_at(index, reader)
87 }
88
89 /// Returns a reference without bounds or hole checking.
90 ///
91 /// # Safety
92 /// Caller must ensure index is within stored bounds and not in holes or updated map.
93 #[inline]
94 pub fn unchecked_read_ref_at<'a>(&self, index: usize, reader: &'a Reader) -> Option<&'a T> {
95 let offset = (index * Self::SIZE_OF_T) + HEADER_OFFSET;
96 let bytes = reader.prefixed(offset);
97 T::ref_from_prefix(bytes).map(|(v, _)| v).ok()
98 }
99}
100
101impl_vec_wrapper!(
102 ZeroCopyVec,
103 RawVecInner<I, T, ZeroCopyStrategy<T>>,
104 ZeroCopyVecValue,
105 ZeroCopyVecIterator,
106 CleanZeroCopyVecIterator,
107 DirtyZeroCopyVecIterator,
108 Format::ZeroCopy
109);