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