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
49const BINARY_SEARCH_THRESHOLD: usize = 128;
52
53impl<K: Ord, V> ForwardInMemoryCursor<'_, K, V> {
54 pub fn seek(&mut self, key: &K) -> Option<&(K, V)> {
57 if self.current().is_some_and(|(k, _)| k >= key) {
58 return self.current()
59 }
60
61 self.advance_while(|k| k < key)
62 }
63
64 pub fn first_after(&mut self, key: &K) -> Option<&(K, V)> {
67 if self.current().is_some_and(|(k, _)| k > key) {
68 return self.current()
69 }
70
71 self.advance_while(|k| k <= key)
72 }
73
74 fn advance_while(&mut self, predicate: impl Fn(&K) -> bool) -> Option<&(K, V)> {
82 let remaining = self.entries.len().saturating_sub(self.idx);
83 if remaining >= BINARY_SEARCH_THRESHOLD {
84 let slice = &self.entries[self.idx..];
85 let pos = slice.partition_point(|(k, _)| predicate(k));
86 self.idx += pos;
87 } else {
88 while self.idx < self.entries.len() && predicate(&self.entries[self.idx].0) {
89 self.idx += 1;
90 }
91 }
92 self.current()
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_cursor_small() {
102 let mut cursor = ForwardInMemoryCursor::new(&[(1, ()), (2, ()), (3, ()), (4, ()), (5, ())]);
103 assert_eq!(cursor.current(), Some(&(1, ())));
104
105 assert_eq!(cursor.seek(&0), Some(&(1, ())));
106 assert_eq!(cursor.current(), Some(&(1, ())));
107
108 assert_eq!(cursor.seek(&3), Some(&(3, ())));
109 assert_eq!(cursor.current(), Some(&(3, ())));
110
111 assert_eq!(cursor.seek(&3), Some(&(3, ())));
112 assert_eq!(cursor.current(), Some(&(3, ())));
113
114 assert_eq!(cursor.seek(&4), Some(&(4, ())));
115 assert_eq!(cursor.current(), Some(&(4, ())));
116
117 assert_eq!(cursor.seek(&6), None);
118 assert_eq!(cursor.current(), None);
119 }
120
121 #[test]
122 fn test_cursor_large_binary_search() {
123 let entries: Vec<(i32, ())> = (0..200).map(|i| (i * 2, ())).collect();
125 let mut cursor = ForwardInMemoryCursor::new(&entries);
126
127 assert_eq!(cursor.seek(&0), Some(&(0, ())));
129 assert_eq!(cursor.idx, 0);
130
131 assert_eq!(cursor.seek(&100), Some(&(100, ())));
133 assert_eq!(cursor.idx, 50);
134
135 assert_eq!(cursor.seek(&101), Some(&(102, ())));
137 assert_eq!(cursor.idx, 51);
138
139 assert_eq!(cursor.seek(&398), Some(&(398, ())));
141 assert_eq!(cursor.idx, 199);
142
143 assert_eq!(cursor.seek(&1000), None);
145 }
146
147 #[test]
148 fn test_first_after_large() {
149 let entries: Vec<(i32, ())> = (0..200).map(|i| (i * 2, ())).collect();
150 let mut cursor = ForwardInMemoryCursor::new(&entries);
151
152 assert_eq!(cursor.first_after(&0), Some(&(2, ())));
154 assert_eq!(cursor.idx, 1);
155
156 cursor.reset();
158 assert_eq!(cursor.first_after(&99), Some(&(100, ())));
159
160 cursor.reset();
162 assert_eq!(cursor.first_after(&100), Some(&(102, ())));
163 }
164
165 #[test]
166 fn test_cursor_consistency() {
167 let entries: Vec<(i32, ())> = (0..200).map(|i| (i * 3, ())).collect();
169
170 for search_key in [0, 1, 3, 50, 150, 299, 300, 597, 598, 599, 1000] {
171 let mut cursor1 = ForwardInMemoryCursor::new(&entries);
173 let result1 = cursor1.seek(&search_key);
174
175 let mut cursor2 = ForwardInMemoryCursor::new(&entries);
177 if search_key > 100 {
178 cursor2.seek(&(search_key - 50));
179 }
180 let result2 = cursor2.seek(&search_key);
181
182 assert_eq!(
183 result1, result2,
184 "Mismatch for key {search_key}: binary={result1:?}, linear={result2:?}"
185 );
186 }
187 }
188}