commonware_storage/index/partitioned/ordered/
cursor.rs1use super::partition::Partition;
11use crate::index::Cursor as CursorTrait;
12use commonware_runtime::telemetry::metrics::{Counter, Gauge};
13use std::{
14 collections::{btree_map, hash_map, BTreeMap, HashMap},
15 ops::Range,
16};
17
18const MUST_CALL_NEXT: &str = "must call Cursor::next()";
19const NO_ACTIVE_ITEM: &str = "no active item in Cursor";
20
21enum State {
23 NeedNext { from: usize },
26 Active { offset: usize },
28 Done,
30}
31
32enum Backing<'a, K: Ord + Copy, V> {
35 Soa {
41 partition: &'a mut Partition<K, V>,
42 key: K,
43 run: Range<usize>,
44 },
45 Spilled {
48 spilled: &'a mut HashMap<usize, BTreeMap<K, Vec<V>>>,
49 partition: usize,
50 key: K,
51 },
52}
53
54impl<K: Ord + Copy, V> Backing<'_, K, V> {
55 fn len(&self) -> usize {
57 match self {
58 Self::Soa { run, .. } => run.len(),
59 Self::Spilled {
60 spilled,
61 partition,
62 key,
63 } => spilled
64 .get(partition)
65 .and_then(|inner| inner.get(key))
66 .map_or(0, Vec::len),
67 }
68 }
69
70 fn get(&self, off: usize) -> &V {
72 match self {
73 Self::Soa { partition, run, .. } => partition.value_at(run.start + off),
74 Self::Spilled {
75 spilled,
76 partition,
77 key,
78 } => &spilled
79 .get(partition)
80 .and_then(|inner| inner.get(key))
81 .expect("active cursor must reference a present key")[off],
82 }
83 }
84
85 fn set(&mut self, off: usize, value: V) {
87 match self {
88 Self::Soa { partition, run, .. } => partition.set(run.start + off, value),
89 Self::Spilled {
90 spilled,
91 partition,
92 key,
93 } => {
94 spilled
95 .get_mut(partition)
96 .and_then(|inner| inner.get_mut(key))
97 .expect("active cursor must reference a present key")[off] = value;
98 }
99 }
100 }
101
102 fn insert(&mut self, off: usize, value: V) -> bool {
104 match self {
105 Self::Soa {
106 partition,
107 key,
108 run,
109 } => {
110 #[allow(unstable_name_collisions)]
111 let created = run.is_empty(); partition.insert_at(run.start + off, *key, value);
113 run.end += 1;
114 created
115 }
116 Self::Spilled {
117 spilled,
118 partition,
119 key,
120 } => match spilled.entry(*partition).or_default().entry(*key) {
121 btree_map::Entry::Occupied(mut run) => {
122 run.get_mut().insert(off, value);
123 false
124 }
125 btree_map::Entry::Vacant(run) => {
126 run.insert(vec![value]);
127 true
128 }
129 },
130 }
131 }
132
133 fn remove(&mut self, off: usize) -> bool {
135 match self {
136 Self::Soa { partition, run, .. } => {
137 partition.remove(run.start + off);
138 run.end -= 1;
139 #[allow(unstable_name_collisions)]
140 run.is_empty()
141 }
142 Self::Spilled {
143 spilled,
144 partition,
145 key,
146 } => {
147 let hash_map::Entry::Occupied(mut part) = spilled.entry(*partition) else {
148 unreachable!("active cursor must reference a present partition")
149 };
150 let btree_map::Entry::Occupied(mut run) = part.get_mut().entry(*key) else {
151 unreachable!("active cursor must reference a present key")
152 };
153 run.get_mut().remove(off);
154 if !run.get().is_empty() {
155 return false;
156 }
157 run.remove();
160 if part.get().is_empty() {
161 part.remove();
162 }
163 true
164 }
165 }
166 }
167}
168
169pub struct Cursor<'a, K: Ord + Copy, V> {
174 backing: Backing<'a, K, V>,
175 state: State,
176 keys: &'a Gauge,
177 items: &'a Gauge,
178 pruned: &'a Counter,
179}
180
181impl<'a, K: Ord + Copy, V> Cursor<'a, K, V> {
182 pub(super) const fn soa(
185 partition: &'a mut Partition<K, V>,
186 key: K,
187 run: Range<usize>,
188 keys: &'a Gauge,
189 items: &'a Gauge,
190 pruned: &'a Counter,
191 ) -> Self {
192 Self {
193 backing: Backing::Soa {
194 partition,
195 key,
196 run,
197 },
198 state: State::NeedNext { from: 0 },
199 keys,
200 items,
201 pruned,
202 }
203 }
204
205 pub(super) const fn spilled(
207 spilled: &'a mut HashMap<usize, BTreeMap<K, Vec<V>>>,
208 partition: usize,
209 key: K,
210 keys: &'a Gauge,
211 items: &'a Gauge,
212 pruned: &'a Counter,
213 ) -> Self {
214 Self {
215 backing: Backing::Spilled {
216 spilled,
217 partition,
218 key,
219 },
220 state: State::NeedNext { from: 0 },
221 keys,
222 items,
223 pruned,
224 }
225 }
226}
227
228impl<K: Ord + Copy + Send + Sync, V: Send + Sync> CursorTrait for Cursor<'_, K, V> {
229 type Value = V;
230
231 fn next(&mut self) -> Option<&V> {
232 let off = match self.state {
233 State::Done => return None,
234 State::NeedNext { from } => from,
235 State::Active { offset } => offset + 1,
236 };
237 if off >= self.backing.len() {
238 self.state = State::Done;
239 return None;
240 }
241 self.state = State::Active { offset: off };
242 Some(self.backing.get(off))
243 }
244
245 fn update(&mut self, value: V) {
246 match self.state {
247 State::NeedNext { .. } => panic!("{MUST_CALL_NEXT}"),
248 State::Done => panic!("{NO_ACTIVE_ITEM}"),
249 State::Active { offset } => self.backing.set(offset, value),
250 }
251 }
252
253 fn insert(&mut self, value: V) {
254 match self.state {
255 State::NeedNext { .. } => panic!("{MUST_CALL_NEXT}"),
256 State::Active { offset } => {
257 self.backing.insert(offset + 1, value);
261 self.items.inc();
262 self.state = State::NeedNext { from: offset + 2 };
263 }
264 State::Done => {
265 let end = self.backing.len();
267 if self.backing.insert(end, value) {
268 self.keys.inc();
269 }
270 self.items.inc();
271 }
272 }
273 }
274
275 fn delete(&mut self) {
276 let offset = match self.state {
277 State::NeedNext { .. } => panic!("{MUST_CALL_NEXT}"),
278 State::Done => panic!("{NO_ACTIVE_ITEM}"),
279 State::Active { offset } => offset,
280 };
281 if self.backing.remove(offset) {
282 self.keys.dec();
284 }
285 self.items.dec();
286 self.pruned.inc();
287
288 self.state = State::NeedNext { from: offset };
290 }
291}