1use std::cmp::Ordering;
33use std::sync::{Arc, RwLock};
34
35use super::page::{Page, PageType, HEADER_SIZE, PAGE_SIZE};
36use super::page_store::PageStore;
37use super::pager::PagerError;
38
39pub const MAX_KEY_SIZE: usize = 1024;
41
42pub const MAX_VALUE_SIZE: usize = value_layout::MAX_VALUE_SIZE;
55
56pub const MIN_FILL_FACTOR: usize = 25;
58
59const LEAF_PREV_OFFSET: usize = HEADER_SIZE;
61
62const LEAF_NEXT_OFFSET: usize = HEADER_SIZE + 4;
64
65const LEAF_SLOT_ARRAY_OFFSET: usize = HEADER_SIZE + 8;
80
81const _: () = assert!(
84 2 * (MAX_KEY_SIZE + value_layout::OVERFLOW_THRESHOLD + 1 + 4 + 2)
85 <= PAGE_SIZE - LEAF_SLOT_ARRAY_OFFSET
86);
87
88const LEAF_DATA_OFFSET: usize = LEAF_SLOT_ARRAY_OFFSET;
91
92const INTERIOR_DATA_OFFSET: usize = HEADER_SIZE;
94
95#[derive(Debug, Clone)]
97pub enum BTreeError {
98 NotFound,
100 DuplicateKey,
102 KeyTooLarge(usize),
104 ValueTooLarge(usize),
106 Corrupted(String),
108 Pager(String),
110 LockPoisoned(String),
112 ValueLayout(String),
114 ReadLimitExceeded {
116 required_bytes: usize,
117 limit_bytes: usize,
118 },
119}
120
121impl std::fmt::Display for BTreeError {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 Self::NotFound => write!(f, "Key not found"),
125 Self::DuplicateKey => write!(f, "Key already exists"),
126 Self::KeyTooLarge(size) => {
127 write!(f, "Key too large: {} bytes (max {})", size, MAX_KEY_SIZE)
128 }
129 Self::ValueTooLarge(size) => write!(
130 f,
131 "Value too large: {} bytes (max {})",
132 size, MAX_VALUE_SIZE
133 ),
134 Self::Corrupted(msg) => write!(f, "B-tree corrupted: {}", msg),
135 Self::Pager(msg) => write!(f, "Pager error: {}", msg),
136 Self::LockPoisoned(msg) => write!(f, "Lock poisoned: {}", msg),
137 Self::ValueLayout(msg) => write!(f, "Value layout: {}", msg),
138 Self::ReadLimitExceeded {
139 required_bytes,
140 limit_bytes,
141 } => write!(
142 f,
143 "B-tree read needs {required_bytes} bytes over read memory limit {limit_bytes}"
144 ),
145 }
146 }
147}
148
149impl From<value_layout::ValueLayoutError> for BTreeError {
150 fn from(e: value_layout::ValueLayoutError) -> Self {
151 match e {
152 value_layout::ValueLayoutError::ValueTooLarge(n) => Self::ValueTooLarge(n),
153 value_layout::ValueLayoutError::ReadLimitExceeded {
154 required_bytes,
155 limit_bytes,
156 } => Self::ReadLimitExceeded {
157 required_bytes,
158 limit_bytes,
159 },
160 other => Self::ValueLayout(other.to_string()),
161 }
162 }
163}
164
165impl std::error::Error for BTreeError {}
166
167impl From<PagerError> for BTreeError {
168 fn from(e: PagerError) -> Self {
169 Self::Pager(e.to_string())
170 }
171}
172
173impl From<super::page::PageError> for BTreeError {
174 fn from(e: super::page::PageError) -> Self {
175 Self::Corrupted(e.to_string())
176 }
177}
178
179pub type BTreeResult<T> = Result<T, BTreeError>;
181
182pub struct BTreeCursor<P: PageStore> {
184 _retirement_pin: Arc<()>,
186 leaf_page_id: u32,
188 position: usize,
190 pager: Arc<P>,
192 prefetched_next: bool,
196 immutable: bool,
199 immutable_leaf: Option<Box<Page>>,
200}
201
202impl<P: PageStore> BTreeCursor<P> {
203 pub(crate) fn fork_immutable(&self) -> Self {
205 assert!(self.immutable, "only immutable cursors may be forked");
206 Self {
207 _retirement_pin: Arc::clone(&self._retirement_pin),
208 leaf_page_id: self.leaf_page_id,
209 position: self.position,
210 pager: Arc::clone(&self.pager),
211 prefetched_next: self.prefetched_next,
212 immutable: true,
213 immutable_leaf: self.immutable_leaf.clone(),
214 }
215 }
216
217 pub(crate) fn with_immutable_leaves(mut self) -> Self {
220 self.immutable = true;
221 self
222 }
223
224 fn prepare_immutable_leaf(&mut self) -> BTreeResult<bool> {
225 if self.leaf_page_id == 0 {
226 return Ok(false);
227 }
228 if self.immutable_leaf.is_none() {
229 let page = self.pager.read_page(self.leaf_page_id)?;
230 if page.page_type()? != PageType::BTreeLeaf {
231 return Err(BTreeError::Corrupted(
232 "cursor reached a non-leaf page".into(),
233 ));
234 }
235 self.immutable_leaf = Some(Box::new(page));
236 }
237 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
238 if self.position < usize::from(page.cell_count()) {
239 return Ok(true);
240 }
241 let next = read_next_leaf(page);
242 if next == 0 {
243 self.leaf_page_id = 0;
244 self.immutable_leaf = None;
245 return Ok(false);
246 }
247 let next_page = self.pager.read_page(next)?;
248 if next_page.page_type()? != PageType::BTreeLeaf || next_page.cell_count() == 0 {
249 return Err(BTreeError::Corrupted(
250 "leaf chain reached an invalid leaf".into(),
251 ));
252 }
253 let first_key = read_leaf_cell_borrowed(&next_page, 0)?.0;
254 if leaf_high_key(page)?
255 .as_ref()
256 .is_some_and(|key| first_key <= key.as_slice())
257 {
258 return Err(BTreeError::Corrupted(
259 "leaf chain does not advance its key range".into(),
260 ));
261 }
262 **self.immutable_leaf.as_mut().expect("loaded immutable leaf") = next_page;
263 self.leaf_page_id = next;
264 self.position = 0;
265 self.prefetched_next = false;
266 Ok(true)
267 }
268
269 pub(crate) fn peek_key_with<T>(
271 &mut self,
272 visit: impl FnOnce(&[u8]) -> T,
273 ) -> BTreeResult<Option<T>> {
274 if self.immutable {
275 if !self.prepare_immutable_leaf()? {
276 return Ok(None);
277 }
278 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
279 return Ok(Some(visit(read_leaf_cell_borrowed(page, self.position)?.0)));
280 }
281 let Some(page) = self.prepare_next_leaf()? else {
282 return Ok(None);
283 };
284 Ok(Some(visit(
285 read_leaf_cell_borrowed(&page, self.position)?.0,
286 )))
287 }
288
289 pub fn peek_key(&mut self) -> BTreeResult<Option<Vec<u8>>> {
291 if self.immutable {
292 if !self.prepare_immutable_leaf()? {
293 return Ok(None);
294 }
295 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
296 return Ok(Some(
297 read_leaf_cell_borrowed(page, self.position)?.0.to_vec(),
298 ));
299 }
300 let Some(page) = self.prepare_next_leaf()? else {
301 return Ok(None);
302 };
303 Ok(Some(read_leaf_cell(&page, self.position)?.0))
304 }
305
306 pub fn skip_next(&mut self) -> BTreeResult<bool> {
308 if self.immutable {
309 if !self.prepare_immutable_leaf()? {
310 return Ok(false);
311 }
312 self.position += 1;
313 return Ok(true);
314 }
315 if self.prepare_next_leaf()?.is_none() {
316 return Ok(false);
317 }
318 self.position += 1;
319 Ok(true)
320 }
321
322 fn prepare_next_leaf(&mut self) -> BTreeResult<Option<Page>> {
323 if self.leaf_page_id == 0 {
324 return Ok(None);
325 }
326 let page = self.pager.read_page(self.leaf_page_id)?;
327 if page.page_type()? != PageType::BTreeLeaf {
328 return Err(BTreeError::Corrupted(
329 "cursor reached a non-leaf page".into(),
330 ));
331 }
332 if self.position < usize::from(page.cell_count()) {
333 return Ok(Some(page));
334 }
335 let next = read_next_leaf(&page);
336 if next == 0 {
337 self.leaf_page_id = 0;
338 return Ok(None);
339 }
340 let next_page = self.pager.read_page(next)?;
341 if next_page.page_type()? != PageType::BTreeLeaf || next_page.cell_count() == 0 {
342 return Err(BTreeError::Corrupted(
343 "leaf chain reached an invalid leaf".into(),
344 ));
345 }
346 let first_key = read_leaf_cell(&next_page, 0)?.0;
347 if leaf_high_key(&page)?
348 .as_ref()
349 .is_some_and(|key| first_key <= *key)
350 {
351 return Err(BTreeError::Corrupted(
352 "leaf chain does not advance its key range".into(),
353 ));
354 }
355 self.leaf_page_id = next;
356 self.position = 0;
357 self.prefetched_next = false;
358 Ok(Some(next_page))
359 }
360
361 pub fn next_with_budget(
363 &mut self,
364 limit_bytes: usize,
365 ) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
366 self.next_impl(limit_bytes)
367 }
368
369 pub fn next(&mut self) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
371 self.next_impl(usize::MAX)
372 }
373
374 fn next_impl(&mut self, limit_bytes: usize) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
375 if self.immutable {
376 return self.next_immutable(limit_bytes);
377 }
378 if self.leaf_page_id == 0 {
379 return Ok(None);
380 }
381
382 let page = self.pager.read_page(self.leaf_page_id)?;
383 if page.page_type()? != PageType::BTreeLeaf {
384 return Err(BTreeError::Corrupted(
385 "cursor reached a non-leaf page".into(),
386 ));
387 }
388 let cell_count = page.cell_count() as usize;
389
390 if !self.prefetched_next && self.position >= cell_count / 2 && cell_count > 0 {
396 let next_leaf = read_next_leaf(&page);
397 if next_leaf != 0 {
398 self.pager.prefetch_hint(next_leaf);
399 }
400 self.prefetched_next = true;
401 }
402
403 if self.position < cell_count {
405 let (key, stored) = read_leaf_cell(&page, self.position)?;
406 let value =
407 value_layout::decode_with_budget(self.pager.as_ref(), &stored, limit_bytes)?;
408 self.position += 1;
409 return Ok(Some((key, value)));
410 }
411
412 let next_leaf = read_next_leaf(&page);
414 if next_leaf == 0 {
415 self.leaf_page_id = 0;
416 return Ok(None);
417 }
418 let previous_high_key = leaf_high_key(&page)?;
419
420 self.leaf_page_id = next_leaf;
421 self.position = 0;
422 self.prefetched_next = false; let page = self.pager.read_page(self.leaf_page_id)?;
426 if page.page_type()? != PageType::BTreeLeaf {
427 return Err(BTreeError::Corrupted(
428 "leaf chain reached a non-leaf page".into(),
429 ));
430 }
431 if page.cell_count() == 0 {
432 return Ok(None);
433 }
434
435 let (key, stored) = read_leaf_cell(&page, 0)?;
436 if previous_high_key
440 .as_ref()
441 .is_some_and(|previous| key <= *previous)
442 {
443 self.leaf_page_id = 0;
444 return Err(BTreeError::Corrupted(
445 "leaf chain does not advance its key range".into(),
446 ));
447 }
448 let value = value_layout::decode_with_budget(self.pager.as_ref(), &stored, limit_bytes)?;
449 self.position = 1;
450 Ok(Some((key, value)))
451 }
452
453 fn next_immutable(&mut self, limit_bytes: usize) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
454 if !self.prepare_immutable_leaf()? {
455 return Ok(None);
456 }
457 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
458 if !self.prefetched_next && self.position >= usize::from(page.cell_count()) / 2 {
459 let next = read_next_leaf(page);
460 if next != 0 {
461 self.pager.prefetch_hint(next);
462 }
463 self.prefetched_next = true;
464 }
465 let (key, stored) = read_leaf_cell_borrowed(page, self.position)?;
466 let value = value_layout::decode_with_budget(self.pager.as_ref(), stored, limit_bytes)?;
467 self.position += 1;
468 Ok(Some((key.to_vec(), value)))
469 }
470
471 pub fn peek(&self) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
473 if self.leaf_page_id == 0 {
474 return Ok(None);
475 }
476
477 let page = self.pager.read_page(self.leaf_page_id)?;
478 let cell_count = page.cell_count() as usize;
479
480 if self.position >= cell_count {
481 return Ok(None);
482 }
483
484 let (key, stored) = read_leaf_cell(&page, self.position)?;
485 let value = value_layout::decode(self.pager.as_ref(), &stored)?;
486 Ok(Some((key, value)))
487 }
488}
489
490pub struct BTree<P: PageStore> {
492 retirement_pin: Arc<()>,
493 pager: Arc<P>,
495 root_page_id: RwLock<u32>,
497 rightmost_leaf: RwLock<Option<(u32, Vec<u8>)>>,
506 writer: parking_lot::ReentrantMutex<()>,
514}
515
516#[path = "btree/impl.rs"]
517mod btree_impl;
518
519#[path = "btree/reclaim.rs"]
520mod reclaim;
521
522#[cfg(test)]
523#[path = "btree/read_budget_tests.rs"]
524mod read_budget_tests;
525
526#[path = "btree/lehman_yao.rs"]
527pub mod lehman_yao;
528
529#[path = "btree/value_layout.rs"]
530pub mod value_layout;
531enum SearchResult {
534 Found(usize),
535 NotFound(usize),
536}
537
538fn search_leaf(page: &Page, key: &[u8]) -> BTreeResult<SearchResult> {
539 let cell_count = page.cell_count() as usize;
540
541 let mut low = 0;
543 let mut high = cell_count;
544
545 while low < high {
546 let mid = (low + high) / 2;
547 let (cell_key, _) = read_leaf_cell(page, mid)?;
548
549 match cell_key.as_slice().cmp(key) {
550 Ordering::Less => low = mid + 1,
551 Ordering::Greater => high = mid,
552 Ordering::Equal => return Ok(SearchResult::Found(mid)),
553 }
554 }
555
556 Ok(SearchResult::NotFound(low))
557}
558
559fn find_child(page: &Page, key: &[u8]) -> BTreeResult<u32> {
560 let cell_count = page.cell_count() as usize;
561
562 for i in 0..cell_count {
564 let (cell_key, child) = read_interior_cell(page, i)?;
565 if key < cell_key.as_slice() {
566 return Ok(child);
567 }
568 }
569
570 Ok(page.right_child())
572}
573
574fn find_first_child(page: &Page) -> BTreeResult<u32> {
575 if page.cell_count() == 0 {
576 return Ok(page.right_child());
577 }
578 let (_, child) = read_interior_cell(page, 0)?;
579 Ok(child)
580}
581
582fn leaf_min_bytes() -> usize {
583 (PAGE_SIZE - LEAF_DATA_OFFSET) * MIN_FILL_FACTOR / 100
584}
585
586fn interior_min_bytes() -> usize {
587 (PAGE_SIZE - INTERIOR_DATA_OFFSET) * MIN_FILL_FACTOR / 100
588}
589
590fn leaf_entry_size(entry: &(Vec<u8>, Vec<u8>)) -> usize {
594 2 + 4 + entry.0.len() + entry.1.len()
595}
596
597fn leaf_entries_size(entries: &[(Vec<u8>, Vec<u8>)]) -> usize {
598 entries.iter().map(leaf_entry_size).sum()
599}
600
601#[inline]
602fn leaf_slot_offset_for(index: usize) -> usize {
603 LEAF_SLOT_ARRAY_OFFSET + index * 2
604}
605
606#[inline]
607fn leaf_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
608 let data = page.as_bytes();
609 let slot_pos = leaf_slot_offset_for(index);
610 if slot_pos + 2 > PAGE_SIZE {
611 return Err(BTreeError::Corrupted("slot array overflows page".into()));
612 }
613 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
614}
615
616#[inline]
617fn leaf_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
618 let data = page.as_bytes_mut();
619 let slot_pos = leaf_slot_offset_for(index);
620 if slot_pos + 2 > PAGE_SIZE {
621 return Err(BTreeError::Corrupted("slot array overflows page".into()));
622 }
623 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
624 Ok(())
625}
626
627#[inline]
628fn leaf_slots_end(page: &Page) -> usize {
629 LEAF_SLOT_ARRAY_OFFSET + (page.cell_count() as usize) * 2
630}
631
632#[inline]
633fn leaf_cells_start(page: &Page) -> usize {
634 let end = page.free_end() as usize;
635 if end == 0 {
636 PAGE_SIZE
637 } else {
638 end
639 }
640}
641
642#[inline]
643fn leaf_free_bytes(page: &Page) -> usize {
644 let slot_end = leaf_slots_end(page);
645 let cells = leaf_cells_start(page);
646 cells.saturating_sub(slot_end)
647}
648
649fn interior_key_size(key: &[u8]) -> usize {
650 2 + key.len() + 4
651}
652
653fn interior_entries_size(keys: &[Vec<u8>]) -> usize {
654 keys.iter().map(|k| interior_key_size(k)).sum()
655}
656
657fn read_leaf_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, Vec<u8>)> {
663 let (key, value) = read_leaf_cell_borrowed(page, index)?;
664 Ok((key.to_vec(), value.to_vec()))
665}
666
667fn read_leaf_cell_borrowed(page: &Page, index: usize) -> BTreeResult<(&[u8], &[u8])> {
668 let cell_count = page.cell_count() as usize;
669 if index >= cell_count {
670 return Err(BTreeError::Corrupted("Cell index out of range".into()));
671 }
672 let offset = leaf_read_slot(page, index)?;
673 let data = page.as_bytes();
674 if offset + 4 > PAGE_SIZE {
675 return Err(BTreeError::Corrupted("cell header out of range".into()));
676 }
677 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
678 let val_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
679 let end = offset + 4 + key_len + val_len;
680 if end > PAGE_SIZE {
681 return Err(BTreeError::Corrupted("cell body out of range".into()));
682 }
683 let key = &data[offset + 4..offset + 4 + key_len];
684 let value = &data[offset + 4 + key_len..end];
685 Ok((key, value))
686}
687
688fn read_leaf_entries(page: &Page) -> BTreeResult<Vec<(Vec<u8>, Vec<u8>)>> {
689 let cell_count = page.cell_count() as usize;
690 let mut entries = Vec::with_capacity(cell_count);
691 for i in 0..cell_count {
692 entries.push(read_leaf_cell(page, i)?);
693 }
694 Ok(entries)
695}
696
697fn write_leaf_entries(page: &mut Page, entries: &[(Vec<u8>, Vec<u8>)]) -> BTreeResult<()> {
703 clear_leaf_cells(page);
704 for (i, (k, v)) in entries.iter().enumerate() {
705 let cell_size = 4 + k.len() + v.len();
706 let cells_start = leaf_cells_start(page);
707 let slot_end = LEAF_SLOT_ARRAY_OFFSET + (i + 1) * 2;
708 if slot_end + cell_size > cells_start {
709 return Err(BTreeError::Corrupted("leaf rebuild overflowed page".into()));
710 }
711 let cell_offset = cells_start - cell_size;
712 {
713 let data = page.as_bytes_mut();
714 data[cell_offset..cell_offset + 2].copy_from_slice(&(k.len() as u16).to_le_bytes());
715 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(v.len() as u16).to_le_bytes());
716 data[cell_offset + 4..cell_offset + 4 + k.len()].copy_from_slice(k);
717 data[cell_offset + 4 + k.len()..cell_offset + 4 + k.len() + v.len()].copy_from_slice(v);
718 }
719 page.set_free_end(cell_offset as u16);
720 leaf_write_slot(page, i, cell_offset as u16)?;
721 }
722 page.set_cell_count(entries.len() as u16);
723 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + entries.len() * 2) as u16);
724 Ok(())
725}
726
727fn can_insert_leaf(page: &Page, key: &[u8], value: &[u8]) -> bool {
728 let needed = 2 + 4 + key.len() + value.len();
729 leaf_free_bytes(page) >= needed
730}
731
732pub(crate) fn overwrite_leaf_value(
748 page: &mut Page,
749 index: usize,
750 new_value: &[u8],
751) -> BTreeResult<()> {
752 let cell_offset = leaf_read_slot(page, index)?;
753 let data = page.as_bytes();
754 if cell_offset + 4 > data.len() {
755 return Err(BTreeError::Corrupted(
756 "leaf cell header out of bounds".into(),
757 ));
758 }
759 let k_len = u16::from_le_bytes([data[cell_offset], data[cell_offset + 1]]) as usize;
760 let old_v_len = u16::from_le_bytes([data[cell_offset + 2], data[cell_offset + 3]]) as usize;
761 if new_value.len() > old_v_len {
762 return Err(BTreeError::Corrupted(
763 "overwrite_leaf_value: new value larger than slot".into(),
764 ));
765 }
766 let value_start = cell_offset + 4 + k_len;
767 if value_start + new_value.len() > data.len() {
768 return Err(BTreeError::Corrupted(
769 "leaf cell value out of bounds".into(),
770 ));
771 }
772 let data = page.as_bytes_mut();
773 if new_value.len() != old_v_len {
776 let new_len = new_value.len() as u16;
777 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&new_len.to_le_bytes());
778 }
779 data[value_start..value_start + new_value.len()].copy_from_slice(new_value);
780 Ok(())
781}
782
783fn insert_into_leaf(page: &mut Page, key: &[u8], value: &[u8]) -> BTreeResult<()> {
785 let cell_count = page.cell_count() as usize;
787 let mut low = 0;
788 let mut high = cell_count;
789 while low < high {
790 let mid = (low + high) / 2;
791 let (cell_key, _) = read_leaf_cell(page, mid)?;
792 match cell_key.as_slice().cmp(key) {
793 Ordering::Less => low = mid + 1,
794 Ordering::Greater => high = mid,
795 Ordering::Equal => {
796 low = mid + 1;
799 break;
800 }
801 }
802 }
803 let insert_pos = low;
804
805 let cell_size = 4 + key.len() + value.len();
807 let slot_end_after = LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2;
808 let cells_start = leaf_cells_start(page);
809 if slot_end_after + cell_size > cells_start {
810 return Err(BTreeError::Corrupted("leaf page full".into()));
811 }
812 let cell_offset = cells_start - cell_size;
813 {
814 let data = page.as_bytes_mut();
815 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
816 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(value.len() as u16).to_le_bytes());
817 data[cell_offset + 4..cell_offset + 4 + key.len()].copy_from_slice(key);
818 data[cell_offset + 4 + key.len()..cell_offset + 4 + key.len() + value.len()]
819 .copy_from_slice(value);
820 }
821 page.set_free_end(cell_offset as u16);
822
823 {
827 let shift_from = leaf_slot_offset_for(insert_pos);
828 let shift_to = shift_from + 2;
829 let shift_len = (cell_count - insert_pos) * 2;
830 if shift_len > 0 {
831 let data = page.as_bytes_mut();
832 data.copy_within(shift_from..shift_from + shift_len, shift_to);
833 }
834 }
835 leaf_write_slot(page, insert_pos, cell_offset as u16)?;
836
837 page.set_cell_count((cell_count + 1) as u16);
839 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2) as u16);
840 Ok(())
841}
842
843fn delete_from_leaf(page: &mut Page, index: usize) -> BTreeResult<()> {
848 let cell_count = page.cell_count() as usize;
849 if index >= cell_count {
850 return Err(BTreeError::Corrupted("delete index out of range".into()));
851 }
852 if index + 1 < cell_count {
853 let shift_from = leaf_slot_offset_for(index + 1);
854 let shift_to = leaf_slot_offset_for(index);
855 let shift_len = (cell_count - index - 1) * 2;
856 let data = page.as_bytes_mut();
857 data.copy_within(shift_from..shift_from + shift_len, shift_to);
858 }
859 page.set_cell_count((cell_count - 1) as u16);
860 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count - 1) * 2) as u16);
861 Ok(())
862}
863
864fn clear_leaf_cells(page: &mut Page) {
868 {
869 let data = page.as_bytes_mut();
870 for byte in &mut data[LEAF_SLOT_ARRAY_OFFSET..] {
871 *byte = 0;
872 }
873 }
874 page.set_cell_count(0);
875 page.set_free_start(LEAF_SLOT_ARRAY_OFFSET as u16);
876 page.set_free_end(PAGE_SIZE as u16);
877}
878
879fn init_leaf_links(page: &mut Page, prev: u32, next: u32) {
880 let data = page.as_bytes_mut();
881 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
882 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
883}
884
885fn read_next_leaf(page: &Page) -> u32 {
886 let data = page.as_bytes();
887 u32::from_le_bytes([
888 data[LEAF_NEXT_OFFSET],
889 data[LEAF_NEXT_OFFSET + 1],
890 data[LEAF_NEXT_OFFSET + 2],
891 data[LEAF_NEXT_OFFSET + 3],
892 ])
893}
894
895#[inline]
898fn leaf_right_sibling(page: &Page) -> u32 {
899 read_next_leaf(page)
900}
901
902fn leaf_high_key(page: &Page) -> BTreeResult<Option<Vec<u8>>> {
906 let cell_count = page.cell_count() as usize;
907 if cell_count == 0 {
908 return Ok(None);
909 }
910 let (key, _) = read_leaf_cell(page, cell_count - 1)?;
911 Ok(Some(key))
912}
913
914fn set_prev_leaf(page: &mut Page, prev: u32) {
915 let data = page.as_bytes_mut();
916 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
917}
918
919fn set_next_leaf(page: &mut Page, next: u32) {
920 let data = page.as_bytes_mut();
921 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
922}
923
924#[inline]
939fn interior_slot_offset_for(index: usize) -> usize {
940 INTERIOR_DATA_OFFSET + index * 2
941}
942
943#[inline]
944fn interior_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
945 let data = page.as_bytes();
946 let slot_pos = interior_slot_offset_for(index);
947 if slot_pos + 2 > PAGE_SIZE {
948 return Err(BTreeError::Corrupted(
949 "interior slot array overflows page".into(),
950 ));
951 }
952 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
953}
954
955#[inline]
956fn interior_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
957 let data = page.as_bytes_mut();
958 let slot_pos = interior_slot_offset_for(index);
959 if slot_pos + 2 > PAGE_SIZE {
960 return Err(BTreeError::Corrupted(
961 "interior slot array overflows page".into(),
962 ));
963 }
964 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
965 Ok(())
966}
967
968#[inline]
969fn interior_cells_start(page: &Page) -> usize {
970 let end = page.free_end() as usize;
971 if end == 0 {
972 PAGE_SIZE
973 } else {
974 end
975 }
976}
977
978#[inline]
979fn interior_free_bytes(page: &Page) -> usize {
980 let slot_end = INTERIOR_DATA_OFFSET + (page.cell_count() as usize) * 2;
981 interior_cells_start(page).saturating_sub(slot_end)
982}
983
984fn read_interior_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, u32)> {
985 let cell_count = page.cell_count() as usize;
986 if index >= cell_count {
987 return Err(BTreeError::Corrupted("Cell index out of range".into()));
988 }
989 let offset = interior_read_slot(page, index)?;
990 let data = page.as_bytes();
991 if offset + 2 > PAGE_SIZE {
992 return Err(BTreeError::Corrupted(
993 "interior cell header out of range".into(),
994 ));
995 }
996 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
997 let end = offset + 2 + key_len + 4;
998 if end > PAGE_SIZE {
999 return Err(BTreeError::Corrupted(
1000 "interior cell body out of range".into(),
1001 ));
1002 }
1003 let key = data[offset + 2..offset + 2 + key_len].to_vec();
1004 let child = u32::from_le_bytes([
1005 data[offset + 2 + key_len],
1006 data[offset + 2 + key_len + 1],
1007 data[offset + 2 + key_len + 2],
1008 data[offset + 2 + key_len + 3],
1009 ]);
1010 Ok((key, child))
1011}
1012
1013fn read_interior_keys_children(page: &Page) -> BTreeResult<(Vec<Vec<u8>>, Vec<u32>)> {
1014 let cell_count = page.cell_count() as usize;
1015 let mut keys = Vec::with_capacity(cell_count);
1016 let mut children = Vec::with_capacity(cell_count + 1);
1017
1018 for i in 0..cell_count {
1019 let (key, child) = read_interior_cell(page, i)?;
1020 keys.push(key);
1021 children.push(child);
1022 }
1023
1024 if cell_count == 0 {
1025 let right_child = page.right_child();
1026 if right_child != 0 {
1027 children.push(right_child);
1028 }
1029 } else {
1030 children.push(page.right_child());
1031 }
1032
1033 Ok((keys, children))
1034}
1035
1036fn write_interior_entries(page: &mut Page, keys: &[Vec<u8>], children: &[u32]) -> BTreeResult<()> {
1041 if !keys.is_empty() && children.len() != keys.len() + 1 {
1042 return Err(BTreeError::Corrupted(
1043 "Interior keys/children length mismatch".into(),
1044 ));
1045 }
1046
1047 clear_interior_cells(page);
1048 if keys.is_empty() {
1049 let right_child = children.first().copied().unwrap_or(0);
1050 page.set_right_child(right_child);
1051 return Ok(());
1052 }
1053
1054 for (i, key) in keys.iter().enumerate() {
1055 let cell_size = 2 + key.len() + 4;
1056 let cells_start = interior_cells_start(page);
1057 let slot_end = INTERIOR_DATA_OFFSET + (i + 1) * 2;
1058 if slot_end + cell_size > cells_start {
1059 return Err(BTreeError::Corrupted(
1060 "interior rebuild overflowed page".into(),
1061 ));
1062 }
1063 let cell_offset = cells_start - cell_size;
1064 {
1065 let data = page.as_bytes_mut();
1066 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1067 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1068 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1069 .copy_from_slice(&children[i].to_le_bytes());
1070 }
1071 page.set_free_end(cell_offset as u16);
1072 interior_write_slot(page, i, cell_offset as u16)?;
1073 }
1074 page.set_cell_count(keys.len() as u16);
1075 page.set_free_start((INTERIOR_DATA_OFFSET + keys.len() * 2) as u16);
1076 page.set_right_child(*children.last().ok_or_else(|| {
1077 BTreeError::Corrupted("write_interior_entries: children empty with non-empty keys".into())
1078 })?);
1079 Ok(())
1080}
1081
1082fn can_insert_interior(page: &Page, key: &[u8]) -> bool {
1083 let needed = 2 + 2 + key.len() + 4;
1084 interior_free_bytes(page) >= needed
1085}
1086
1087fn insert_into_interior(
1092 page: &mut Page,
1093 key: &[u8],
1094 left_child: u32,
1095 right_child: u32,
1096) -> BTreeResult<()> {
1097 let cell_count = page.cell_count() as usize;
1099 let mut low = 0;
1100 let mut high = cell_count;
1101 while low < high {
1102 let mid = (low + high) / 2;
1103 let (cell_key, _) = read_interior_cell(page, mid)?;
1104 match cell_key.as_slice().cmp(key) {
1105 Ordering::Less => low = mid + 1,
1106 Ordering::Greater | Ordering::Equal => high = mid,
1107 }
1108 }
1109 let insert_pos = low;
1110
1111 if insert_pos < cell_count {
1116 let old_offset = interior_read_slot(page, insert_pos)?;
1117 let data = page.as_bytes();
1118 let key_len = u16::from_le_bytes([data[old_offset], data[old_offset + 1]]) as usize;
1119 let child_pos = old_offset + 2 + key_len;
1120 let data = page.as_bytes_mut();
1121 data[child_pos..child_pos + 4].copy_from_slice(&right_child.to_le_bytes());
1122 } else {
1123 page.set_right_child(right_child);
1124 }
1125
1126 let cell_size = 2 + key.len() + 4;
1128 let slot_end_after = INTERIOR_DATA_OFFSET + (cell_count + 1) * 2;
1129 let cells_start = interior_cells_start(page);
1130 if slot_end_after + cell_size > cells_start {
1131 return Err(BTreeError::Corrupted("interior page full".into()));
1132 }
1133 let cell_offset = cells_start - cell_size;
1134 {
1135 let data = page.as_bytes_mut();
1136 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1137 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1138 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1139 .copy_from_slice(&left_child.to_le_bytes());
1140 }
1141 page.set_free_end(cell_offset as u16);
1142
1143 {
1146 let shift_from = interior_slot_offset_for(insert_pos);
1147 let shift_to = shift_from + 2;
1148 let shift_len = (cell_count - insert_pos) * 2;
1149 if shift_len > 0 {
1150 let data = page.as_bytes_mut();
1151 data.copy_within(shift_from..shift_from + shift_len, shift_to);
1152 }
1153 }
1154 interior_write_slot(page, insert_pos, cell_offset as u16)?;
1155 page.set_cell_count((cell_count + 1) as u16);
1156 page.set_free_start((INTERIOR_DATA_OFFSET + (cell_count + 1) * 2) as u16);
1157 Ok(())
1158}
1159
1160fn clear_interior_cells(page: &mut Page) {
1161 {
1162 let data = page.as_bytes_mut();
1163 for byte in &mut data[INTERIOR_DATA_OFFSET..] {
1164 *byte = 0;
1165 }
1166 }
1167 page.set_cell_count(0);
1168 page.set_free_start(INTERIOR_DATA_OFFSET as u16);
1169 page.set_free_end(PAGE_SIZE as u16);
1170}