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 with_immutable_leaves(mut self) -> Self {
206 self.immutable = true;
207 self
208 }
209
210 fn prepare_immutable_leaf(&mut self) -> BTreeResult<bool> {
211 if self.leaf_page_id == 0 {
212 return Ok(false);
213 }
214 if self.immutable_leaf.is_none() {
215 let page = self.pager.read_page(self.leaf_page_id)?;
216 if page.page_type()? != PageType::BTreeLeaf {
217 return Err(BTreeError::Corrupted(
218 "cursor reached a non-leaf page".into(),
219 ));
220 }
221 self.immutable_leaf = Some(Box::new(page));
222 }
223 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
224 if self.position < usize::from(page.cell_count()) {
225 return Ok(true);
226 }
227 let next = read_next_leaf(page);
228 if next == 0 {
229 self.leaf_page_id = 0;
230 self.immutable_leaf = None;
231 return Ok(false);
232 }
233 let next_page = self.pager.read_page(next)?;
234 if next_page.page_type()? != PageType::BTreeLeaf || next_page.cell_count() == 0 {
235 return Err(BTreeError::Corrupted(
236 "leaf chain reached an invalid leaf".into(),
237 ));
238 }
239 let first_key = read_leaf_cell_borrowed(&next_page, 0)?.0;
240 if leaf_high_key(page)?
241 .as_ref()
242 .is_some_and(|key| first_key <= key.as_slice())
243 {
244 return Err(BTreeError::Corrupted(
245 "leaf chain does not advance its key range".into(),
246 ));
247 }
248 **self.immutable_leaf.as_mut().expect("loaded immutable leaf") = next_page;
249 self.leaf_page_id = next;
250 self.position = 0;
251 self.prefetched_next = false;
252 Ok(true)
253 }
254
255 pub(crate) fn peek_key_with<T>(
257 &mut self,
258 visit: impl FnOnce(&[u8]) -> T,
259 ) -> BTreeResult<Option<T>> {
260 if self.immutable {
261 if !self.prepare_immutable_leaf()? {
262 return Ok(None);
263 }
264 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
265 return Ok(Some(visit(read_leaf_cell_borrowed(page, self.position)?.0)));
266 }
267 let Some(page) = self.prepare_next_leaf()? else {
268 return Ok(None);
269 };
270 Ok(Some(visit(
271 read_leaf_cell_borrowed(&page, self.position)?.0,
272 )))
273 }
274
275 pub fn peek_key(&mut self) -> BTreeResult<Option<Vec<u8>>> {
277 if self.immutable {
278 if !self.prepare_immutable_leaf()? {
279 return Ok(None);
280 }
281 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
282 return Ok(Some(
283 read_leaf_cell_borrowed(page, self.position)?.0.to_vec(),
284 ));
285 }
286 let Some(page) = self.prepare_next_leaf()? else {
287 return Ok(None);
288 };
289 Ok(Some(read_leaf_cell(&page, self.position)?.0))
290 }
291
292 pub fn skip_next(&mut self) -> BTreeResult<bool> {
294 if self.immutable {
295 if !self.prepare_immutable_leaf()? {
296 return Ok(false);
297 }
298 self.position += 1;
299 return Ok(true);
300 }
301 if self.prepare_next_leaf()?.is_none() {
302 return Ok(false);
303 }
304 self.position += 1;
305 Ok(true)
306 }
307
308 fn prepare_next_leaf(&mut self) -> BTreeResult<Option<Page>> {
309 if self.leaf_page_id == 0 {
310 return Ok(None);
311 }
312 let page = self.pager.read_page(self.leaf_page_id)?;
313 if page.page_type()? != PageType::BTreeLeaf {
314 return Err(BTreeError::Corrupted(
315 "cursor reached a non-leaf page".into(),
316 ));
317 }
318 if self.position < usize::from(page.cell_count()) {
319 return Ok(Some(page));
320 }
321 let next = read_next_leaf(&page);
322 if next == 0 {
323 self.leaf_page_id = 0;
324 return Ok(None);
325 }
326 let next_page = self.pager.read_page(next)?;
327 if next_page.page_type()? != PageType::BTreeLeaf || next_page.cell_count() == 0 {
328 return Err(BTreeError::Corrupted(
329 "leaf chain reached an invalid leaf".into(),
330 ));
331 }
332 let first_key = read_leaf_cell(&next_page, 0)?.0;
333 if leaf_high_key(&page)?
334 .as_ref()
335 .is_some_and(|key| first_key <= *key)
336 {
337 return Err(BTreeError::Corrupted(
338 "leaf chain does not advance its key range".into(),
339 ));
340 }
341 self.leaf_page_id = next;
342 self.position = 0;
343 self.prefetched_next = false;
344 Ok(Some(next_page))
345 }
346
347 pub fn next_with_budget(
349 &mut self,
350 limit_bytes: usize,
351 ) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
352 self.next_impl(limit_bytes)
353 }
354
355 pub fn next(&mut self) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
357 self.next_impl(usize::MAX)
358 }
359
360 fn next_impl(&mut self, limit_bytes: usize) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
361 if self.immutable {
362 return self.next_immutable(limit_bytes);
363 }
364 if self.leaf_page_id == 0 {
365 return Ok(None);
366 }
367
368 let page = self.pager.read_page(self.leaf_page_id)?;
369 if page.page_type()? != PageType::BTreeLeaf {
370 return Err(BTreeError::Corrupted(
371 "cursor reached a non-leaf page".into(),
372 ));
373 }
374 let cell_count = page.cell_count() as usize;
375
376 if !self.prefetched_next && self.position >= cell_count / 2 && cell_count > 0 {
382 let next_leaf = read_next_leaf(&page);
383 if next_leaf != 0 {
384 self.pager.prefetch_hint(next_leaf);
385 }
386 self.prefetched_next = true;
387 }
388
389 if self.position < cell_count {
391 let (key, stored) = read_leaf_cell(&page, self.position)?;
392 let value =
393 value_layout::decode_with_budget(self.pager.as_ref(), &stored, limit_bytes)?;
394 self.position += 1;
395 return Ok(Some((key, value)));
396 }
397
398 let next_leaf = read_next_leaf(&page);
400 if next_leaf == 0 {
401 self.leaf_page_id = 0;
402 return Ok(None);
403 }
404 let previous_high_key = leaf_high_key(&page)?;
405
406 self.leaf_page_id = next_leaf;
407 self.position = 0;
408 self.prefetched_next = false; let page = self.pager.read_page(self.leaf_page_id)?;
412 if page.page_type()? != PageType::BTreeLeaf {
413 return Err(BTreeError::Corrupted(
414 "leaf chain reached a non-leaf page".into(),
415 ));
416 }
417 if page.cell_count() == 0 {
418 return Ok(None);
419 }
420
421 let (key, stored) = read_leaf_cell(&page, 0)?;
422 if previous_high_key
426 .as_ref()
427 .is_some_and(|previous| key <= *previous)
428 {
429 self.leaf_page_id = 0;
430 return Err(BTreeError::Corrupted(
431 "leaf chain does not advance its key range".into(),
432 ));
433 }
434 let value = value_layout::decode_with_budget(self.pager.as_ref(), &stored, limit_bytes)?;
435 self.position = 1;
436 Ok(Some((key, value)))
437 }
438
439 fn next_immutable(&mut self, limit_bytes: usize) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
440 if !self.prepare_immutable_leaf()? {
441 return Ok(None);
442 }
443 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
444 if !self.prefetched_next && self.position >= usize::from(page.cell_count()) / 2 {
445 let next = read_next_leaf(page);
446 if next != 0 {
447 self.pager.prefetch_hint(next);
448 }
449 self.prefetched_next = true;
450 }
451 let (key, stored) = read_leaf_cell_borrowed(page, self.position)?;
452 let value = value_layout::decode_with_budget(self.pager.as_ref(), stored, limit_bytes)?;
453 self.position += 1;
454 Ok(Some((key.to_vec(), value)))
455 }
456
457 pub fn peek(&self) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
459 if self.leaf_page_id == 0 {
460 return Ok(None);
461 }
462
463 let page = self.pager.read_page(self.leaf_page_id)?;
464 let cell_count = page.cell_count() as usize;
465
466 if self.position >= cell_count {
467 return Ok(None);
468 }
469
470 let (key, stored) = read_leaf_cell(&page, self.position)?;
471 let value = value_layout::decode(self.pager.as_ref(), &stored)?;
472 Ok(Some((key, value)))
473 }
474}
475
476pub struct BTree<P: PageStore> {
478 retirement_pin: Arc<()>,
479 pager: Arc<P>,
481 root_page_id: RwLock<u32>,
483 rightmost_leaf: RwLock<Option<(u32, Vec<u8>)>>,
492 writer: parking_lot::ReentrantMutex<()>,
500}
501
502#[path = "btree/impl.rs"]
503mod btree_impl;
504
505#[path = "btree/reclaim.rs"]
506mod reclaim;
507
508#[cfg(test)]
509#[path = "btree/read_budget_tests.rs"]
510mod read_budget_tests;
511
512#[path = "btree/lehman_yao.rs"]
513pub mod lehman_yao;
514
515#[path = "btree/value_layout.rs"]
516pub mod value_layout;
517enum SearchResult {
520 Found(usize),
521 NotFound(usize),
522}
523
524fn search_leaf(page: &Page, key: &[u8]) -> BTreeResult<SearchResult> {
525 let cell_count = page.cell_count() as usize;
526
527 let mut low = 0;
529 let mut high = cell_count;
530
531 while low < high {
532 let mid = (low + high) / 2;
533 let (cell_key, _) = read_leaf_cell(page, mid)?;
534
535 match cell_key.as_slice().cmp(key) {
536 Ordering::Less => low = mid + 1,
537 Ordering::Greater => high = mid,
538 Ordering::Equal => return Ok(SearchResult::Found(mid)),
539 }
540 }
541
542 Ok(SearchResult::NotFound(low))
543}
544
545fn find_child(page: &Page, key: &[u8]) -> BTreeResult<u32> {
546 let cell_count = page.cell_count() as usize;
547
548 for i in 0..cell_count {
550 let (cell_key, child) = read_interior_cell(page, i)?;
551 if key < cell_key.as_slice() {
552 return Ok(child);
553 }
554 }
555
556 Ok(page.right_child())
558}
559
560fn find_first_child(page: &Page) -> BTreeResult<u32> {
561 if page.cell_count() == 0 {
562 return Ok(page.right_child());
563 }
564 let (_, child) = read_interior_cell(page, 0)?;
565 Ok(child)
566}
567
568fn leaf_min_bytes() -> usize {
569 (PAGE_SIZE - LEAF_DATA_OFFSET) * MIN_FILL_FACTOR / 100
570}
571
572fn interior_min_bytes() -> usize {
573 (PAGE_SIZE - INTERIOR_DATA_OFFSET) * MIN_FILL_FACTOR / 100
574}
575
576fn leaf_entry_size(entry: &(Vec<u8>, Vec<u8>)) -> usize {
580 2 + 4 + entry.0.len() + entry.1.len()
581}
582
583fn leaf_entries_size(entries: &[(Vec<u8>, Vec<u8>)]) -> usize {
584 entries.iter().map(leaf_entry_size).sum()
585}
586
587#[inline]
588fn leaf_slot_offset_for(index: usize) -> usize {
589 LEAF_SLOT_ARRAY_OFFSET + index * 2
590}
591
592#[inline]
593fn leaf_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
594 let data = page.as_bytes();
595 let slot_pos = leaf_slot_offset_for(index);
596 if slot_pos + 2 > PAGE_SIZE {
597 return Err(BTreeError::Corrupted("slot array overflows page".into()));
598 }
599 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
600}
601
602#[inline]
603fn leaf_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
604 let data = page.as_bytes_mut();
605 let slot_pos = leaf_slot_offset_for(index);
606 if slot_pos + 2 > PAGE_SIZE {
607 return Err(BTreeError::Corrupted("slot array overflows page".into()));
608 }
609 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
610 Ok(())
611}
612
613#[inline]
614fn leaf_slots_end(page: &Page) -> usize {
615 LEAF_SLOT_ARRAY_OFFSET + (page.cell_count() as usize) * 2
616}
617
618#[inline]
619fn leaf_cells_start(page: &Page) -> usize {
620 let end = page.free_end() as usize;
621 if end == 0 {
622 PAGE_SIZE
623 } else {
624 end
625 }
626}
627
628#[inline]
629fn leaf_free_bytes(page: &Page) -> usize {
630 let slot_end = leaf_slots_end(page);
631 let cells = leaf_cells_start(page);
632 cells.saturating_sub(slot_end)
633}
634
635fn interior_key_size(key: &[u8]) -> usize {
636 2 + key.len() + 4
637}
638
639fn interior_entries_size(keys: &[Vec<u8>]) -> usize {
640 keys.iter().map(|k| interior_key_size(k)).sum()
641}
642
643fn read_leaf_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, Vec<u8>)> {
649 let (key, value) = read_leaf_cell_borrowed(page, index)?;
650 Ok((key.to_vec(), value.to_vec()))
651}
652
653fn read_leaf_cell_borrowed(page: &Page, index: usize) -> BTreeResult<(&[u8], &[u8])> {
654 let cell_count = page.cell_count() as usize;
655 if index >= cell_count {
656 return Err(BTreeError::Corrupted("Cell index out of range".into()));
657 }
658 let offset = leaf_read_slot(page, index)?;
659 let data = page.as_bytes();
660 if offset + 4 > PAGE_SIZE {
661 return Err(BTreeError::Corrupted("cell header out of range".into()));
662 }
663 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
664 let val_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
665 let end = offset + 4 + key_len + val_len;
666 if end > PAGE_SIZE {
667 return Err(BTreeError::Corrupted("cell body out of range".into()));
668 }
669 let key = &data[offset + 4..offset + 4 + key_len];
670 let value = &data[offset + 4 + key_len..end];
671 Ok((key, value))
672}
673
674fn read_leaf_entries(page: &Page) -> BTreeResult<Vec<(Vec<u8>, Vec<u8>)>> {
675 let cell_count = page.cell_count() as usize;
676 let mut entries = Vec::with_capacity(cell_count);
677 for i in 0..cell_count {
678 entries.push(read_leaf_cell(page, i)?);
679 }
680 Ok(entries)
681}
682
683fn write_leaf_entries(page: &mut Page, entries: &[(Vec<u8>, Vec<u8>)]) -> BTreeResult<()> {
689 clear_leaf_cells(page);
690 for (i, (k, v)) in entries.iter().enumerate() {
691 let cell_size = 4 + k.len() + v.len();
692 let cells_start = leaf_cells_start(page);
693 let slot_end = LEAF_SLOT_ARRAY_OFFSET + (i + 1) * 2;
694 if slot_end + cell_size > cells_start {
695 return Err(BTreeError::Corrupted("leaf rebuild overflowed page".into()));
696 }
697 let cell_offset = cells_start - cell_size;
698 {
699 let data = page.as_bytes_mut();
700 data[cell_offset..cell_offset + 2].copy_from_slice(&(k.len() as u16).to_le_bytes());
701 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(v.len() as u16).to_le_bytes());
702 data[cell_offset + 4..cell_offset + 4 + k.len()].copy_from_slice(k);
703 data[cell_offset + 4 + k.len()..cell_offset + 4 + k.len() + v.len()].copy_from_slice(v);
704 }
705 page.set_free_end(cell_offset as u16);
706 leaf_write_slot(page, i, cell_offset as u16)?;
707 }
708 page.set_cell_count(entries.len() as u16);
709 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + entries.len() * 2) as u16);
710 Ok(())
711}
712
713fn can_insert_leaf(page: &Page, key: &[u8], value: &[u8]) -> bool {
714 let needed = 2 + 4 + key.len() + value.len();
715 leaf_free_bytes(page) >= needed
716}
717
718pub(crate) fn overwrite_leaf_value(
734 page: &mut Page,
735 index: usize,
736 new_value: &[u8],
737) -> BTreeResult<()> {
738 let cell_offset = leaf_read_slot(page, index)?;
739 let data = page.as_bytes();
740 if cell_offset + 4 > data.len() {
741 return Err(BTreeError::Corrupted(
742 "leaf cell header out of bounds".into(),
743 ));
744 }
745 let k_len = u16::from_le_bytes([data[cell_offset], data[cell_offset + 1]]) as usize;
746 let old_v_len = u16::from_le_bytes([data[cell_offset + 2], data[cell_offset + 3]]) as usize;
747 if new_value.len() > old_v_len {
748 return Err(BTreeError::Corrupted(
749 "overwrite_leaf_value: new value larger than slot".into(),
750 ));
751 }
752 let value_start = cell_offset + 4 + k_len;
753 if value_start + new_value.len() > data.len() {
754 return Err(BTreeError::Corrupted(
755 "leaf cell value out of bounds".into(),
756 ));
757 }
758 let data = page.as_bytes_mut();
759 if new_value.len() != old_v_len {
762 let new_len = new_value.len() as u16;
763 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&new_len.to_le_bytes());
764 }
765 data[value_start..value_start + new_value.len()].copy_from_slice(new_value);
766 Ok(())
767}
768
769fn insert_into_leaf(page: &mut Page, key: &[u8], value: &[u8]) -> BTreeResult<()> {
771 let cell_count = page.cell_count() as usize;
773 let mut low = 0;
774 let mut high = cell_count;
775 while low < high {
776 let mid = (low + high) / 2;
777 let (cell_key, _) = read_leaf_cell(page, mid)?;
778 match cell_key.as_slice().cmp(key) {
779 Ordering::Less => low = mid + 1,
780 Ordering::Greater => high = mid,
781 Ordering::Equal => {
782 low = mid + 1;
785 break;
786 }
787 }
788 }
789 let insert_pos = low;
790
791 let cell_size = 4 + key.len() + value.len();
793 let slot_end_after = LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2;
794 let cells_start = leaf_cells_start(page);
795 if slot_end_after + cell_size > cells_start {
796 return Err(BTreeError::Corrupted("leaf page full".into()));
797 }
798 let cell_offset = cells_start - cell_size;
799 {
800 let data = page.as_bytes_mut();
801 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
802 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(value.len() as u16).to_le_bytes());
803 data[cell_offset + 4..cell_offset + 4 + key.len()].copy_from_slice(key);
804 data[cell_offset + 4 + key.len()..cell_offset + 4 + key.len() + value.len()]
805 .copy_from_slice(value);
806 }
807 page.set_free_end(cell_offset as u16);
808
809 {
813 let shift_from = leaf_slot_offset_for(insert_pos);
814 let shift_to = shift_from + 2;
815 let shift_len = (cell_count - insert_pos) * 2;
816 if shift_len > 0 {
817 let data = page.as_bytes_mut();
818 data.copy_within(shift_from..shift_from + shift_len, shift_to);
819 }
820 }
821 leaf_write_slot(page, insert_pos, cell_offset as u16)?;
822
823 page.set_cell_count((cell_count + 1) as u16);
825 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2) as u16);
826 Ok(())
827}
828
829fn delete_from_leaf(page: &mut Page, index: usize) -> BTreeResult<()> {
834 let cell_count = page.cell_count() as usize;
835 if index >= cell_count {
836 return Err(BTreeError::Corrupted("delete index out of range".into()));
837 }
838 if index + 1 < cell_count {
839 let shift_from = leaf_slot_offset_for(index + 1);
840 let shift_to = leaf_slot_offset_for(index);
841 let shift_len = (cell_count - index - 1) * 2;
842 let data = page.as_bytes_mut();
843 data.copy_within(shift_from..shift_from + shift_len, shift_to);
844 }
845 page.set_cell_count((cell_count - 1) as u16);
846 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count - 1) * 2) as u16);
847 Ok(())
848}
849
850fn clear_leaf_cells(page: &mut Page) {
854 {
855 let data = page.as_bytes_mut();
856 for byte in &mut data[LEAF_SLOT_ARRAY_OFFSET..] {
857 *byte = 0;
858 }
859 }
860 page.set_cell_count(0);
861 page.set_free_start(LEAF_SLOT_ARRAY_OFFSET as u16);
862 page.set_free_end(PAGE_SIZE as u16);
863}
864
865fn init_leaf_links(page: &mut Page, prev: u32, next: u32) {
866 let data = page.as_bytes_mut();
867 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
868 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
869}
870
871fn read_next_leaf(page: &Page) -> u32 {
872 let data = page.as_bytes();
873 u32::from_le_bytes([
874 data[LEAF_NEXT_OFFSET],
875 data[LEAF_NEXT_OFFSET + 1],
876 data[LEAF_NEXT_OFFSET + 2],
877 data[LEAF_NEXT_OFFSET + 3],
878 ])
879}
880
881#[inline]
884fn leaf_right_sibling(page: &Page) -> u32 {
885 read_next_leaf(page)
886}
887
888fn leaf_high_key(page: &Page) -> BTreeResult<Option<Vec<u8>>> {
892 let cell_count = page.cell_count() as usize;
893 if cell_count == 0 {
894 return Ok(None);
895 }
896 let (key, _) = read_leaf_cell(page, cell_count - 1)?;
897 Ok(Some(key))
898}
899
900fn set_prev_leaf(page: &mut Page, prev: u32) {
901 let data = page.as_bytes_mut();
902 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
903}
904
905fn set_next_leaf(page: &mut Page, next: u32) {
906 let data = page.as_bytes_mut();
907 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
908}
909
910#[inline]
925fn interior_slot_offset_for(index: usize) -> usize {
926 INTERIOR_DATA_OFFSET + index * 2
927}
928
929#[inline]
930fn interior_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
931 let data = page.as_bytes();
932 let slot_pos = interior_slot_offset_for(index);
933 if slot_pos + 2 > PAGE_SIZE {
934 return Err(BTreeError::Corrupted(
935 "interior slot array overflows page".into(),
936 ));
937 }
938 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
939}
940
941#[inline]
942fn interior_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
943 let data = page.as_bytes_mut();
944 let slot_pos = interior_slot_offset_for(index);
945 if slot_pos + 2 > PAGE_SIZE {
946 return Err(BTreeError::Corrupted(
947 "interior slot array overflows page".into(),
948 ));
949 }
950 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
951 Ok(())
952}
953
954#[inline]
955fn interior_cells_start(page: &Page) -> usize {
956 let end = page.free_end() as usize;
957 if end == 0 {
958 PAGE_SIZE
959 } else {
960 end
961 }
962}
963
964#[inline]
965fn interior_free_bytes(page: &Page) -> usize {
966 let slot_end = INTERIOR_DATA_OFFSET + (page.cell_count() as usize) * 2;
967 interior_cells_start(page).saturating_sub(slot_end)
968}
969
970fn read_interior_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, u32)> {
971 let cell_count = page.cell_count() as usize;
972 if index >= cell_count {
973 return Err(BTreeError::Corrupted("Cell index out of range".into()));
974 }
975 let offset = interior_read_slot(page, index)?;
976 let data = page.as_bytes();
977 if offset + 2 > PAGE_SIZE {
978 return Err(BTreeError::Corrupted(
979 "interior cell header out of range".into(),
980 ));
981 }
982 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
983 let end = offset + 2 + key_len + 4;
984 if end > PAGE_SIZE {
985 return Err(BTreeError::Corrupted(
986 "interior cell body out of range".into(),
987 ));
988 }
989 let key = data[offset + 2..offset + 2 + key_len].to_vec();
990 let child = u32::from_le_bytes([
991 data[offset + 2 + key_len],
992 data[offset + 2 + key_len + 1],
993 data[offset + 2 + key_len + 2],
994 data[offset + 2 + key_len + 3],
995 ]);
996 Ok((key, child))
997}
998
999fn read_interior_keys_children(page: &Page) -> BTreeResult<(Vec<Vec<u8>>, Vec<u32>)> {
1000 let cell_count = page.cell_count() as usize;
1001 let mut keys = Vec::with_capacity(cell_count);
1002 let mut children = Vec::with_capacity(cell_count + 1);
1003
1004 for i in 0..cell_count {
1005 let (key, child) = read_interior_cell(page, i)?;
1006 keys.push(key);
1007 children.push(child);
1008 }
1009
1010 if cell_count == 0 {
1011 let right_child = page.right_child();
1012 if right_child != 0 {
1013 children.push(right_child);
1014 }
1015 } else {
1016 children.push(page.right_child());
1017 }
1018
1019 Ok((keys, children))
1020}
1021
1022fn write_interior_entries(page: &mut Page, keys: &[Vec<u8>], children: &[u32]) -> BTreeResult<()> {
1027 if !keys.is_empty() && children.len() != keys.len() + 1 {
1028 return Err(BTreeError::Corrupted(
1029 "Interior keys/children length mismatch".into(),
1030 ));
1031 }
1032
1033 clear_interior_cells(page);
1034 if keys.is_empty() {
1035 let right_child = children.first().copied().unwrap_or(0);
1036 page.set_right_child(right_child);
1037 return Ok(());
1038 }
1039
1040 for (i, key) in keys.iter().enumerate() {
1041 let cell_size = 2 + key.len() + 4;
1042 let cells_start = interior_cells_start(page);
1043 let slot_end = INTERIOR_DATA_OFFSET + (i + 1) * 2;
1044 if slot_end + cell_size > cells_start {
1045 return Err(BTreeError::Corrupted(
1046 "interior rebuild overflowed page".into(),
1047 ));
1048 }
1049 let cell_offset = cells_start - cell_size;
1050 {
1051 let data = page.as_bytes_mut();
1052 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1053 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1054 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1055 .copy_from_slice(&children[i].to_le_bytes());
1056 }
1057 page.set_free_end(cell_offset as u16);
1058 interior_write_slot(page, i, cell_offset as u16)?;
1059 }
1060 page.set_cell_count(keys.len() as u16);
1061 page.set_free_start((INTERIOR_DATA_OFFSET + keys.len() * 2) as u16);
1062 page.set_right_child(*children.last().ok_or_else(|| {
1063 BTreeError::Corrupted("write_interior_entries: children empty with non-empty keys".into())
1064 })?);
1065 Ok(())
1066}
1067
1068fn can_insert_interior(page: &Page, key: &[u8]) -> bool {
1069 let needed = 2 + 2 + key.len() + 4;
1070 interior_free_bytes(page) >= needed
1071}
1072
1073fn insert_into_interior(
1078 page: &mut Page,
1079 key: &[u8],
1080 left_child: u32,
1081 right_child: u32,
1082) -> BTreeResult<()> {
1083 let cell_count = page.cell_count() as usize;
1085 let mut low = 0;
1086 let mut high = cell_count;
1087 while low < high {
1088 let mid = (low + high) / 2;
1089 let (cell_key, _) = read_interior_cell(page, mid)?;
1090 match cell_key.as_slice().cmp(key) {
1091 Ordering::Less => low = mid + 1,
1092 Ordering::Greater | Ordering::Equal => high = mid,
1093 }
1094 }
1095 let insert_pos = low;
1096
1097 if insert_pos < cell_count {
1102 let old_offset = interior_read_slot(page, insert_pos)?;
1103 let data = page.as_bytes();
1104 let key_len = u16::from_le_bytes([data[old_offset], data[old_offset + 1]]) as usize;
1105 let child_pos = old_offset + 2 + key_len;
1106 let data = page.as_bytes_mut();
1107 data[child_pos..child_pos + 4].copy_from_slice(&right_child.to_le_bytes());
1108 } else {
1109 page.set_right_child(right_child);
1110 }
1111
1112 let cell_size = 2 + key.len() + 4;
1114 let slot_end_after = INTERIOR_DATA_OFFSET + (cell_count + 1) * 2;
1115 let cells_start = interior_cells_start(page);
1116 if slot_end_after + cell_size > cells_start {
1117 return Err(BTreeError::Corrupted("interior page full".into()));
1118 }
1119 let cell_offset = cells_start - cell_size;
1120 {
1121 let data = page.as_bytes_mut();
1122 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1123 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1124 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1125 .copy_from_slice(&left_child.to_le_bytes());
1126 }
1127 page.set_free_end(cell_offset as u16);
1128
1129 {
1132 let shift_from = interior_slot_offset_for(insert_pos);
1133 let shift_to = shift_from + 2;
1134 let shift_len = (cell_count - insert_pos) * 2;
1135 if shift_len > 0 {
1136 let data = page.as_bytes_mut();
1137 data.copy_within(shift_from..shift_from + shift_len, shift_to);
1138 }
1139 }
1140 interior_write_slot(page, insert_pos, cell_offset as u16)?;
1141 page.set_cell_count((cell_count + 1) as u16);
1142 page.set_free_start((INTERIOR_DATA_OFFSET + (cell_count + 1) * 2) as u16);
1143 Ok(())
1144}
1145
1146fn clear_interior_cells(page: &mut Page) {
1147 {
1148 let data = page.as_bytes_mut();
1149 for byte in &mut data[INTERIOR_DATA_OFFSET..] {
1150 *byte = 0;
1151 }
1152 }
1153 page.set_cell_count(0);
1154 page.set_free_start(INTERIOR_DATA_OFFSET as u16);
1155 page.set_free_end(PAGE_SIZE as u16);
1156}