1use std::collections::HashMap;
4use std::hash::BuildHasher;
5use std::sync::Arc;
6
7use citadel_core::types::{PageId, PageType, ValueType};
8use citadel_core::{Error, Result};
9use citadel_page::leaf_node::LeafCell;
10use citadel_page::page::Page;
11use citadel_page::{branch_node, leaf_node};
12
13pub trait PageMap {
14 fn get_page(&self, id: &PageId) -> Option<&Page>;
15}
16
17pub trait PageLoader: PageMap {
19 fn ensure_loaded(&mut self, id: PageId) -> Result<()>;
20}
21
22impl<S: BuildHasher> PageMap for HashMap<PageId, Page, S> {
23 fn get_page(&self, id: &PageId) -> Option<&Page> {
24 self.get(id)
25 }
26}
27
28impl<S: BuildHasher> PageMap for HashMap<PageId, Arc<Page>, S> {
29 fn get_page(&self, id: &PageId) -> Option<&Page> {
30 self.get(id).map(|a| a.as_ref())
31 }
32}
33
34pub struct CursorEntry {
35 pub key: Vec<u8>,
36 pub val_type: ValueType,
37 pub value: Vec<u8>,
38}
39
40pub struct Cursor {
42 path: Vec<(PageId, usize)>,
44 leaf: PageId,
46 cell_idx: u16,
48 valid: bool,
50}
51
52impl Cursor {
53 pub fn seek(pages: &impl PageMap, root: PageId, key: &[u8]) -> Result<Self> {
55 let mut path = Vec::new();
56 let mut current = root;
57
58 loop {
59 let page = pages
60 .get_page(¤t)
61 .ok_or(Error::PageOutOfBounds(current))?;
62 match page.page_type() {
63 Some(PageType::Leaf) => break,
64 Some(PageType::Branch) => {
65 let child_idx = branch_node::search_child_index(page, key);
66 let child = branch_node::get_child(page, child_idx);
67 path.push((current, child_idx));
68 current = child;
69 }
70 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
71 }
72 }
73
74 let page = pages.get_page(¤t).unwrap();
75 let cell_idx = match leaf_node::search(page, key) {
76 Ok(idx) => idx,
77 Err(idx) => idx,
78 };
79
80 let valid = cell_idx < page.num_cells();
81
82 let mut cursor = Self {
83 path,
84 leaf: current,
85 cell_idx,
86 valid,
87 };
88
89 if !valid && page.num_cells() > 0 {
91 cursor.advance_leaf(pages)?;
92 } else if page.num_cells() == 0 {
93 cursor.valid = false;
94 }
95
96 Ok(cursor)
97 }
98
99 pub fn first(pages: &impl PageMap, root: PageId) -> Result<Self> {
101 let mut path = Vec::new();
102 let mut current = root;
103
104 loop {
105 let page = pages
106 .get_page(¤t)
107 .ok_or(Error::PageOutOfBounds(current))?;
108 match page.page_type() {
109 Some(PageType::Leaf) => break,
110 Some(PageType::Branch) => {
111 let child = branch_node::get_child(page, 0);
112 path.push((current, 0));
113 current = child;
114 }
115 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
116 }
117 }
118
119 let page = pages.get_page(¤t).unwrap();
120 let valid = page.num_cells() > 0;
121
122 Ok(Self {
123 path,
124 leaf: current,
125 cell_idx: 0,
126 valid,
127 })
128 }
129
130 pub fn last(pages: &impl PageMap, root: PageId) -> Result<Self> {
132 let mut path = Vec::new();
133 let mut current = root;
134
135 loop {
136 let page = pages
137 .get_page(¤t)
138 .ok_or(Error::PageOutOfBounds(current))?;
139 match page.page_type() {
140 Some(PageType::Leaf) => break,
141 Some(PageType::Branch) => {
142 let n = page.num_cells() as usize;
143 let child = page.right_child();
144 path.push((current, n));
145 current = child;
146 }
147 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
148 }
149 }
150
151 let page = pages.get_page(¤t).unwrap();
152 let n = page.num_cells();
153 let valid = n > 0;
154 let cell_idx = if valid { n - 1 } else { 0 };
155
156 Ok(Self {
157 path,
158 leaf: current,
159 cell_idx,
160 valid,
161 })
162 }
163
164 pub fn is_valid(&self) -> bool {
166 self.valid
167 }
168
169 pub fn leaf_page_id(&self) -> PageId {
171 self.leaf
172 }
173
174 pub fn cell_index(&self) -> u16 {
176 self.cell_idx
177 }
178
179 pub fn set_leaf_page_id(&mut self, id: PageId) {
181 self.leaf = id;
182 }
183
184 pub fn set_cell_index(&mut self, idx: u16) {
185 self.cell_idx = idx;
186 }
187
188 pub fn advance_to_next_leaf<P: PageLoader + ?Sized>(&mut self, pages: &mut P) -> Result<bool> {
189 self.advance_leaf_lazy(pages)
190 }
191
192 pub fn current(&self, pages: &impl PageMap) -> Option<CursorEntry> {
193 if !self.valid {
194 return None;
195 }
196 let page = pages.get_page(&self.leaf)?;
197 let cell = leaf_node::read_cell(page, self.cell_idx);
198 Some(CursorEntry {
199 key: cell.key.to_vec(),
200 val_type: cell.val_type,
201 value: cell.value.to_vec(),
202 })
203 }
204
205 pub fn current_ref<'a, P: PageMap>(&self, pages: &'a P) -> Option<LeafCell<'a>> {
206 if !self.valid {
207 return None;
208 }
209 let page = pages.get_page(&self.leaf)?;
210 Some(leaf_node::read_cell(page, self.cell_idx))
211 }
212
213 pub fn next(&mut self, pages: &impl PageMap) -> Result<bool> {
215 if !self.valid {
216 return Ok(false);
217 }
218
219 let page = pages
220 .get_page(&self.leaf)
221 .ok_or(Error::PageOutOfBounds(self.leaf))?;
222
223 if self.cell_idx + 1 < page.num_cells() {
224 self.cell_idx += 1;
225 return Ok(true);
226 }
227
228 self.advance_leaf(pages)
229 }
230
231 pub fn prev(&mut self, pages: &impl PageMap) -> Result<bool> {
233 if !self.valid {
234 return Ok(false);
235 }
236
237 if self.cell_idx > 0 {
238 self.cell_idx -= 1;
239 return Ok(true);
240 }
241
242 self.retreat_leaf(pages)
243 }
244
245 fn advance_leaf(&mut self, pages: &impl PageMap) -> Result<bool> {
247 while let Some((parent_id, child_idx)) = self.path.pop() {
248 let parent = pages
249 .get_page(&parent_id)
250 .ok_or(Error::PageOutOfBounds(parent_id))?;
251 let n = parent.num_cells() as usize;
252
253 if child_idx < n {
254 let next_child_idx = child_idx + 1;
255 let next_child = branch_node::get_child(parent, next_child_idx);
256 self.path.push((parent_id, next_child_idx));
257
258 let mut current = next_child;
259 loop {
260 let page = pages
261 .get_page(¤t)
262 .ok_or(Error::PageOutOfBounds(current))?;
263 match page.page_type() {
264 Some(PageType::Leaf) => {
265 self.leaf = current;
266 self.cell_idx = 0;
267 self.valid = page.num_cells() > 0;
268 return Ok(self.valid);
269 }
270 Some(PageType::Branch) => {
271 let child = branch_node::get_child(page, 0);
272 self.path.push((current, 0));
273 current = child;
274 }
275 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
276 }
277 }
278 }
279 }
281
282 self.valid = false;
284 Ok(false)
285 }
286
287 pub fn seek_lazy(pages: &mut impl PageLoader, root: PageId, key: &[u8]) -> Result<Self> {
289 let mut path = Vec::new();
290 let mut current = root;
291
292 loop {
293 pages.ensure_loaded(current)?;
294 let page = pages
295 .get_page(¤t)
296 .ok_or(Error::PageOutOfBounds(current))?;
297 match page.page_type() {
298 Some(PageType::Leaf) => break,
299 Some(PageType::Branch) => {
300 let child_idx = branch_node::search_child_index(page, key);
301 let child = branch_node::get_child(page, child_idx);
302 path.push((current, child_idx));
303 current = child;
304 }
305 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
306 }
307 }
308
309 let page = pages.get_page(¤t).unwrap();
310 let cell_idx = match leaf_node::search(page, key) {
311 Ok(idx) => idx,
312 Err(idx) => idx,
313 };
314
315 let valid = cell_idx < page.num_cells();
316
317 let mut cursor = Self {
318 path,
319 leaf: current,
320 cell_idx,
321 valid,
322 };
323
324 if !valid && page.num_cells() > 0 {
325 cursor.advance_leaf_lazy(pages)?;
326 } else if page.num_cells() == 0 {
327 cursor.valid = false;
328 }
329
330 Ok(cursor)
331 }
332
333 pub fn current_ref_lazy<'a, P: PageLoader + ?Sized>(
335 &self,
336 pages: &'a mut P,
337 ) -> Option<LeafCell<'a>> {
338 if !self.valid {
339 return None;
340 }
341 pages.ensure_loaded(self.leaf).ok()?;
342 let page = pages.get_page(&self.leaf)?;
343 Some(leaf_node::read_cell(page, self.cell_idx))
344 }
345
346 pub fn next_lazy<P: PageLoader + ?Sized>(&mut self, pages: &mut P) -> Result<bool> {
348 if !self.valid {
349 return Ok(false);
350 }
351
352 pages.ensure_loaded(self.leaf)?;
353 let page = pages
354 .get_page(&self.leaf)
355 .ok_or(Error::PageOutOfBounds(self.leaf))?;
356
357 if self.cell_idx + 1 < page.num_cells() {
358 self.cell_idx += 1;
359 return Ok(true);
360 }
361
362 self.advance_leaf_lazy(pages)
363 }
364
365 fn advance_leaf_lazy<P: PageLoader + ?Sized>(&mut self, pages: &mut P) -> Result<bool> {
367 while let Some((parent_id, child_idx)) = self.path.pop() {
368 let parent = pages
369 .get_page(&parent_id)
370 .ok_or(Error::PageOutOfBounds(parent_id))?;
371 let n = parent.num_cells() as usize;
372
373 if child_idx < n {
374 let next_child_idx = child_idx + 1;
375 let next_child = branch_node::get_child(parent, next_child_idx);
376 self.path.push((parent_id, next_child_idx));
377
378 let mut current = next_child;
379 loop {
380 pages.ensure_loaded(current)?;
381 let page = pages
382 .get_page(¤t)
383 .ok_or(Error::PageOutOfBounds(current))?;
384 match page.page_type() {
385 Some(PageType::Leaf) => {
386 self.leaf = current;
387 self.cell_idx = 0;
388 self.valid = page.num_cells() > 0;
389 return Ok(self.valid);
390 }
391 Some(PageType::Branch) => {
392 let child = branch_node::get_child(page, 0);
393 self.path.push((current, 0));
394 current = child;
395 }
396 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
397 }
398 }
399 }
400 }
401
402 self.valid = false;
403 Ok(false)
404 }
405
406 fn retreat_leaf(&mut self, pages: &impl PageMap) -> Result<bool> {
408 while let Some((parent_id, child_idx)) = self.path.pop() {
409 if child_idx > 0 {
410 let prev_child_idx = child_idx - 1;
411 let parent = pages
412 .get_page(&parent_id)
413 .ok_or(Error::PageOutOfBounds(parent_id))?;
414 let prev_child = branch_node::get_child(parent, prev_child_idx);
415 self.path.push((parent_id, prev_child_idx));
416
417 let mut current = prev_child;
418 loop {
419 let page = pages
420 .get_page(¤t)
421 .ok_or(Error::PageOutOfBounds(current))?;
422 match page.page_type() {
423 Some(PageType::Leaf) => {
424 self.leaf = current;
425 let n = page.num_cells();
426 if n > 0 {
427 self.cell_idx = n - 1;
428 self.valid = true;
429 } else {
430 self.valid = false;
431 }
432 return Ok(self.valid);
433 }
434 Some(PageType::Branch) => {
435 let n = page.num_cells() as usize;
436 let child = page.right_child();
437 self.path.push((current, n));
438 current = child;
439 }
440 _ => return Err(Error::InvalidPageType(page.page_type_raw(), current)),
441 }
442 }
443 }
444 }
446
447 self.valid = false;
449 Ok(false)
450 }
451}
452
453#[cfg(test)]
454#[path = "cursor_tests.rs"]
455mod tests;