/rust/registry/src/index.crates.io-1949cf8c6b5b557f/surrealmx-0.15.0/src/versions.rs
Line | Count | Source |
1 | | use crate::version::Version; |
2 | | use bytes::Bytes; |
3 | | use smallvec::SmallVec; |
4 | | |
5 | | pub(crate) enum IndexOrUpdate<'a> { |
6 | | /// No need to insert the entry or update |
7 | | Ignore, |
8 | | /// Insert the entry at the specified index |
9 | | Index(usize), |
10 | | /// Update an entry with the specified entry |
11 | | Update(&'a mut Version), |
12 | | } |
13 | | |
14 | | pub struct Versions { |
15 | | inner: SmallVec<[Version; 4]>, |
16 | | } |
17 | | |
18 | | impl From<Version> for Versions { |
19 | 4.24k | fn from(value: Version) -> Self { |
20 | 4.24k | let mut inner = SmallVec::new(); |
21 | 4.24k | inner.push(value); |
22 | 4.24k | Versions { |
23 | 4.24k | inner, |
24 | 4.24k | } |
25 | 4.24k | } |
26 | | } |
27 | | |
28 | | impl Versions { |
29 | | /// Create a new versions object. |
30 | | #[inline] |
31 | 0 | pub(crate) fn new() -> Self { |
32 | 0 | Versions { |
33 | 0 | inner: SmallVec::new(), |
34 | 0 | } |
35 | 0 | } |
36 | | |
37 | | /// Appends or inserts an element into its sorted position. |
38 | | #[inline] |
39 | 675 | pub(crate) fn push(&mut self, value: Version) { |
40 | | // Fast path: check if appending to the end |
41 | 675 | if let Some(last) = self.inner.last_mut() { |
42 | | // Compare the new value with the last value |
43 | 675 | match value.version.cmp(&last.version) { |
44 | | std::cmp::Ordering::Greater => { |
45 | | // Newer version - append if value is different |
46 | 675 | if value.value != last.value { |
47 | 539 | self.inner.push(value); |
48 | 539 | } |
49 | | // Same value, ignore duplicate |
50 | 675 | return; |
51 | | } |
52 | | std::cmp::Ordering::Equal => { |
53 | | // Same version - update if value is different |
54 | 0 | if value.value != last.value { |
55 | 0 | last.value = value.value; |
56 | 0 | } |
57 | | // Same value, ignore |
58 | 0 | return; |
59 | | } |
60 | 0 | std::cmp::Ordering::Less => { |
61 | 0 | // Older version - fall through to slow path |
62 | 0 | } |
63 | | } |
64 | | } else { |
65 | | // Empty list - push if not a delete |
66 | 0 | if value.value.is_some() { |
67 | 0 | self.inner.push(value); |
68 | 0 | } |
69 | | // Delete on empty list, ignore |
70 | 0 | return; |
71 | | } |
72 | | // Otherwise, use the index or update logic |
73 | 0 | match self.fetch_index_or_update(&value) { |
74 | | // No need to insert or update the entry |
75 | 0 | IndexOrUpdate::Ignore => { |
76 | 0 | // Do nothing |
77 | 0 | } |
78 | | // Insert the entry at the specified index |
79 | 0 | IndexOrUpdate::Index(idx) => { |
80 | 0 | self.inner.insert(idx, value); |
81 | 0 | } |
82 | | // Update an existing entry in the list |
83 | 0 | IndexOrUpdate::Update(entry) => { |
84 | 0 | entry.value = value.value; |
85 | 0 | } |
86 | | } |
87 | 675 | } |
88 | | |
89 | | /// Determine if a new entry should be ignored, inserted, or update an existing entry. |
90 | | /// |
91 | | /// This function works in the following way: |
92 | | /// - Return IndexOrUpdate::Ignore if: |
93 | | /// - The latest entry with a version <= value.version has the same value as the new value |
94 | | /// - The latest entry with a version <= value.version is a delete and the new value is a delete |
95 | | /// - Return IndexOrUpdate::Update(version) if: |
96 | | /// - The new value version is the same as an existing version and we should update the entry |
97 | | /// - Return IndexOrUpdate::Index(index) if: |
98 | | /// - There is no entry with a version <= value.version |
99 | | /// - The new value version is different to the latest entry with a version <= value.version |
100 | | /// |
101 | | #[inline] |
102 | 0 | pub(crate) fn fetch_index_or_update(&mut self, value: &Version) -> IndexOrUpdate<'_> { |
103 | | // Find the index of the item where item.version <= value.version |
104 | 0 | let idx = self.find_index_lte_version(value.version); |
105 | | // If there is no entry with a version <= value.version |
106 | 0 | if idx == 0 { |
107 | | // If this is a delete, ignore it (no point storing initial delete) |
108 | 0 | if value.value.is_none() { |
109 | 0 | return IndexOrUpdate::Ignore; |
110 | 0 | } |
111 | | // Otherwise, insert at the beginning |
112 | 0 | return IndexOrUpdate::Index(0); |
113 | 0 | } |
114 | | // Get the latest entry with version <= value.version |
115 | 0 | if let Some(existing) = self.inner.get_mut(idx - 1) { |
116 | | // Check if the version is the same as an existing version |
117 | 0 | if existing.version == value.version { |
118 | | // Check if the values are the same |
119 | 0 | if existing.value == value.value { |
120 | | // Same version, same value - ignore |
121 | 0 | return IndexOrUpdate::Ignore; |
122 | 0 | } |
123 | | // Same version, different value - update |
124 | 0 | return IndexOrUpdate::Update(existing); |
125 | 0 | } |
126 | | // Different version - check if values are the same |
127 | 0 | if existing.value == value.value { |
128 | | // Latest entry has the same value - ignore |
129 | 0 | return IndexOrUpdate::Ignore; |
130 | 0 | } |
131 | | // Different version, different value - insert |
132 | 0 | return IndexOrUpdate::Index(idx); |
133 | 0 | } |
134 | | // Fallback - should not reach here |
135 | 0 | IndexOrUpdate::Index(idx) |
136 | 0 | } |
137 | | |
138 | | /// An iterator that removes the items and yields them by value. |
139 | | #[inline] |
140 | 365 | pub fn drain<R>(&mut self, range: R) |
141 | 365 | where |
142 | 365 | R: std::ops::RangeBounds<usize>, |
143 | | { |
144 | | // Drain the versions |
145 | 365 | self.inner.drain(range); |
146 | | // Shrink the vec inline |
147 | 365 | self.inner.shrink_to_fit(); |
148 | 365 | } Unexecuted instantiation: <surrealmx::versions::Versions>::drain::<core::ops::range::RangeToInclusive<usize>> <surrealmx::versions::Versions>::drain::<core::ops::range::RangeTo<usize>> Line | Count | Source | 140 | 365 | pub fn drain<R>(&mut self, range: R) | 141 | 365 | where | 142 | 365 | R: std::ops::RangeBounds<usize>, | 143 | | { | 144 | | // Drain the versions | 145 | 365 | self.inner.drain(range); | 146 | | // Shrink the vec inline | 147 | 365 | self.inner.shrink_to_fit(); | 148 | 365 | } |
|
149 | | |
150 | | /// Check if the item at a specific version is a delete. |
151 | | #[inline] |
152 | 241 | pub(crate) fn is_delete(&self, version: usize) -> bool { |
153 | 241 | self.inner.get(version).is_some_and(|v| v.value.is_none()) |
154 | 241 | } |
155 | | |
156 | | /// Find the index of the entry where item.version < version. |
157 | | #[inline] |
158 | 675 | pub(crate) fn find_index_lt_version(&self, version: u64) -> usize { |
159 | | // Check for any existing version |
160 | 675 | if let Some(last) = self.inner.last() { |
161 | | // Check if the version is newer |
162 | 675 | if version > last.version { |
163 | | // Return the index of the last version |
164 | 434 | return self.inner.len(); |
165 | 241 | } |
166 | 0 | } |
167 | | // Check the list length for reverse iteration or binary search |
168 | 241 | if self.inner.len() <= 4 { |
169 | | // Use linear search to find the first element where v.version > version |
170 | 298 | self.inner.iter().rposition(|v| v.version < version).map_or(0, |i| i + 1) |
171 | | } else { |
172 | | // Find the index of the item where item.version <= version |
173 | 448 | self.inner.partition_point(|v| v.version < version) |
174 | | } |
175 | 675 | } |
176 | | |
177 | | /// Find the index of the entry where item.version <= version. |
178 | | #[inline] |
179 | 17.7k | pub(crate) fn find_index_lte_version(&self, version: u64) -> usize { |
180 | | // Check for any existing version |
181 | 17.7k | if let Some(last) = self.inner.last() { |
182 | | // Check if the version is newer |
183 | 17.7k | if version >= last.version { |
184 | | // Return the index of the last version |
185 | 17.7k | return self.inner.len(); |
186 | 0 | } |
187 | 0 | } |
188 | | // Check the list length for reverse iteration or binary search |
189 | 0 | if self.inner.len() <= 4 { |
190 | | // Use linear search to find the first element where v.version > version |
191 | 0 | self.inner.iter().rposition(|v| v.version <= version).map_or(0, |i| i + 1) |
192 | | } else { |
193 | | // Use binary search to find the first element where v.version >= version |
194 | 0 | self.inner.partition_point(|v| v.version <= version) |
195 | | } |
196 | 17.7k | } <surrealmx::versions::Versions>::find_index_lte_version Line | Count | Source | 179 | 14.4k | pub(crate) fn find_index_lte_version(&self, version: u64) -> usize { | 180 | | // Check for any existing version | 181 | 14.4k | if let Some(last) = self.inner.last() { | 182 | | // Check if the version is newer | 183 | 14.4k | if version >= last.version { | 184 | | // Return the index of the last version | 185 | 14.4k | return self.inner.len(); | 186 | 0 | } | 187 | 0 | } | 188 | | // Check the list length for reverse iteration or binary search | 189 | 0 | if self.inner.len() <= 4 { | 190 | | // Use linear search to find the first element where v.version > version | 191 | 0 | self.inner.iter().rposition(|v| v.version <= version).map_or(0, |i| i + 1) | 192 | | } else { | 193 | | // Use binary search to find the first element where v.version >= version | 194 | 0 | self.inner.partition_point(|v| v.version <= version) | 195 | | } | 196 | 14.4k | } |
<surrealmx::versions::Versions>::find_index_lte_version Line | Count | Source | 179 | 3.32k | pub(crate) fn find_index_lte_version(&self, version: u64) -> usize { | 180 | | // Check for any existing version | 181 | 3.32k | if let Some(last) = self.inner.last() { | 182 | | // Check if the version is newer | 183 | 3.32k | if version >= last.version { | 184 | | // Return the index of the last version | 185 | 3.32k | return self.inner.len(); | 186 | 0 | } | 187 | 0 | } | 188 | | // Check the list length for reverse iteration or binary search | 189 | 0 | if self.inner.len() <= 4 { | 190 | | // Use linear search to find the first element where v.version > version | 191 | 0 | self.inner.iter().rposition(|v| v.version <= version).map_or(0, |i| i + 1) | 192 | | } else { | 193 | | // Use binary search to find the first element where v.version >= version | 194 | 0 | self.inner.partition_point(|v| v.version <= version) | 195 | | } | 196 | 3.32k | } |
|
197 | | |
198 | | /// Fetch the entry at a specific version in the versions list. |
199 | | #[inline] |
200 | 17.5k | pub(crate) fn fetch_version(&self, version: u64) -> Option<Bytes> { |
201 | | // Find the index of the item where item.version <= version |
202 | 17.5k | let idx = self.find_index_lte_version(version); |
203 | | // If there is an entry, return the value |
204 | 17.5k | if idx > 0 { |
205 | 17.5k | self.inner.get(idx - 1).and_then(|v| v.value.clone()) |
206 | | } else { |
207 | 0 | None |
208 | | } |
209 | 17.5k | } <surrealmx::versions::Versions>::fetch_version Line | Count | Source | 200 | 14.1k | pub(crate) fn fetch_version(&self, version: u64) -> Option<Bytes> { | 201 | | // Find the index of the item where item.version <= version | 202 | 14.1k | let idx = self.find_index_lte_version(version); | 203 | | // If there is an entry, return the value | 204 | 14.1k | if idx > 0 { | 205 | 14.1k | self.inner.get(idx - 1).and_then(|v| v.value.clone()) | 206 | | } else { | 207 | 0 | None | 208 | | } | 209 | 14.1k | } |
<surrealmx::versions::Versions>::fetch_version Line | Count | Source | 200 | 3.32k | pub(crate) fn fetch_version(&self, version: u64) -> Option<Bytes> { | 201 | | // Find the index of the item where item.version <= version | 202 | 3.32k | let idx = self.find_index_lte_version(version); | 203 | | // If there is an entry, return the value | 204 | 3.32k | if idx > 0 { | 205 | 3.32k | self.inner.get(idx - 1).and_then(|v| v.value.clone()) | 206 | | } else { | 207 | 0 | None | 208 | | } | 209 | 3.32k | } |
|
210 | | |
211 | | /// Check if an entry at a specific version exists and is not a delete. |
212 | | #[inline] |
213 | 291 | pub(crate) fn exists_version(&self, version: u64) -> bool { |
214 | | // Find the index of the item where item.version <= version |
215 | 291 | let idx = self.find_index_lte_version(version); |
216 | | // If there is an entry, return the value |
217 | 291 | if idx > 0 { |
218 | 291 | self.inner.get(idx - 1).is_some_and(|v| v.value.is_some()) |
219 | | } else { |
220 | 0 | false |
221 | | } |
222 | 291 | } <surrealmx::versions::Versions>::exists_version Line | Count | Source | 213 | 291 | pub(crate) fn exists_version(&self, version: u64) -> bool { | 214 | | // Find the index of the item where item.version <= version | 215 | 291 | let idx = self.find_index_lte_version(version); | 216 | | // If there is an entry, return the value | 217 | 291 | if idx > 0 { | 218 | 291 | self.inner.get(idx - 1).is_some_and(|v| v.value.is_some()) | 219 | | } else { | 220 | 0 | false | 221 | | } | 222 | 291 | } |
Unexecuted instantiation: <surrealmx::versions::Versions>::exists_version |
223 | | |
224 | | /// Get all versions as a vector of (version, value) tuples. |
225 | | #[inline] |
226 | 0 | pub(crate) fn all_versions(&self) -> Vec<(u64, Option<Bytes>)> { |
227 | 0 | self.inner.iter().map(|v| (v.version, v.value.clone())).collect() |
228 | 0 | } Unexecuted instantiation: <surrealmx::versions::Versions>::all_versions Unexecuted instantiation: <surrealmx::versions::Versions>::all_versions |
229 | | |
230 | | /// Remove all versions older than the specified version. |
231 | | #[inline] |
232 | 675 | pub(crate) fn gc_older_versions(&mut self, version: u64) -> usize { |
233 | | // Find the index of the item where item.version < version |
234 | 675 | let idx = self.find_index_lt_version(version); |
235 | | // Handle the case where all versions are older than the cutoff |
236 | 675 | if idx >= self.inner.len() { |
237 | | // Check if the last version is a delete |
238 | 434 | if let Some(last) = self.inner.last() { |
239 | 434 | if last.value.is_none() { |
240 | 0 | // Last version is a delete, remove everything |
241 | 0 | self.inner.clear(); |
242 | 434 | } else if idx > 1 { |
243 | 248 | // Last version has data, keep it and remove all others |
244 | 248 | self.drain(..idx - 1); |
245 | 248 | } |
246 | 0 | } |
247 | 241 | } else if self.is_delete(idx) { |
248 | 0 | // Remove all versions up to and including this delete |
249 | 0 | self.drain(..=idx); |
250 | 241 | } else if idx > 0 { |
251 | 117 | // Remove all versions up to this version |
252 | 117 | self.drain(..idx); |
253 | 124 | } |
254 | | // Return the length |
255 | 675 | self.inner.len() |
256 | 675 | } |
257 | | } |
258 | | |
259 | | #[cfg(test)] |
260 | | mod tests { |
261 | | use super::*; |
262 | | use bytes::Bytes; |
263 | | |
264 | | /// Helper function to create a Version from a version number and optional value |
265 | | fn make_version(version: u64, value: Option<&str>) -> Version { |
266 | | Version { |
267 | | version, |
268 | | value: value.map(|s| Bytes::from(s.to_string())), |
269 | | } |
270 | | } |
271 | | |
272 | | /// Helper function to create a Versions instance with the given version tuples |
273 | | fn make_versions(versions: Vec<(u64, Option<&str>)>) -> Versions { |
274 | | let mut v = Versions::new(); |
275 | | for (version, value) in versions { |
276 | | v.push(make_version(version, value)); |
277 | | } |
278 | | v |
279 | | } |
280 | | |
281 | | // ==================== Tests for find_index_lt_version ==================== |
282 | | |
283 | | #[test] |
284 | | fn test_find_index_lt_version_empty() { |
285 | | let versions = Versions::new(); |
286 | | assert_eq!(versions.find_index_lt_version(0), 0); |
287 | | assert_eq!(versions.find_index_lt_version(1), 0); |
288 | | assert_eq!(versions.find_index_lt_version(100), 0); |
289 | | } |
290 | | |
291 | | #[test] |
292 | | fn test_find_index_lt_version_single_version() { |
293 | | let versions = make_versions(vec![(10, Some("value"))]); |
294 | | // Query before the version |
295 | | assert_eq!(versions.find_index_lt_version(5), 0); |
296 | | assert_eq!(versions.find_index_lt_version(9), 0); |
297 | | // Query at the version |
298 | | assert_eq!(versions.find_index_lt_version(10), 0); |
299 | | // Query after the version |
300 | | assert_eq!(versions.find_index_lt_version(11), 1); |
301 | | assert_eq!(versions.find_index_lt_version(100), 1); |
302 | | } |
303 | | |
304 | | #[test] |
305 | | fn test_find_index_lt_version_multiple_versions() { |
306 | | // Create a small list (≤32 elements) to trigger linear search |
307 | | let versions = make_versions(vec![ |
308 | | (10, Some("v1")), |
309 | | (20, Some("v2")), |
310 | | (30, Some("v3")), |
311 | | (40, Some("v4")), |
312 | | (50, Some("v5")), |
313 | | ]); |
314 | | // Query before the first version |
315 | | assert_eq!(versions.find_index_lt_version(0), 0); |
316 | | assert_eq!(versions.find_index_lt_version(5), 0); |
317 | | // Query at the first version |
318 | | assert_eq!(versions.find_index_lt_version(10), 0); |
319 | | // Query after the first version |
320 | | assert_eq!(versions.find_index_lt_version(15), 1); |
321 | | // Query at the second version |
322 | | assert_eq!(versions.find_index_lt_version(20), 1); |
323 | | // Query after the second version |
324 | | assert_eq!(versions.find_index_lt_version(25), 2); |
325 | | assert_eq!(versions.find_index_lt_version(30), 2); |
326 | | // Query at the third version |
327 | | assert_eq!(versions.find_index_lt_version(35), 3); |
328 | | assert_eq!(versions.find_index_lt_version(40), 3); |
329 | | // Query at the fourth version |
330 | | assert_eq!(versions.find_index_lt_version(45), 4); |
331 | | // Query at the fifth version |
332 | | assert_eq!(versions.find_index_lt_version(50), 4); |
333 | | // Query after the fifth version |
334 | | assert_eq!(versions.find_index_lt_version(51), 5); |
335 | | assert_eq!(versions.find_index_lt_version(100), 5); |
336 | | } |
337 | | |
338 | | #[test] |
339 | | fn test_find_index_lt_version_with_deletes() { |
340 | | let versions = make_versions(vec![ |
341 | | (10, Some("v1")), |
342 | | (20, None), // Delete |
343 | | (30, Some("v3")), |
344 | | (40, None), // Delete |
345 | | ]); |
346 | | // Query before the first version |
347 | | assert_eq!(versions.find_index_lt_version(5), 0); |
348 | | assert_eq!(versions.find_index_lt_version(9), 0); |
349 | | // Query at the first version |
350 | | assert_eq!(versions.find_index_lt_version(10), 0); |
351 | | // Query after the first version |
352 | | assert_eq!(versions.find_index_lt_version(15), 1); |
353 | | // Query at the second version |
354 | | assert_eq!(versions.find_index_lt_version(20), 1); |
355 | | // Query after the second version |
356 | | assert_eq!(versions.find_index_lt_version(25), 2); |
357 | | assert_eq!(versions.find_index_lt_version(30), 2); |
358 | | // Query at the third version |
359 | | assert_eq!(versions.find_index_lt_version(35), 3); |
360 | | assert_eq!(versions.find_index_lt_version(40), 3); |
361 | | // Query after the third version |
362 | | assert_eq!(versions.find_index_lt_version(50), 4); |
363 | | } |
364 | | |
365 | | // ==================== Tests for find_index_lte_version ==================== |
366 | | |
367 | | #[test] |
368 | | fn test_find_index_lte_version_empty() { |
369 | | let versions = Versions::new(); |
370 | | assert_eq!(versions.find_index_lte_version(0), 0); |
371 | | assert_eq!(versions.find_index_lte_version(1), 0); |
372 | | assert_eq!(versions.find_index_lte_version(100), 0); |
373 | | } |
374 | | |
375 | | #[test] |
376 | | fn test_find_index_lte_version_single_version() { |
377 | | let versions = make_versions(vec![(10, Some("value"))]); |
378 | | // Query before the version |
379 | | assert_eq!(versions.find_index_lte_version(5), 0); |
380 | | assert_eq!(versions.find_index_lte_version(9), 0); |
381 | | // Query at the version |
382 | | assert_eq!(versions.find_index_lte_version(10), 1); |
383 | | // Query after the version |
384 | | assert_eq!(versions.find_index_lte_version(11), 1); |
385 | | assert_eq!(versions.find_index_lte_version(100), 1); |
386 | | } |
387 | | |
388 | | #[test] |
389 | | fn test_find_index_lte_version_multiple_versions() { |
390 | | // Create a small list (≤32 elements) to trigger linear search |
391 | | let versions = make_versions(vec![ |
392 | | (10, Some("v1")), |
393 | | (20, Some("v2")), |
394 | | (30, Some("v3")), |
395 | | (40, Some("v4")), |
396 | | (50, Some("v5")), |
397 | | ]); |
398 | | // Query before the first version |
399 | | assert_eq!(versions.find_index_lte_version(0), 0); |
400 | | assert_eq!(versions.find_index_lte_version(5), 0); |
401 | | // Query at the first version |
402 | | assert_eq!(versions.find_index_lte_version(10), 1); |
403 | | // Query after the first version |
404 | | assert_eq!(versions.find_index_lte_version(15), 1); |
405 | | // Query at the second version |
406 | | assert_eq!(versions.find_index_lte_version(20), 2); |
407 | | // Query after the second version |
408 | | assert_eq!(versions.find_index_lte_version(25), 2); |
409 | | // Query at the third version |
410 | | assert_eq!(versions.find_index_lte_version(30), 3); |
411 | | // Query after the third version |
412 | | assert_eq!(versions.find_index_lte_version(35), 3); |
413 | | // Query at the fourth version |
414 | | assert_eq!(versions.find_index_lte_version(40), 4); |
415 | | // Query after the fourth version |
416 | | assert_eq!(versions.find_index_lte_version(45), 4); |
417 | | // Query at the fifth version |
418 | | assert_eq!(versions.find_index_lte_version(50), 5); |
419 | | // Query after the fifth version |
420 | | assert_eq!(versions.find_index_lte_version(51), 5); |
421 | | assert_eq!(versions.find_index_lte_version(100), 5); |
422 | | } |
423 | | |
424 | | #[test] |
425 | | fn test_find_index_lte_version_with_deletes() { |
426 | | let versions = make_versions(vec![ |
427 | | (10, Some("v1")), |
428 | | (20, None), // Delete |
429 | | (30, Some("v3")), |
430 | | (40, None), // Delete |
431 | | ]); |
432 | | // Query at the first version |
433 | | assert_eq!(versions.find_index_lte_version(10), 1); |
434 | | // Query after the first version |
435 | | assert_eq!(versions.find_index_lte_version(15), 1); |
436 | | // Query at the second version |
437 | | assert_eq!(versions.find_index_lte_version(20), 2); |
438 | | // Query after the second version |
439 | | assert_eq!(versions.find_index_lte_version(25), 2); |
440 | | // Query at the third version |
441 | | assert_eq!(versions.find_index_lte_version(30), 3); |
442 | | // Query after the third version |
443 | | assert_eq!(versions.find_index_lte_version(35), 3); |
444 | | // Query at the fourth version |
445 | | assert_eq!(versions.find_index_lte_version(40), 4); |
446 | | // Query after the fourth version |
447 | | assert_eq!(versions.find_index_lte_version(50), 4); |
448 | | } |
449 | | |
450 | | #[test] |
451 | | fn test_find_index_lt_vs_lte_difference() { |
452 | | // This test demonstrates the key difference between < and <= |
453 | | let versions = make_versions(vec![ |
454 | | (10, Some("v1")), |
455 | | (20, Some("v2")), |
456 | | (30, Some("v3")), |
457 | | (40, Some("v4")), |
458 | | (50, Some("v5")), |
459 | | ]); |
460 | | // Query at the first version |
461 | | assert_eq!(versions.find_index_lt_version(10), 0); |
462 | | assert_eq!(versions.find_index_lte_version(10), 1); |
463 | | // Query after the first version |
464 | | assert_eq!(versions.find_index_lt_version(15), 1); |
465 | | assert_eq!(versions.find_index_lte_version(15), 1); |
466 | | // Query at the second version |
467 | | assert_eq!(versions.find_index_lt_version(20), 1); |
468 | | assert_eq!(versions.find_index_lte_version(20), 2); |
469 | | // Query at the third version |
470 | | assert_eq!(versions.find_index_lt_version(30), 2); |
471 | | assert_eq!(versions.find_index_lte_version(30), 3); |
472 | | // Query after the third version |
473 | | assert_eq!(versions.find_index_lt_version(35), 3); |
474 | | assert_eq!(versions.find_index_lte_version(35), 3); |
475 | | } |
476 | | |
477 | | // ==================== Tests for fetch_version ==================== |
478 | | |
479 | | #[test] |
480 | | fn test_fetch_version_empty() { |
481 | | let versions = Versions::new(); |
482 | | assert_eq!(versions.fetch_version(0), None); |
483 | | assert_eq!(versions.fetch_version(10), None); |
484 | | assert_eq!(versions.fetch_version(100), None); |
485 | | } |
486 | | |
487 | | #[test] |
488 | | fn test_fetch_version_single_version() { |
489 | | let versions = make_versions(vec![(10, Some("value"))]); |
490 | | // Query before the version |
491 | | assert_eq!(versions.fetch_version(5), None); |
492 | | assert_eq!(versions.fetch_version(9), None); |
493 | | // Query at the version |
494 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("value".to_string()))); |
495 | | // Query after the version |
496 | | assert_eq!(versions.fetch_version(11), Some(Bytes::from("value".to_string()))); |
497 | | assert_eq!(versions.fetch_version(100), Some(Bytes::from("value".to_string()))); |
498 | | } |
499 | | |
500 | | #[test] |
501 | | fn test_fetch_version_multiple_versions() { |
502 | | let versions = make_versions(vec![ |
503 | | (10, Some("v1")), |
504 | | (20, Some("v2")), |
505 | | (30, Some("v3")), |
506 | | (40, Some("v4")), |
507 | | (50, Some("v5")), |
508 | | ]); |
509 | | // Query before the first version |
510 | | assert_eq!(versions.fetch_version(5), None); |
511 | | // Query at the first version |
512 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v1".to_string()))); |
513 | | // Query after the first version |
514 | | assert_eq!(versions.fetch_version(15), Some(Bytes::from("v1".to_string()))); |
515 | | // Query at the second version |
516 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2".to_string()))); |
517 | | // Query after the second version |
518 | | assert_eq!(versions.fetch_version(25), Some(Bytes::from("v2".to_string()))); |
519 | | // Query at the third version |
520 | | assert_eq!(versions.fetch_version(30), Some(Bytes::from("v3".to_string()))); |
521 | | // Query after the third version |
522 | | assert_eq!(versions.fetch_version(35), Some(Bytes::from("v3".to_string()))); |
523 | | // Query at the fourth version |
524 | | assert_eq!(versions.fetch_version(40), Some(Bytes::from("v4".to_string()))); |
525 | | // Query after the fourth version |
526 | | assert_eq!(versions.fetch_version(45), Some(Bytes::from("v4".to_string()))); |
527 | | // Query at the fifth version |
528 | | assert_eq!(versions.fetch_version(50), Some(Bytes::from("v5".to_string()))); |
529 | | // Query after the fifth version |
530 | | assert_eq!(versions.fetch_version(100), Some(Bytes::from("v5".to_string()))); |
531 | | } |
532 | | |
533 | | #[test] |
534 | | fn test_fetch_version_with_deletes() { |
535 | | let versions = make_versions(vec![ |
536 | | (10, Some("v1")), |
537 | | (20, None), // Delete |
538 | | (30, Some("v3")), |
539 | | (40, None), // Delete |
540 | | ]); |
541 | | // Query before the first version |
542 | | assert_eq!(versions.fetch_version(5), None); |
543 | | // Query at the first version |
544 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v1".to_string()))); |
545 | | // Query after the first version |
546 | | assert_eq!(versions.fetch_version(15), Some(Bytes::from("v1".to_string()))); |
547 | | // Query at the second version (delete) |
548 | | assert_eq!(versions.fetch_version(20), None); |
549 | | // Query after the second version (delete) |
550 | | assert_eq!(versions.fetch_version(25), None); |
551 | | // Query at the third version |
552 | | assert_eq!(versions.fetch_version(30), Some(Bytes::from("v3".to_string()))); |
553 | | // Query after the third version |
554 | | assert_eq!(versions.fetch_version(35), Some(Bytes::from("v3".to_string()))); |
555 | | // Query at the fourth version (delete) |
556 | | assert_eq!(versions.fetch_version(40), None); |
557 | | // Query after the fourth version (delete) |
558 | | assert_eq!(versions.fetch_version(50), None); |
559 | | } |
560 | | |
561 | | // ==================== Tests for exists_version ==================== |
562 | | |
563 | | #[test] |
564 | | fn test_exists_version_empty() { |
565 | | let versions = Versions::new(); |
566 | | assert!(!versions.exists_version(0)); |
567 | | assert!(!versions.exists_version(10)); |
568 | | assert!(!versions.exists_version(100)); |
569 | | } |
570 | | |
571 | | #[test] |
572 | | fn test_exists_version_single_version() { |
573 | | let versions = make_versions(vec![(10, Some("value"))]); |
574 | | // Query before the version |
575 | | assert!(!versions.exists_version(5)); |
576 | | assert!(!versions.exists_version(9)); |
577 | | // Query at the version |
578 | | assert!(versions.exists_version(10)); |
579 | | // Query after the version |
580 | | assert!(versions.exists_version(11)); |
581 | | assert!(versions.exists_version(100)); |
582 | | } |
583 | | |
584 | | #[test] |
585 | | fn test_exists_version_multiple_versions() { |
586 | | let versions = make_versions(vec![ |
587 | | (10, Some("v1")), |
588 | | (20, Some("v2")), |
589 | | (30, Some("v3")), |
590 | | (40, Some("v4")), |
591 | | (50, Some("v5")), |
592 | | ]); |
593 | | // Query before the first version |
594 | | assert!(!versions.exists_version(5)); |
595 | | // Query at the first version |
596 | | assert!(versions.exists_version(10)); |
597 | | // Query after the first version |
598 | | assert!(versions.exists_version(15)); |
599 | | // Query at the second version |
600 | | assert!(versions.exists_version(20)); |
601 | | // Query after the second version |
602 | | assert!(versions.exists_version(25)); |
603 | | // Query at the third version |
604 | | assert!(versions.exists_version(30)); |
605 | | // Query after the third version |
606 | | assert!(versions.exists_version(35)); |
607 | | // Query at the fourth version |
608 | | assert!(versions.exists_version(40)); |
609 | | // Query after the fourth version |
610 | | assert!(versions.exists_version(45)); |
611 | | // Query at the fifth version |
612 | | assert!(versions.exists_version(50)); |
613 | | // Query after the fifth version |
614 | | assert!(versions.exists_version(100)); |
615 | | } |
616 | | |
617 | | #[test] |
618 | | fn test_exists_version_with_deletes() { |
619 | | let versions = make_versions(vec![ |
620 | | (10, Some("v1")), |
621 | | (20, None), // Delete |
622 | | (30, Some("v3")), |
623 | | (40, None), // Delete |
624 | | ]); |
625 | | // Query before the first version |
626 | | assert!(!versions.exists_version(5)); |
627 | | // Query at the first version |
628 | | assert!(versions.exists_version(10)); |
629 | | // Query after the first version |
630 | | assert!(versions.exists_version(15)); |
631 | | // Query at the second version (delete) |
632 | | assert!(!versions.exists_version(20)); |
633 | | // Query after the second version (delete) |
634 | | assert!(!versions.exists_version(25)); |
635 | | // Query at the third version |
636 | | assert!(versions.exists_version(30)); |
637 | | // Query after the third version |
638 | | assert!(versions.exists_version(35)); |
639 | | // Query at the fourth version (delete) |
640 | | assert!(!versions.exists_version(40)); |
641 | | // Query after the fourth version (delete) |
642 | | assert!(!versions.exists_version(50)); |
643 | | } |
644 | | |
645 | | // ==================== Tests for push ==================== |
646 | | |
647 | | #[test] |
648 | | fn test_push_to_empty_list() { |
649 | | let mut versions = Versions::new(); |
650 | | // Push a value to empty list |
651 | | versions.push(make_version(10, Some("v1"))); |
652 | | assert_eq!(versions.inner.len(), 1); |
653 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v1".to_string()))); |
654 | | } |
655 | | |
656 | | #[test] |
657 | | fn test_push_delete_to_empty_list() { |
658 | | let mut versions = Versions::new(); |
659 | | // Push a delete (None) to empty list - should not add |
660 | | versions.push(make_version(10, None)); |
661 | | assert_eq!(versions.inner.len(), 0); |
662 | | } |
663 | | |
664 | | #[test] |
665 | | fn test_push_in_order() { |
666 | | let mut versions = Versions::new(); |
667 | | // Push versions in increasing order |
668 | | versions.push(make_version(10, Some("v1"))); |
669 | | versions.push(make_version(20, Some("v2"))); |
670 | | versions.push(make_version(30, Some("v3"))); |
671 | | assert_eq!(versions.inner.len(), 3); |
672 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v1".to_string()))); |
673 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2".to_string()))); |
674 | | assert_eq!(versions.fetch_version(30), Some(Bytes::from("v3".to_string()))); |
675 | | } |
676 | | |
677 | | #[test] |
678 | | fn test_push_duplicate_values() { |
679 | | let mut versions = Versions::new(); |
680 | | // Push first version |
681 | | versions.push(make_version(10, Some("v1"))); |
682 | | assert_eq!(versions.inner.len(), 1); |
683 | | // Push same value at newer version - should be skipped |
684 | | versions.push(make_version(20, Some("v1"))); |
685 | | assert_eq!(versions.inner.len(), 1); |
686 | | // Push different value - should be added |
687 | | versions.push(make_version(30, Some("v2"))); |
688 | | assert_eq!(versions.inner.len(), 2); |
689 | | // Push same value again - should be skipped |
690 | | versions.push(make_version(40, Some("v2"))); |
691 | | assert_eq!(versions.inner.len(), 2); |
692 | | } |
693 | | |
694 | | #[test] |
695 | | fn test_push_out_of_order() { |
696 | | let mut versions = Versions::new(); |
697 | | // Push versions out of order |
698 | | versions.push(make_version(30, Some("v3"))); |
699 | | versions.push(make_version(10, Some("v1"))); |
700 | | versions.push(make_version(20, Some("v2"))); |
701 | | // Should be sorted correctly |
702 | | assert_eq!(versions.inner.len(), 3); |
703 | | assert_eq!(versions.inner[0].version, 10); |
704 | | assert_eq!(versions.inner[1].version, 20); |
705 | | assert_eq!(versions.inner[2].version, 30); |
706 | | } |
707 | | |
708 | | #[test] |
709 | | fn test_push_with_deletes() { |
710 | | let mut versions = Versions::new(); |
711 | | // Push value, then delete, then value again |
712 | | versions.push(make_version(10, Some("v1"))); |
713 | | assert_eq!(versions.inner.len(), 1); |
714 | | // Push delete |
715 | | versions.push(make_version(20, None)); |
716 | | assert_eq!(versions.inner.len(), 2); |
717 | | assert!(!versions.exists_version(20)); |
718 | | // Push new value |
719 | | versions.push(make_version(30, Some("v3"))); |
720 | | assert_eq!(versions.inner.len(), 3); |
721 | | assert!(versions.exists_version(30)); |
722 | | } |
723 | | |
724 | | #[test] |
725 | | fn test_push_same_version_different_value() { |
726 | | let mut versions = Versions::new(); |
727 | | // Push a version |
728 | | versions.push(make_version(10, Some("v1"))); |
729 | | assert_eq!(versions.inner.len(), 1); |
730 | | // Push same version with different value - should update/replace |
731 | | versions.push(make_version(10, Some("v2"))); |
732 | | assert_eq!(versions.inner.len(), 1); |
733 | | // The new value should have replaced the old one |
734 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v2".to_string()))); |
735 | | } |
736 | | |
737 | | #[test] |
738 | | fn test_push_same_version_same_value() { |
739 | | let mut versions = Versions::new(); |
740 | | // Push a version |
741 | | versions.push(make_version(10, Some("v1"))); |
742 | | assert_eq!(versions.inner.len(), 1); |
743 | | // Push same version with same value - should still update (no-op) |
744 | | versions.push(make_version(10, Some("v1"))); |
745 | | assert_eq!(versions.inner.len(), 1); |
746 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v1".to_string()))); |
747 | | } |
748 | | |
749 | | // ==================== Fast Path Tests ==================== |
750 | | |
751 | | #[test] |
752 | | fn test_push_fast_path_append_different_value() { |
753 | | let mut versions = Versions::new(); |
754 | | versions.push(make_version(10, Some("v1"))); |
755 | | versions.push(make_version(20, Some("v2"))); |
756 | | // Fast path: append with different value |
757 | | versions.push(make_version(30, Some("v3"))); |
758 | | assert_eq!(versions.inner.len(), 3); |
759 | | assert_eq!(versions.inner[2].version, 30); |
760 | | assert_eq!(versions.fetch_version(30), Some(Bytes::from("v3".to_string()))); |
761 | | } |
762 | | |
763 | | #[test] |
764 | | fn test_push_fast_path_append_same_value() { |
765 | | let mut versions = Versions::new(); |
766 | | versions.push(make_version(10, Some("v1"))); |
767 | | versions.push(make_version(20, Some("v2"))); |
768 | | // Fast path: append with same value as last - should be ignored |
769 | | versions.push(make_version(30, Some("v2"))); |
770 | | assert_eq!(versions.inner.len(), 2); |
771 | | assert_eq!(versions.fetch_version(30), Some(Bytes::from("v2".to_string()))); |
772 | | } |
773 | | |
774 | | #[test] |
775 | | fn test_push_fast_path_update_last_different_value() { |
776 | | let mut versions = Versions::new(); |
777 | | versions.push(make_version(10, Some("v1"))); |
778 | | versions.push(make_version(20, Some("v2"))); |
779 | | // Fast path: update last version with different value |
780 | | versions.push(make_version(20, Some("v2_updated"))); |
781 | | assert_eq!(versions.inner.len(), 2); |
782 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2_updated".to_string()))); |
783 | | } |
784 | | |
785 | | #[test] |
786 | | fn test_push_fast_path_update_last_same_value() { |
787 | | let mut versions = Versions::new(); |
788 | | versions.push(make_version(10, Some("v1"))); |
789 | | versions.push(make_version(20, Some("v2"))); |
790 | | // Fast path: update last version with same value - no-op |
791 | | versions.push(make_version(20, Some("v2"))); |
792 | | assert_eq!(versions.inner.len(), 2); |
793 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2".to_string()))); |
794 | | } |
795 | | |
796 | | #[test] |
797 | | fn test_push_fast_path_multiple_updates_to_last() { |
798 | | let mut versions = Versions::new(); |
799 | | versions.push(make_version(10, Some("v1"))); |
800 | | // Multiple sequential updates to the same version |
801 | | versions.push(make_version(10, Some("v2"))); |
802 | | versions.push(make_version(10, Some("v3"))); |
803 | | versions.push(make_version(10, Some("v4"))); |
804 | | assert_eq!(versions.inner.len(), 1); |
805 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v4".to_string()))); |
806 | | } |
807 | | |
808 | | #[test] |
809 | | fn test_push_fast_path_alternating_append_update() { |
810 | | let mut versions = Versions::new(); |
811 | | // Append version 10 |
812 | | versions.push(make_version(10, Some("v1"))); |
813 | | // Append version 20 |
814 | | versions.push(make_version(20, Some("v2"))); |
815 | | // Update version 20 |
816 | | versions.push(make_version(20, Some("v2_updated"))); |
817 | | // Append version 30 |
818 | | versions.push(make_version(30, Some("v3"))); |
819 | | // Update version 30 |
820 | | versions.push(make_version(30, Some("v3_updated"))); |
821 | | |
822 | | assert_eq!(versions.inner.len(), 3); |
823 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v1".to_string()))); |
824 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2_updated".to_string()))); |
825 | | assert_eq!(versions.fetch_version(30), Some(Bytes::from("v3_updated".to_string()))); |
826 | | } |
827 | | |
828 | | #[test] |
829 | | fn test_push_slow_path_insert_middle() { |
830 | | let mut versions = Versions::new(); |
831 | | versions.push(make_version(10, Some("v1"))); |
832 | | versions.push(make_version(30, Some("v3"))); |
833 | | // Slow path: insert in the middle (version < last.version) |
834 | | versions.push(make_version(20, Some("v2"))); |
835 | | |
836 | | assert_eq!(versions.inner.len(), 3); |
837 | | assert_eq!(versions.inner[0].version, 10); |
838 | | assert_eq!(versions.inner[1].version, 20); |
839 | | assert_eq!(versions.inner[2].version, 30); |
840 | | } |
841 | | |
842 | | #[test] |
843 | | fn test_push_slow_path_insert_beginning() { |
844 | | let mut versions = Versions::new(); |
845 | | versions.push(make_version(20, Some("v2"))); |
846 | | versions.push(make_version(30, Some("v3"))); |
847 | | // Slow path: insert at the beginning |
848 | | versions.push(make_version(10, Some("v1"))); |
849 | | |
850 | | assert_eq!(versions.inner.len(), 3); |
851 | | assert_eq!(versions.inner[0].version, 10); |
852 | | assert_eq!(versions.inner[1].version, 20); |
853 | | assert_eq!(versions.inner[2].version, 30); |
854 | | } |
855 | | |
856 | | #[test] |
857 | | fn test_push_slow_path_update_middle() { |
858 | | let mut versions = Versions::new(); |
859 | | versions.push(make_version(10, Some("v1"))); |
860 | | versions.push(make_version(20, Some("v2"))); |
861 | | versions.push(make_version(30, Some("v3"))); |
862 | | // Slow path: update a middle version |
863 | | versions.push(make_version(20, Some("v2_updated"))); |
864 | | |
865 | | assert_eq!(versions.inner.len(), 3); |
866 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2_updated".to_string()))); |
867 | | } |
868 | | |
869 | | #[test] |
870 | | fn test_push_with_delete_at_end() { |
871 | | let mut versions = Versions::new(); |
872 | | versions.push(make_version(10, Some("v1"))); |
873 | | versions.push(make_version(20, Some("v2"))); |
874 | | // Fast path: append delete |
875 | | versions.push(make_version(30, None)); |
876 | | |
877 | | assert_eq!(versions.inner.len(), 3); |
878 | | assert!(!versions.exists_version(30)); |
879 | | assert_eq!(versions.fetch_version(30), None); |
880 | | } |
881 | | |
882 | | #[test] |
883 | | fn test_push_delete_then_value_same_version() { |
884 | | let mut versions = Versions::new(); |
885 | | versions.push(make_version(10, Some("v1"))); |
886 | | // Push delete |
887 | | versions.push(make_version(20, None)); |
888 | | assert!(!versions.exists_version(20)); |
889 | | // Update same version with a value |
890 | | versions.push(make_version(20, Some("v2"))); |
891 | | assert_eq!(versions.inner.len(), 2); |
892 | | assert!(versions.exists_version(20)); |
893 | | assert_eq!(versions.fetch_version(20), Some(Bytes::from("v2".to_string()))); |
894 | | } |
895 | | |
896 | | #[test] |
897 | | fn test_push_value_then_delete_same_version() { |
898 | | let mut versions = Versions::new(); |
899 | | versions.push(make_version(10, Some("v1"))); |
900 | | versions.push(make_version(20, Some("v2"))); |
901 | | // Update last version to delete |
902 | | versions.push(make_version(20, None)); |
903 | | |
904 | | assert_eq!(versions.inner.len(), 2); |
905 | | assert!(!versions.exists_version(20)); |
906 | | assert_eq!(versions.fetch_version(20), None); |
907 | | } |
908 | | |
909 | | #[test] |
910 | | fn test_push_consecutive_deletes() { |
911 | | let mut versions = Versions::new(); |
912 | | versions.push(make_version(10, Some("v1"))); |
913 | | // Push delete at version 20 |
914 | | versions.push(make_version(20, None)); |
915 | | // Push another delete at version 30 (different from last value which is None) |
916 | | versions.push(make_version(30, None)); |
917 | | |
918 | | // Should only have 2 entries - version 20 and 30 deletes should be separate |
919 | | assert_eq!(versions.inner.len(), 2); |
920 | | assert!(!versions.exists_version(20)); |
921 | | assert!(!versions.exists_version(30)); |
922 | | } |
923 | | |
924 | | #[test] |
925 | | fn test_push_stress_many_appends() { |
926 | | let mut versions = Versions::new(); |
927 | | // Push many versions in order (all fast path appends) |
928 | | for i in 0..100 { |
929 | | let value = format!("v{}", i); |
930 | | versions.push(make_version(i * 10, Some(&value))); |
931 | | } |
932 | | assert_eq!(versions.inner.len(), 100); |
933 | | assert_eq!(versions.inner[0].version, 0); |
934 | | assert_eq!(versions.inner[99].version, 990); |
935 | | } |
936 | | |
937 | | #[test] |
938 | | fn test_push_stress_many_updates() { |
939 | | let mut versions = Versions::new(); |
940 | | versions.push(make_version(10, Some("v1"))); |
941 | | // Update the same version many times (all fast path updates) |
942 | | for i in 0..100 { |
943 | | let value = format!("v{}", i); |
944 | | versions.push(make_version(10, Some(&value))); |
945 | | } |
946 | | assert_eq!(versions.inner.len(), 1); |
947 | | assert_eq!(versions.fetch_version(10), Some(Bytes::from("v99".to_string()))); |
948 | | } |
949 | | } |