Skip to main content

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);