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 #[cfg(feature = "full")]
271 pub(crate) fn peek_key_with<T>(
272 &mut self,
273 visit: impl FnOnce(&[u8]) -> T,
274 ) -> BTreeResult<Option<T>> {
275 if self.immutable {
276 if !self.prepare_immutable_leaf()? {
277 return Ok(None);
278 }
279 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
280 return Ok(Some(visit(read_leaf_cell_borrowed(page, self.position)?.0)));
281 }
282 let Some(page) = self.prepare_next_leaf()? else {
283 return Ok(None);
284 };
285 Ok(Some(visit(
286 read_leaf_cell_borrowed(&page, self.position)?.0,
287 )))
288 }
289
290 pub fn peek_key(&mut self) -> BTreeResult<Option<Vec<u8>>> {
292 if self.immutable {
293 if !self.prepare_immutable_leaf()? {
294 return Ok(None);
295 }
296 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
297 return Ok(Some(
298 read_leaf_cell_borrowed(page, self.position)?.0.to_vec(),
299 ));
300 }
301 let Some(page) = self.prepare_next_leaf()? else {
302 return Ok(None);
303 };
304 Ok(Some(read_leaf_cell(&page, self.position)?.0))
305 }
306
307 pub fn skip_next(&mut self) -> BTreeResult<bool> {
309 if self.immutable {
310 if !self.prepare_immutable_leaf()? {
311 return Ok(false);
312 }
313 self.position += 1;
314 return Ok(true);
315 }
316 if self.prepare_next_leaf()?.is_none() {
317 return Ok(false);
318 }
319 self.position += 1;
320 Ok(true)
321 }
322
323 fn prepare_next_leaf(&mut self) -> BTreeResult<Option<Page>> {
324 if self.leaf_page_id == 0 {
325 return Ok(None);
326 }
327 let page = self.pager.read_page(self.leaf_page_id)?;
328 if page.page_type()? != PageType::BTreeLeaf {
329 return Err(BTreeError::Corrupted(
330 "cursor reached a non-leaf page".into(),
331 ));
332 }
333 if self.position < usize::from(page.cell_count()) {
334 return Ok(Some(page));
335 }
336 let next = read_next_leaf(&page);
337 if next == 0 {
338 self.leaf_page_id = 0;
339 return Ok(None);
340 }
341 let next_page = self.pager.read_page(next)?;
342 if next_page.page_type()? != PageType::BTreeLeaf || next_page.cell_count() == 0 {
343 return Err(BTreeError::Corrupted(
344 "leaf chain reached an invalid leaf".into(),
345 ));
346 }
347 let first_key = read_leaf_cell(&next_page, 0)?.0;
348 if leaf_high_key(&page)?
349 .as_ref()
350 .is_some_and(|key| first_key <= *key)
351 {
352 return Err(BTreeError::Corrupted(
353 "leaf chain does not advance its key range".into(),
354 ));
355 }
356 self.leaf_page_id = next;
357 self.position = 0;
358 self.prefetched_next = false;
359 Ok(Some(next_page))
360 }
361
362 pub fn next_with_budget(
364 &mut self,
365 limit_bytes: usize,
366 ) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
367 self.next_impl(limit_bytes)
368 }
369
370 pub fn next(&mut self) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
372 self.next_impl(usize::MAX)
373 }
374
375 fn next_impl(&mut self, limit_bytes: usize) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
376 if self.immutable {
377 return self.next_immutable(limit_bytes);
378 }
379 if self.leaf_page_id == 0 {
380 return Ok(None);
381 }
382
383 let page = self.pager.read_page(self.leaf_page_id)?;
384 if page.page_type()? != PageType::BTreeLeaf {
385 return Err(BTreeError::Corrupted(
386 "cursor reached a non-leaf page".into(),
387 ));
388 }
389 let cell_count = page.cell_count() as usize;
390
391 if !self.prefetched_next && self.position >= cell_count / 2 && cell_count > 0 {
397 let next_leaf = read_next_leaf(&page);
398 if next_leaf != 0 {
399 self.pager.prefetch_hint(next_leaf);
400 }
401 self.prefetched_next = true;
402 }
403
404 if self.position < cell_count {
406 let (key, stored) = read_leaf_cell(&page, self.position)?;
407 let value =
408 value_layout::decode_with_budget(self.pager.as_ref(), &stored, limit_bytes)?;
409 self.position += 1;
410 return Ok(Some((key, value)));
411 }
412
413 let next_leaf = read_next_leaf(&page);
415 if next_leaf == 0 {
416 self.leaf_page_id = 0;
417 return Ok(None);
418 }
419 let previous_high_key = leaf_high_key(&page)?;
420
421 self.leaf_page_id = next_leaf;
422 self.position = 0;
423 self.prefetched_next = false; let page = self.pager.read_page(self.leaf_page_id)?;
427 if page.page_type()? != PageType::BTreeLeaf {
428 return Err(BTreeError::Corrupted(
429 "leaf chain reached a non-leaf page".into(),
430 ));
431 }
432 if page.cell_count() == 0 {
433 return Ok(None);
434 }
435
436 let (key, stored) = read_leaf_cell(&page, 0)?;
437 if previous_high_key
441 .as_ref()
442 .is_some_and(|previous| key <= *previous)
443 {
444 self.leaf_page_id = 0;
445 return Err(BTreeError::Corrupted(
446 "leaf chain does not advance its key range".into(),
447 ));
448 }
449 let value = value_layout::decode_with_budget(self.pager.as_ref(), &stored, limit_bytes)?;
450 self.position = 1;
451 Ok(Some((key, value)))
452 }
453
454 fn next_immutable(&mut self, limit_bytes: usize) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
455 if !self.prepare_immutable_leaf()? {
456 return Ok(None);
457 }
458 let page = self.immutable_leaf.as_ref().expect("loaded immutable leaf");
459 if !self.prefetched_next && self.position >= usize::from(page.cell_count()) / 2 {
460 let next = read_next_leaf(page);
461 if next != 0 {
462 self.pager.prefetch_hint(next);
463 }
464 self.prefetched_next = true;
465 }
466 let (key, stored) = read_leaf_cell_borrowed(page, self.position)?;
467 let value = value_layout::decode_with_budget(self.pager.as_ref(), stored, limit_bytes)?;
468 self.position += 1;
469 Ok(Some((key.to_vec(), value)))
470 }
471
472 pub fn peek(&self) -> BTreeResult<Option<(Vec<u8>, Vec<u8>)>> {
474 if self.leaf_page_id == 0 {
475 return Ok(None);
476 }
477
478 let page = self.pager.read_page(self.leaf_page_id)?;
479 let cell_count = page.cell_count() as usize;
480
481 if self.position >= cell_count {
482 return Ok(None);
483 }
484
485 let (key, stored) = read_leaf_cell(&page, self.position)?;
486 let value = value_layout::decode(self.pager.as_ref(), &stored)?;
487 Ok(Some((key, value)))
488 }
489}
490
491pub struct BTree<P: PageStore> {
493 retirement_pin: Arc<()>,
494 pager: Arc<P>,
496 root_page_id: RwLock<u32>,
498 rightmost_leaf: RwLock<Option<(u32, Vec<u8>)>>,
507 writer: parking_lot::ReentrantMutex<()>,
515}
516
517#[path = "btree/impl.rs"]
518mod btree_impl;
519
520#[path = "btree/reclaim.rs"]
521mod reclaim;
522
523#[cfg(test)]
524#[path = "btree/read_budget_tests.rs"]
525mod read_budget_tests;
526
527#[path = "btree/lehman_yao.rs"]
528pub mod lehman_yao;
529
530#[path = "btree/value_layout.rs"]
531pub mod value_layout;
532enum SearchResult {
535 Found(usize),
536 NotFound(usize),
537}
538
539fn search_leaf(page: &Page, key: &[u8]) -> BTreeResult<SearchResult> {
540 let cell_count = page.cell_count() as usize;
541
542 let mut low = 0;
544 let mut high = cell_count;
545
546 while low < high {
547 let mid = (low + high) / 2;
548 let (cell_key, _) = read_leaf_cell(page, mid)?;
549
550 match cell_key.as_slice().cmp(key) {
551 Ordering::Less => low = mid + 1,
552 Ordering::Greater => high = mid,
553 Ordering::Equal => return Ok(SearchResult::Found(mid)),
554 }
555 }
556
557 Ok(SearchResult::NotFound(low))
558}
559
560fn find_child(page: &Page, key: &[u8]) -> BTreeResult<u32> {
561 let cell_count = page.cell_count() as usize;
562
563 for i in 0..cell_count {
565 let (cell_key, child) = read_interior_cell(page, i)?;
566 if key < cell_key.as_slice() {
567 return Ok(child);
568 }
569 }
570
571 Ok(page.right_child())
573}
574
575fn find_first_child(page: &Page) -> BTreeResult<u32> {
576 if page.cell_count() == 0 {
577 return Ok(page.right_child());
578 }
579 let (_, child) = read_interior_cell(page, 0)?;
580 Ok(child)
581}
582
583fn leaf_min_bytes() -> usize {
584 (PAGE_SIZE - LEAF_DATA_OFFSET) * MIN_FILL_FACTOR / 100
585}
586
587fn interior_min_bytes() -> usize {
588 (PAGE_SIZE - INTERIOR_DATA_OFFSET) * MIN_FILL_FACTOR / 100
589}
590
591fn leaf_entry_size(entry: &(Vec<u8>, Vec<u8>)) -> usize {
595 2 + 4 + entry.0.len() + entry.1.len()
596}
597
598fn leaf_entries_size(entries: &[(Vec<u8>, Vec<u8>)]) -> usize {
599 entries.iter().map(leaf_entry_size).sum()
600}
601
602#[inline]
603fn leaf_slot_offset_for(index: usize) -> usize {
604 LEAF_SLOT_ARRAY_OFFSET + index * 2
605}
606
607#[inline]
608fn leaf_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
609 let data = page.as_bytes();
610 let slot_pos = leaf_slot_offset_for(index);
611 if slot_pos + 2 > PAGE_SIZE {
612 return Err(BTreeError::Corrupted("slot array overflows page".into()));
613 }
614 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
615}
616
617#[inline]
618fn leaf_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
619 let data = page.as_bytes_mut();
620 let slot_pos = leaf_slot_offset_for(index);
621 if slot_pos + 2 > PAGE_SIZE {
622 return Err(BTreeError::Corrupted("slot array overflows page".into()));
623 }
624 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
625 Ok(())
626}
627
628#[inline]
629fn leaf_slots_end(page: &Page) -> usize {
630 LEAF_SLOT_ARRAY_OFFSET + (page.cell_count() as usize) * 2
631}
632
633#[inline]
634fn leaf_cells_start(page: &Page) -> usize {
635 let end = page.free_end() as usize;
636 if end == 0 {
637 PAGE_SIZE
638 } else {
639 end
640 }
641}
642
643#[inline]
644fn leaf_free_bytes(page: &Page) -> usize {
645 let slot_end = leaf_slots_end(page);
646 let cells = leaf_cells_start(page);
647 cells.saturating_sub(slot_end)
648}
649
650fn interior_key_size(key: &[u8]) -> usize {
651 2 + key.len() + 4
652}
653
654fn interior_entries_size(keys: &[Vec<u8>]) -> usize {
655 keys.iter().map(|k| interior_key_size(k)).sum()
656}
657
658fn read_leaf_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, Vec<u8>)> {
664 let (key, value) = read_leaf_cell_borrowed(page, index)?;
665 Ok((key.to_vec(), value.to_vec()))
666}
667
668fn read_leaf_cell_borrowed(page: &Page, index: usize) -> BTreeResult<(&[u8], &[u8])> {
669 let cell_count = page.cell_count() as usize;
670 if index >= cell_count {
671 return Err(BTreeError::Corrupted("Cell index out of range".into()));
672 }
673 let offset = leaf_read_slot(page, index)?;
674 let data = page.as_bytes();
675 if offset + 4 > PAGE_SIZE {
676 return Err(BTreeError::Corrupted("cell header out of range".into()));
677 }
678 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
679 let val_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
680 let end = offset + 4 + key_len + val_len;
681 if end > PAGE_SIZE {
682 return Err(BTreeError::Corrupted("cell body out of range".into()));
683 }
684 let key = &data[offset + 4..offset + 4 + key_len];
685 let value = &data[offset + 4 + key_len..end];
686 Ok((key, value))
687}
688
689fn read_leaf_entries(page: &Page) -> BTreeResult<Vec<(Vec<u8>, Vec<u8>)>> {
690 let cell_count = page.cell_count() as usize;
691 let mut entries = Vec::with_capacity(cell_count);
692 for i in 0..cell_count {
693 entries.push(read_leaf_cell(page, i)?);
694 }
695 Ok(entries)
696}
697
698fn write_leaf_entries(page: &mut Page, entries: &[(Vec<u8>, Vec<u8>)]) -> BTreeResult<()> {
704 clear_leaf_cells(page);
705 for (i, (k, v)) in entries.iter().enumerate() {
706 let cell_size = 4 + k.len() + v.len();
707 let cells_start = leaf_cells_start(page);
708 let slot_end = LEAF_SLOT_ARRAY_OFFSET + (i + 1) * 2;
709 if slot_end + cell_size > cells_start {
710 return Err(BTreeError::Corrupted("leaf rebuild overflowed page".into()));
711 }
712 let cell_offset = cells_start - cell_size;
713 {
714 let data = page.as_bytes_mut();
715 data[cell_offset..cell_offset + 2].copy_from_slice(&(k.len() as u16).to_le_bytes());
716 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(v.len() as u16).to_le_bytes());
717 data[cell_offset + 4..cell_offset + 4 + k.len()].copy_from_slice(k);
718 data[cell_offset + 4 + k.len()..cell_offset + 4 + k.len() + v.len()].copy_from_slice(v);
719 }
720 page.set_free_end(cell_offset as u16);
721 leaf_write_slot(page, i, cell_offset as u16)?;
722 }
723 page.set_cell_count(entries.len() as u16);
724 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + entries.len() * 2) as u16);
725 Ok(())
726}
727
728fn can_insert_leaf(page: &Page, key: &[u8], value: &[u8]) -> bool {
729 let needed = 2 + 4 + key.len() + value.len();
730 leaf_free_bytes(page) >= needed
731}
732
733pub(crate) fn overwrite_leaf_value(
749 page: &mut Page,
750 index: usize,
751 new_value: &[u8],
752) -> BTreeResult<()> {
753 let cell_offset = leaf_read_slot(page, index)?;
754 let data = page.as_bytes();
755 if cell_offset + 4 > data.len() {
756 return Err(BTreeError::Corrupted(
757 "leaf cell header out of bounds".into(),
758 ));
759 }
760 let k_len = u16::from_le_bytes([data[cell_offset], data[cell_offset + 1]]) as usize;
761 let old_v_len = u16::from_le_bytes([data[cell_offset + 2], data[cell_offset + 3]]) as usize;
762 if new_value.len() > old_v_len {
763 return Err(BTreeError::Corrupted(
764 "overwrite_leaf_value: new value larger than slot".into(),
765 ));
766 }
767 let value_start = cell_offset + 4 + k_len;
768 if value_start + new_value.len() > data.len() {
769 return Err(BTreeError::Corrupted(
770 "leaf cell value out of bounds".into(),
771 ));
772 }
773 let data = page.as_bytes_mut();
774 if new_value.len() != old_v_len {
777 let new_len = new_value.len() as u16;
778 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&new_len.to_le_bytes());
779 }
780 data[value_start..value_start + new_value.len()].copy_from_slice(new_value);
781 Ok(())
782}
783
784fn insert_into_leaf(page: &mut Page, key: &[u8], value: &[u8]) -> BTreeResult<()> {
786 let cell_count = page.cell_count() as usize;
788 let mut low = 0;
789 let mut high = cell_count;
790 while low < high {
791 let mid = (low + high) / 2;
792 let (cell_key, _) = read_leaf_cell(page, mid)?;
793 match cell_key.as_slice().cmp(key) {
794 Ordering::Less => low = mid + 1,
795 Ordering::Greater => high = mid,
796 Ordering::Equal => {
797 low = mid + 1;
800 break;
801 }
802 }
803 }
804 let insert_pos = low;
805
806 let cell_size = 4 + key.len() + value.len();
808 let slot_end_after = LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2;
809 let cells_start = leaf_cells_start(page);
810 if slot_end_after + cell_size > cells_start {
811 return Err(BTreeError::Corrupted("leaf page full".into()));
812 }
813 let cell_offset = cells_start - cell_size;
814 {
815 let data = page.as_bytes_mut();
816 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
817 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(value.len() as u16).to_le_bytes());
818 data[cell_offset + 4..cell_offset + 4 + key.len()].copy_from_slice(key);
819 data[cell_offset + 4 + key.len()..cell_offset + 4 + key.len() + value.len()]
820 .copy_from_slice(value);
821 }
822 page.set_free_end(cell_offset as u16);
823
824 {
828 let shift_from = leaf_slot_offset_for(insert_pos);
829 let shift_to = shift_from + 2;
830 let shift_len = (cell_count - insert_pos) * 2;
831 if shift_len > 0 {
832 let data = page.as_bytes_mut();
833 data.copy_within(shift_from..shift_from + shift_len, shift_to);
834 }
835 }
836 leaf_write_slot(page, insert_pos, cell_offset as u16)?;
837
838 page.set_cell_count((cell_count + 1) as u16);
840 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2) as u16);
841 Ok(())
842}
843
844fn delete_from_leaf(page: &mut Page, index: usize) -> BTreeResult<()> {
849 let cell_count = page.cell_count() as usize;
850 if index >= cell_count {
851 return Err(BTreeError::Corrupted("delete index out of range".into()));
852 }
853 if index + 1 < cell_count {
854 let shift_from = leaf_slot_offset_for(index + 1);
855 let shift_to = leaf_slot_offset_for(index);
856 let shift_len = (cell_count - index - 1) * 2;
857 let data = page.as_bytes_mut();
858 data.copy_within(shift_from..shift_from + shift_len, shift_to);
859 }
860 page.set_cell_count((cell_count - 1) as u16);
861 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count - 1) * 2) as u16);
862 Ok(())
863}
864
865fn clear_leaf_cells(page: &mut Page) {
869 {
870 let data = page.as_bytes_mut();
871 for byte in &mut data[LEAF_SLOT_ARRAY_OFFSET..] {
872 *byte = 0;
873 }
874 }
875 page.set_cell_count(0);
876 page.set_free_start(LEAF_SLOT_ARRAY_OFFSET as u16);
877 page.set_free_end(PAGE_SIZE as u16);
878}
879
880fn init_leaf_links(page: &mut Page, prev: u32, next: u32) {
881 let data = page.as_bytes_mut();
882 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
883 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
884}
885
886fn read_next_leaf(page: &Page) -> u32 {
887 let data = page.as_bytes();
888 u32::from_le_bytes([
889 data[LEAF_NEXT_OFFSET],
890 data[LEAF_NEXT_OFFSET + 1],
891 data[LEAF_NEXT_OFFSET + 2],
892 data[LEAF_NEXT_OFFSET + 3],
893 ])
894}
895
896#[inline]
899fn leaf_right_sibling(page: &Page) -> u32 {
900 read_next_leaf(page)
901}
902
903fn leaf_high_key(page: &Page) -> BTreeResult<Option<Vec<u8>>> {
907 let cell_count = page.cell_count() as usize;
908 if cell_count == 0 {
909 return Ok(None);
910 }
911 let (key, _) = read_leaf_cell(page, cell_count - 1)?;
912 Ok(Some(key))
913}
914
915fn set_prev_leaf(page: &mut Page, prev: u32) {
916 let data = page.as_bytes_mut();
917 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
918}
919
920fn set_next_leaf(page: &mut Page, next: u32) {
921 let data = page.as_bytes_mut();
922 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
923}
924
925#[inline]
940fn interior_slot_offset_for(index: usize) -> usize {
941 INTERIOR_DATA_OFFSET + index * 2
942}
943
944#[inline]
945fn interior_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
946 let data = page.as_bytes();
947 let slot_pos = interior_slot_offset_for(index);
948 if slot_pos + 2 > PAGE_SIZE {
949 return Err(BTreeError::Corrupted(
950 "interior slot array overflows page".into(),
951 ));
952 }
953 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
954}
955
956#[inline]
957fn interior_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
958 let data = page.as_bytes_mut();
959 let slot_pos = interior_slot_offset_for(index);
960 if slot_pos + 2 > PAGE_SIZE {
961 return Err(BTreeError::Corrupted(
962 "interior slot array overflows page".into(),
963 ));
964 }
965 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
966 Ok(())
967}
968
969#[inline]
970fn interior_cells_start(page: &Page) -> usize {
971 let end = page.free_end() as usize;
972 if end == 0 {
973 PAGE_SIZE
974 } else {
975 end
976 }
977}
978
979#[inline]
980fn interior_free_bytes(page: &Page) -> usize {
981 let slot_end = INTERIOR_DATA_OFFSET + (page.cell_count() as usize) * 2;
982 interior_cells_start(page).saturating_sub(slot_end)
983}
984
985fn read_interior_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, u32)> {
986 let cell_count = page.cell_count() as usize;
987 if index >= cell_count {
988 return Err(BTreeError::Corrupted("Cell index out of range".into()));
989 }
990 let offset = interior_read_slot(page, index)?;
991 let data = page.as_bytes();
992 if offset + 2 > PAGE_SIZE {
993 return Err(BTreeError::Corrupted(
994 "interior cell header out of range".into(),
995 ));
996 }
997 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
998 let end = offset + 2 + key_len + 4;
999 if end > PAGE_SIZE {
1000 return Err(BTreeError::Corrupted(
1001 "interior cell body out of range".into(),
1002 ));
1003 }
1004 let key = data[offset + 2..offset + 2 + key_len].to_vec();
1005 let child = u32::from_le_bytes([
1006 data[offset + 2 + key_len],
1007 data[offset + 2 + key_len + 1],
1008 data[offset + 2 + key_len + 2],
1009 data[offset + 2 + key_len + 3],
1010 ]);
1011 Ok((key, child))
1012}
1013
1014fn read_interior_keys_children(page: &Page) -> BTreeResult<(Vec<Vec<u8>>, Vec<u32>)> {
1015 let cell_count = page.cell_count() as usize;
1016 let mut keys = Vec::with_capacity(cell_count);
1017 let mut children = Vec::with_capacity(cell_count + 1);
1018
1019 for i in 0..cell_count {
1020 let (key, child) = read_interior_cell(page, i)?;
1021 keys.push(key);
1022 children.push(child);
1023 }
1024
1025 if cell_count == 0 {
1026 let right_child = page.right_child();
1027 if right_child != 0 {
1028 children.push(right_child);
1029 }
1030 } else {
1031 children.push(page.right_child());
1032 }
1033
1034 Ok((keys, children))
1035}
1036
1037fn write_interior_entries(page: &mut Page, keys: &[Vec<u8>], children: &[u32]) -> BTreeResult<()> {
1042 if !keys.is_empty() && children.len() != keys.len() + 1 {
1043 return Err(BTreeError::Corrupted(
1044 "Interior keys/children length mismatch".into(),
1045 ));
1046 }
1047
1048 clear_interior_cells(page);
1049 if keys.is_empty() {
1050 let right_child = children.first().copied().unwrap_or(0);
1051 page.set_right_child(right_child);
1052 return Ok(());
1053 }
1054
1055 for (i, key) in keys.iter().enumerate() {
1056 let cell_size = 2 + key.len() + 4;
1057 let cells_start = interior_cells_start(page);
1058 let slot_end = INTERIOR_DATA_OFFSET + (i + 1) * 2;
1059 if slot_end + cell_size > cells_start {
1060 return Err(BTreeError::Corrupted(
1061 "interior rebuild overflowed page".into(),
1062 ));
1063 }
1064 let cell_offset = cells_start - cell_size;
1065 {
1066 let data = page.as_bytes_mut();
1067 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1068 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1069 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1070 .copy_from_slice(&children[i].to_le_bytes());
1071 }
1072 page.set_free_end(cell_offset as u16);
1073 interior_write_slot(page, i, cell_offset as u16)?;
1074 }
1075 page.set_cell_count(keys.len() as u16);
1076 page.set_free_start((INTERIOR_DATA_OFFSET + keys.len() * 2) as u16);
1077 page.set_right_child(*children.last().ok_or_else(|| {
1078 BTreeError::Corrupted("write_interior_entries: children empty with non-empty keys".into())
1079 })?);
1080 Ok(())
1081}
1082
1083fn can_insert_interior(page: &Page, key: &[u8]) -> bool {
1084 let needed = 2 + 2 + key.len() + 4;
1085 interior_free_bytes(page) >= needed
1086}
1087
1088fn insert_into_interior(
1093 page: &mut Page,
1094 key: &[u8],
1095 left_child: u32,
1096 right_child: u32,
1097) -> BTreeResult<()> {
1098 let cell_count = page.cell_count() as usize;
1100 let mut low = 0;
1101 let mut high = cell_count;
1102 while low < high {
1103 let mid = (low + high) / 2;
1104 let (cell_key, _) = read_interior_cell(page, mid)?;
1105 match cell_key.as_slice().cmp(key) {
1106 Ordering::Less => low = mid + 1,
1107 Ordering::Greater | Ordering::Equal => high = mid,
1108 }
1109 }
1110 let insert_pos = low;
1111
1112 if insert_pos < cell_count {
1117 let old_offset = interior_read_slot(page, insert_pos)?;
1118 let data = page.as_bytes();
1119 let key_len = u16::from_le_bytes([data[old_offset], data[old_offset + 1]]) as usize;
1120 let child_pos = old_offset + 2 + key_len;
1121 let data = page.as_bytes_mut();
1122 data[child_pos..child_pos + 4].copy_from_slice(&right_child.to_le_bytes());
1123 } else {
1124 page.set_right_child(right_child);
1125 }
1126
1127 let cell_size = 2 + key.len() + 4;
1129 let slot_end_after = INTERIOR_DATA_OFFSET + (cell_count + 1) * 2;
1130 let cells_start = interior_cells_start(page);
1131 if slot_end_after + cell_size > cells_start {
1132 return Err(BTreeError::Corrupted("interior page full".into()));
1133 }
1134 let cell_offset = cells_start - cell_size;
1135 {
1136 let data = page.as_bytes_mut();
1137 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1138 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1139 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1140 .copy_from_slice(&left_child.to_le_bytes());
1141 }
1142 page.set_free_end(cell_offset as u16);
1143
1144 {
1147 let shift_from = interior_slot_offset_for(insert_pos);
1148 let shift_to = shift_from + 2;
1149 let shift_len = (cell_count - insert_pos) * 2;
1150 if shift_len > 0 {
1151 let data = page.as_bytes_mut();
1152 data.copy_within(shift_from..shift_from + shift_len, shift_to);
1153 }
1154 }
1155 interior_write_slot(page, insert_pos, cell_offset as u16)?;
1156 page.set_cell_count((cell_count + 1) as u16);
1157 page.set_free_start((INTERIOR_DATA_OFFSET + (cell_count + 1) * 2) as u16);
1158 Ok(())
1159}
1160
1161fn clear_interior_cells(page: &mut Page) {
1162 {
1163 let data = page.as_bytes_mut();
1164 for byte in &mut data[INTERIOR_DATA_OFFSET..] {
1165 *byte = 0;
1166 }
1167 }
1168 page.set_cell_count(0);
1169 page.set_free_start(INTERIOR_DATA_OFFSET as u16);
1170 page.set_free_end(PAGE_SIZE as u16);
1171}