1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Zero-copy guard for vector data from mmap storage.
//!
//! # Choosing between `as_slice()` / `try_deref()` and `Deref` / `AsRef`
//!
//! In **fallible contexts** (anything that returns `Result`), prefer
//! [`VectorSliceGuard::as_slice()`] or its alias [`VectorSliceGuard::try_deref()`].
//! They return `Result<&[f32]>` and let callers propagate epoch-mismatch errors
//! gracefully.
//!
//! The `Deref` and `AsRef<[f32]>` implementations exist for ergonomics in
//! contexts where panicking on epoch mismatch is acceptable (e.g., short-lived
//! guards within a single function scope where remap cannot happen).
use MmapMut;
use RwLockReadGuard;
/// Zero-copy guard for vector data from mmap storage.
///
/// This guard holds a read lock on the mmap and provides direct access
/// to the vector data without any memory allocation or copy.
///
/// # Performance
///
/// Using `VectorSliceGuard` instead of `retrieve()` eliminates:
/// - Heap allocation for the result `Vec<f32>`
/// - Memory copy from mmap to the new vector
///
/// # Example
///
/// ```rust,no_run
/// # use velesdb_core::storage::{MmapStorage, VectorSliceGuard};
/// # use std::io;
/// # fn example() -> io::Result<()> {
/// # let mut storage = MmapStorage::new("/tmp/test", 128)?;
/// # let id = 1u64;
/// // Get zero-copy access to a vector
/// let guard: Option<VectorSliceGuard> = storage.retrieve_ref(id)?;
/// if let Some(guard) = guard {
/// // Prefer as_slice() / try_deref() in fallible contexts:
/// if let Ok(slice) = guard.as_slice() {
/// // use slice...
/// }
/// // Or use Deref in short-lived, non-fallible scopes:
/// let slice: &[f32] = &*guard;
/// }
/// # Ok(())
/// # }
/// ```
use AtomicU64;
/// Zero-copy guard for vector data from mmap storage.
/// Holds a read-lock on the mmap and validates that the underlying mapping
/// hasn't been remapped via an *epoch* counter.
///
/// # Epoch Validation
///
/// The guard captures the epoch at creation and validates it on each access.
/// If the mmap was remapped (epoch changed), access panics to prevent UB.
///
/// The epoch uses wrapping `u64` arithmetic. Overflow is theoretically possible
/// after 2^64 remaps (~584 years at 1B/sec) but practically irrelevant.
// SAFETY: `VectorSliceGuard` is `Send` because it carries read-only mapped data.
// - Condition 1: `_guard` pins the mapping and prevents concurrent remap mutation.
// - Condition 2: Epoch checks reject stale pointers after remap.
// SAFETY: Transferring read-only guard ownership across threads preserves invariants.
unsafe
// SAFETY: `VectorSliceGuard` is `Sync` because shared access is immutable.
// - Condition 1: Exposed data is `&[f32]` only; no mutable alias is produced.
// - Condition 2: Underlying map lifetime is tied to `_guard` and epoch validation.
// SAFETY: Concurrent reads of stable mapped memory are sound.
unsafe