1#[derive(Debug)]
5pub struct ForwardInMemoryCursor<'a, K, V> {
6 entries: &'a [(K, V)],
8 idx: usize,
10}
11
12impl<'a, K, V> ForwardInMemoryCursor<'a, K, V> {
13 #[inline]
17 pub const fn new(entries: &'a [(K, V)]) -> Self {
18 Self { entries, idx: 0 }
19 }
20
21 #[inline]
23 pub const fn is_empty(&self) -> bool {
24 self.entries.is_empty()
25 }
26
27 #[inline]
29 pub fn has_any<F>(&self, predicate: F) -> bool
30 where
31 F: Fn(&(K, V)) -> bool,
32 {
33 self.entries.iter().any(predicate)
34 }
35
36 #[inline]
38 pub fn current(&self) -> Option<&(K, V)> {
39 self.entries.get(self.idx)
40 }
41
42 #[inline]
44 pub const fn reset(&mut self) {
45 self.idx = 0;
46 }
47
48 #[inline]
49 fn next(&mut self) -> Option<&(K, V)> {
50 let entry = self.entries.get(self.idx)?;
51 self.idx += 1;
52 Some(entry)
53 }
54}
55
56const BINARY_SEARCH_THRESHOLD: usize = 64;
59
60impl<K: Ord, V> ForwardInMemoryCursor<'_, K, V> {
61 pub fn seek(&mut self, key: &K) -> Option<&(K, V)> {
64 self.advance_while(|k| k < key)
65 }
66
67 pub fn first_after(&mut self, key: &K) -> Option<&(K, V)> {
70 self.advance_while(|k| k <= key)
71 }
72
73 fn advance_while(&mut self, predicate: impl Fn(&K) -> bool) -> Option<&(K, V)> {
81 let remaining = self.entries.len().saturating_sub(self.idx);
82 if remaining >= BINARY_SEARCH_THRESHOLD {
83 let slice = &self.entries[self.idx..];
84 let pos = slice.partition_point(|(k, _)| predicate(k));
85 self.idx += pos;
86 } else {
87 while self.current().is_some_and(|(k, _)| predicate(k)) {
88 self.next();
89 }
90 }
91 self.current()
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_cursor_small() {
101 let mut cursor = ForwardInMemoryCursor::new(&[(1, ()), (2, ()), (3, ()), (4, ()), (5, ())]);
102 assert_eq!(cursor.current(), Some(&(1, ())));
103
104 assert_eq!(cursor.seek(&0), Some(&(1, ())));
105 assert_eq!(cursor.current(), Some(&(1, ())));
106
107 assert_eq!(cursor.seek(&3), Some(&(3, ())));
108 assert_eq!(cursor.current(), Some(&(3, ())));
109
110 assert_eq!(cursor.seek(&3), Some(&(3, ())));
111 assert_eq!(cursor.current(), Some(&(3, ())));
112
113 assert_eq!(cursor.seek(&4), Some(&(4, ())));
114 assert_eq!(cursor.current(), Some(&(4, ())));
115
116 assert_eq!(cursor.seek(&6), None);
117 assert_eq!(cursor.current(), None);
118 }
119
120 #[test]
121 fn test_cursor_large_binary_search() {
122 let entries: Vec<(i32, ())> = (0..200).map(|i| (i * 2, ())).collect();
124 let mut cursor = ForwardInMemoryCursor::new(&entries);
125
126 assert_eq!(cursor.seek(&0), Some(&(0, ())));
128 assert_eq!(cursor.idx, 0);
129
130 assert_eq!(cursor.seek(&100), Some(&(100, ())));
132 assert_eq!(cursor.idx, 50);
133
134 assert_eq!(cursor.seek(&101), Some(&(102, ())));
136 assert_eq!(cursor.idx, 51);
137
138 assert_eq!(cursor.seek(&398), Some(&(398, ())));
140 assert_eq!(cursor.idx, 199);
141
142 assert_eq!(cursor.seek(&1000), None);
144 }
145
146 #[test]
147 fn test_first_after_large() {
148 let entries: Vec<(i32, ())> = (0..200).map(|i| (i * 2, ())).collect();
149 let mut cursor = ForwardInMemoryCursor::new(&entries);
150
151 assert_eq!(cursor.first_after(&0), Some(&(2, ())));
153 assert_eq!(cursor.idx, 1);
154
155 cursor.reset();
157 assert_eq!(cursor.first_after(&99), Some(&(100, ())));
158
159 cursor.reset();
161 assert_eq!(cursor.first_after(&100), Some(&(102, ())));
162 }
163
164 #[test]
165 fn test_cursor_consistency() {
166 let entries: Vec<(i32, ())> = (0..200).map(|i| (i * 3, ())).collect();
168
169 for search_key in [0, 1, 3, 50, 150, 299, 300, 597, 598, 599, 1000] {
170 let mut cursor1 = ForwardInMemoryCursor::new(&entries);
172 let result1 = cursor1.seek(&search_key);
173
174 let mut cursor2 = ForwardInMemoryCursor::new(&entries);
176 if search_key > 100 {
177 cursor2.seek(&(search_key - 50));
178 }
179 let result2 = cursor2.seek(&search_key);
180
181 assert_eq!(
182 result1, result2,
183 "Mismatch for key {search_key}: binary={result1:?}, linear={result2:?}"
184 );
185 }
186 }
187}