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<page_proofs::TreePageStore<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<page_proofs::TreePageStore<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
523mod page_proofs;
524
525#[cfg(test)]
526#[path = "btree/read_budget_tests.rs"]
527mod read_budget_tests;
528
529#[path = "btree/lehman_yao.rs"]
530pub mod lehman_yao;
531
532#[path = "btree/value_layout.rs"]
533pub mod value_layout;
534enum SearchResult {
537 Found(usize),
538 NotFound(usize),
539}
540
541fn search_leaf(page: &Page, key: &[u8]) -> BTreeResult<SearchResult> {
542 let cell_count = page.cell_count() as usize;
543
544 let mut low = 0;
546 let mut high = cell_count;
547
548 while low < high {
549 let mid = (low + high) / 2;
550 let (cell_key, _) = read_leaf_cell(page, mid)?;
551
552 match cell_key.as_slice().cmp(key) {
553 Ordering::Less => low = mid + 1,
554 Ordering::Greater => high = mid,
555 Ordering::Equal => return Ok(SearchResult::Found(mid)),
556 }
557 }
558
559 Ok(SearchResult::NotFound(low))
560}
561
562fn find_child(page: &Page, key: &[u8]) -> BTreeResult<u32> {
563 let cell_count = page.cell_count() as usize;
564
565 for i in 0..cell_count {
567 let (cell_key, child) = read_interior_cell(page, i)?;
568 if key < cell_key.as_slice() {
569 return Ok(child);
570 }
571 }
572
573 Ok(page.right_child())
575}
576
577fn find_first_child(page: &Page) -> BTreeResult<u32> {
578 if page.cell_count() == 0 {
579 return Ok(page.right_child());
580 }
581 let (_, child) = read_interior_cell(page, 0)?;
582 Ok(child)
583}
584
585fn leaf_min_bytes() -> usize {
586 (PAGE_SIZE - LEAF_DATA_OFFSET) * MIN_FILL_FACTOR / 100
587}
588
589fn interior_min_bytes() -> usize {
590 (PAGE_SIZE - INTERIOR_DATA_OFFSET) * MIN_FILL_FACTOR / 100
591}
592
593fn leaf_entry_size(entry: &(Vec<u8>, Vec<u8>)) -> usize {
597 2 + 4 + entry.0.len() + entry.1.len()
598}
599
600fn leaf_entries_size(entries: &[(Vec<u8>, Vec<u8>)]) -> usize {
601 entries.iter().map(leaf_entry_size).sum()
602}
603
604#[inline]
605fn leaf_slot_offset_for(index: usize) -> usize {
606 LEAF_SLOT_ARRAY_OFFSET + index * 2
607}
608
609#[inline]
610fn leaf_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
611 let data = page.as_bytes();
612 let slot_pos = leaf_slot_offset_for(index);
613 if slot_pos + 2 > PAGE_SIZE {
614 return Err(BTreeError::Corrupted("slot array overflows page".into()));
615 }
616 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
617}
618
619#[inline]
620fn leaf_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
621 let data = page.as_bytes_mut();
622 let slot_pos = leaf_slot_offset_for(index);
623 if slot_pos + 2 > PAGE_SIZE {
624 return Err(BTreeError::Corrupted("slot array overflows page".into()));
625 }
626 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
627 Ok(())
628}
629
630#[inline]
631fn leaf_slots_end(page: &Page) -> usize {
632 LEAF_SLOT_ARRAY_OFFSET + (page.cell_count() as usize) * 2
633}
634
635#[inline]
636fn leaf_cells_start(page: &Page) -> usize {
637 let end = page.free_end() as usize;
638 if end == 0 {
639 PAGE_SIZE
640 } else {
641 end
642 }
643}
644
645#[inline]
646fn leaf_free_bytes(page: &Page) -> usize {
647 let slot_end = leaf_slots_end(page);
648 let cells = leaf_cells_start(page);
649 cells.saturating_sub(slot_end)
650}
651
652fn interior_key_size(key: &[u8]) -> usize {
653 2 + key.len() + 4
654}
655
656fn interior_entries_size(keys: &[Vec<u8>]) -> usize {
657 keys.iter().map(|k| interior_key_size(k)).sum()
658}
659
660fn read_leaf_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, Vec<u8>)> {
666 let (key, value) = read_leaf_cell_borrowed(page, index)?;
667 Ok((key.to_vec(), value.to_vec()))
668}
669
670fn read_leaf_cell_borrowed(page: &Page, index: usize) -> BTreeResult<(&[u8], &[u8])> {
671 let cell_count = page.cell_count() as usize;
672 if index >= cell_count {
673 return Err(BTreeError::Corrupted("Cell index out of range".into()));
674 }
675 let offset = leaf_read_slot(page, index)?;
676 let data = page.as_bytes();
677 if offset + 4 > PAGE_SIZE {
678 return Err(BTreeError::Corrupted("cell header out of range".into()));
679 }
680 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
681 let val_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
682 let end = offset + 4 + key_len + val_len;
683 if end > PAGE_SIZE {
684 return Err(BTreeError::Corrupted("cell body out of range".into()));
685 }
686 let key = &data[offset + 4..offset + 4 + key_len];
687 let value = &data[offset + 4 + key_len..end];
688 Ok((key, value))
689}
690
691fn read_leaf_entries(page: &Page) -> BTreeResult<Vec<(Vec<u8>, Vec<u8>)>> {
692 let cell_count = page.cell_count() as usize;
693 let mut entries = Vec::with_capacity(cell_count);
694 for i in 0..cell_count {
695 entries.push(read_leaf_cell(page, i)?);
696 }
697 Ok(entries)
698}
699
700fn write_leaf_entries(page: &mut Page, entries: &[(Vec<u8>, Vec<u8>)]) -> BTreeResult<()> {
706 clear_leaf_cells(page);
707 for (i, (k, v)) in entries.iter().enumerate() {
708 let cell_size = 4 + k.len() + v.len();
709 let cells_start = leaf_cells_start(page);
710 let slot_end = LEAF_SLOT_ARRAY_OFFSET + (i + 1) * 2;
711 if slot_end + cell_size > cells_start {
712 return Err(BTreeError::Corrupted("leaf rebuild overflowed page".into()));
713 }
714 let cell_offset = cells_start - cell_size;
715 {
716 let data = page.as_bytes_mut();
717 data[cell_offset..cell_offset + 2].copy_from_slice(&(k.len() as u16).to_le_bytes());
718 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(v.len() as u16).to_le_bytes());
719 data[cell_offset + 4..cell_offset + 4 + k.len()].copy_from_slice(k);
720 data[cell_offset + 4 + k.len()..cell_offset + 4 + k.len() + v.len()].copy_from_slice(v);
721 }
722 page.set_free_end(cell_offset as u16);
723 leaf_write_slot(page, i, cell_offset as u16)?;
724 }
725 page.set_cell_count(entries.len() as u16);
726 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + entries.len() * 2) as u16);
727 Ok(())
728}
729
730fn can_insert_leaf(page: &Page, key: &[u8], value: &[u8]) -> bool {
731 let needed = 2 + 4 + key.len() + value.len();
732 leaf_free_bytes(page) >= needed
733}
734
735pub(crate) fn overwrite_leaf_value(
751 page: &mut Page,
752 index: usize,
753 new_value: &[u8],
754) -> BTreeResult<()> {
755 let cell_offset = leaf_read_slot(page, index)?;
756 let data = page.as_bytes();
757 if cell_offset + 4 > data.len() {
758 return Err(BTreeError::Corrupted(
759 "leaf cell header out of bounds".into(),
760 ));
761 }
762 let k_len = u16::from_le_bytes([data[cell_offset], data[cell_offset + 1]]) as usize;
763 let old_v_len = u16::from_le_bytes([data[cell_offset + 2], data[cell_offset + 3]]) as usize;
764 if new_value.len() > old_v_len {
765 return Err(BTreeError::Corrupted(
766 "overwrite_leaf_value: new value larger than slot".into(),
767 ));
768 }
769 let value_start = cell_offset + 4 + k_len;
770 if value_start + new_value.len() > data.len() {
771 return Err(BTreeError::Corrupted(
772 "leaf cell value out of bounds".into(),
773 ));
774 }
775 let data = page.as_bytes_mut();
776 if new_value.len() != old_v_len {
779 let new_len = new_value.len() as u16;
780 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&new_len.to_le_bytes());
781 }
782 data[value_start..value_start + new_value.len()].copy_from_slice(new_value);
783 Ok(())
784}
785
786fn insert_into_leaf(page: &mut Page, key: &[u8], value: &[u8]) -> BTreeResult<()> {
788 let cell_count = page.cell_count() as usize;
790 let mut low = 0;
791 let mut high = cell_count;
792 while low < high {
793 let mid = (low + high) / 2;
794 let (cell_key, _) = read_leaf_cell(page, mid)?;
795 match cell_key.as_slice().cmp(key) {
796 Ordering::Less => low = mid + 1,
797 Ordering::Greater => high = mid,
798 Ordering::Equal => {
799 low = mid + 1;
802 break;
803 }
804 }
805 }
806 let insert_pos = low;
807
808 let cell_size = 4 + key.len() + value.len();
810 let slot_end_after = LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2;
811 let cells_start = leaf_cells_start(page);
812 if slot_end_after + cell_size > cells_start {
813 return Err(BTreeError::Corrupted("leaf page full".into()));
814 }
815 let cell_offset = cells_start - cell_size;
816 {
817 let data = page.as_bytes_mut();
818 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
819 data[cell_offset + 2..cell_offset + 4].copy_from_slice(&(value.len() as u16).to_le_bytes());
820 data[cell_offset + 4..cell_offset + 4 + key.len()].copy_from_slice(key);
821 data[cell_offset + 4 + key.len()..cell_offset + 4 + key.len() + value.len()]
822 .copy_from_slice(value);
823 }
824 page.set_free_end(cell_offset as u16);
825
826 {
830 let shift_from = leaf_slot_offset_for(insert_pos);
831 let shift_to = shift_from + 2;
832 let shift_len = (cell_count - insert_pos) * 2;
833 if shift_len > 0 {
834 let data = page.as_bytes_mut();
835 data.copy_within(shift_from..shift_from + shift_len, shift_to);
836 }
837 }
838 leaf_write_slot(page, insert_pos, cell_offset as u16)?;
839
840 page.set_cell_count((cell_count + 1) as u16);
842 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count + 1) * 2) as u16);
843 Ok(())
844}
845
846fn delete_from_leaf(page: &mut Page, index: usize) -> BTreeResult<()> {
851 let cell_count = page.cell_count() as usize;
852 if index >= cell_count {
853 return Err(BTreeError::Corrupted("delete index out of range".into()));
854 }
855 if index + 1 < cell_count {
856 let shift_from = leaf_slot_offset_for(index + 1);
857 let shift_to = leaf_slot_offset_for(index);
858 let shift_len = (cell_count - index - 1) * 2;
859 let data = page.as_bytes_mut();
860 data.copy_within(shift_from..shift_from + shift_len, shift_to);
861 }
862 page.set_cell_count((cell_count - 1) as u16);
863 page.set_free_start((LEAF_SLOT_ARRAY_OFFSET + (cell_count - 1) * 2) as u16);
864 Ok(())
865}
866
867fn clear_leaf_cells(page: &mut Page) {
871 {
872 let data = page.as_bytes_mut();
873 for byte in &mut data[LEAF_SLOT_ARRAY_OFFSET..] {
874 *byte = 0;
875 }
876 }
877 page.set_cell_count(0);
878 page.set_free_start(LEAF_SLOT_ARRAY_OFFSET as u16);
879 page.set_free_end(PAGE_SIZE as u16);
880}
881
882fn init_leaf_links(page: &mut Page, prev: u32, next: u32) {
883 let data = page.as_bytes_mut();
884 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
885 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
886}
887
888fn read_next_leaf(page: &Page) -> u32 {
889 let data = page.as_bytes();
890 u32::from_le_bytes([
891 data[LEAF_NEXT_OFFSET],
892 data[LEAF_NEXT_OFFSET + 1],
893 data[LEAF_NEXT_OFFSET + 2],
894 data[LEAF_NEXT_OFFSET + 3],
895 ])
896}
897
898#[inline]
901fn leaf_right_sibling(page: &Page) -> u32 {
902 read_next_leaf(page)
903}
904
905fn leaf_high_key(page: &Page) -> BTreeResult<Option<Vec<u8>>> {
909 let cell_count = page.cell_count() as usize;
910 if cell_count == 0 {
911 return Ok(None);
912 }
913 let (key, _) = read_leaf_cell(page, cell_count - 1)?;
914 Ok(Some(key))
915}
916
917fn set_prev_leaf(page: &mut Page, prev: u32) {
918 let data = page.as_bytes_mut();
919 data[LEAF_PREV_OFFSET..LEAF_PREV_OFFSET + 4].copy_from_slice(&prev.to_le_bytes());
920}
921
922fn set_next_leaf(page: &mut Page, next: u32) {
923 let data = page.as_bytes_mut();
924 data[LEAF_NEXT_OFFSET..LEAF_NEXT_OFFSET + 4].copy_from_slice(&next.to_le_bytes());
925}
926
927#[inline]
942fn interior_slot_offset_for(index: usize) -> usize {
943 INTERIOR_DATA_OFFSET + index * 2
944}
945
946#[inline]
947fn interior_read_slot(page: &Page, index: usize) -> BTreeResult<usize> {
948 let data = page.as_bytes();
949 let slot_pos = interior_slot_offset_for(index);
950 if slot_pos + 2 > PAGE_SIZE {
951 return Err(BTreeError::Corrupted(
952 "interior slot array overflows page".into(),
953 ));
954 }
955 Ok(u16::from_le_bytes([data[slot_pos], data[slot_pos + 1]]) as usize)
956}
957
958#[inline]
959fn interior_write_slot(page: &mut Page, index: usize, cell_offset: u16) -> BTreeResult<()> {
960 let data = page.as_bytes_mut();
961 let slot_pos = interior_slot_offset_for(index);
962 if slot_pos + 2 > PAGE_SIZE {
963 return Err(BTreeError::Corrupted(
964 "interior slot array overflows page".into(),
965 ));
966 }
967 data[slot_pos..slot_pos + 2].copy_from_slice(&cell_offset.to_le_bytes());
968 Ok(())
969}
970
971#[inline]
972fn interior_cells_start(page: &Page) -> usize {
973 let end = page.free_end() as usize;
974 if end == 0 {
975 PAGE_SIZE
976 } else {
977 end
978 }
979}
980
981#[inline]
982fn interior_free_bytes(page: &Page) -> usize {
983 let slot_end = INTERIOR_DATA_OFFSET + (page.cell_count() as usize) * 2;
984 interior_cells_start(page).saturating_sub(slot_end)
985}
986
987fn read_interior_cell(page: &Page, index: usize) -> BTreeResult<(Vec<u8>, u32)> {
988 let cell_count = page.cell_count() as usize;
989 if index >= cell_count {
990 return Err(BTreeError::Corrupted("Cell index out of range".into()));
991 }
992 let offset = interior_read_slot(page, index)?;
993 let data = page.as_bytes();
994 if offset + 2 > PAGE_SIZE {
995 return Err(BTreeError::Corrupted(
996 "interior cell header out of range".into(),
997 ));
998 }
999 let key_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
1000 let end = offset + 2 + key_len + 4;
1001 if end > PAGE_SIZE {
1002 return Err(BTreeError::Corrupted(
1003 "interior cell body out of range".into(),
1004 ));
1005 }
1006 let key = data[offset + 2..offset + 2 + key_len].to_vec();
1007 let child = u32::from_le_bytes([
1008 data[offset + 2 + key_len],
1009 data[offset + 2 + key_len + 1],
1010 data[offset + 2 + key_len + 2],
1011 data[offset + 2 + key_len + 3],
1012 ]);
1013 Ok((key, child))
1014}
1015
1016fn read_interior_keys_children(page: &Page) -> BTreeResult<(Vec<Vec<u8>>, Vec<u32>)> {
1017 let cell_count = page.cell_count() as usize;
1018 let mut keys = Vec::with_capacity(cell_count);
1019 let mut children = Vec::with_capacity(cell_count + 1);
1020
1021 for i in 0..cell_count {
1022 let (key, child) = read_interior_cell(page, i)?;
1023 keys.push(key);
1024 children.push(child);
1025 }
1026
1027 if cell_count == 0 {
1028 let right_child = page.right_child();
1029 if right_child != 0 {
1030 children.push(right_child);
1031 }
1032 } else {
1033 children.push(page.right_child());
1034 }
1035
1036 Ok((keys, children))
1037}
1038
1039fn write_interior_entries(page: &mut Page, keys: &[Vec<u8>], children: &[u32]) -> BTreeResult<()> {
1044 if !keys.is_empty() && children.len() != keys.len() + 1 {
1045 return Err(BTreeError::Corrupted(
1046 "Interior keys/children length mismatch".into(),
1047 ));
1048 }
1049
1050 clear_interior_cells(page);
1051 if keys.is_empty() {
1052 let right_child = children.first().copied().unwrap_or(0);
1053 page.set_right_child(right_child);
1054 return Ok(());
1055 }
1056
1057 for (i, key) in keys.iter().enumerate() {
1058 let cell_size = 2 + key.len() + 4;
1059 let cells_start = interior_cells_start(page);
1060 let slot_end = INTERIOR_DATA_OFFSET + (i + 1) * 2;
1061 if slot_end + cell_size > cells_start {
1062 return Err(BTreeError::Corrupted(
1063 "interior rebuild overflowed page".into(),
1064 ));
1065 }
1066 let cell_offset = cells_start - cell_size;
1067 {
1068 let data = page.as_bytes_mut();
1069 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1070 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1071 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1072 .copy_from_slice(&children[i].to_le_bytes());
1073 }
1074 page.set_free_end(cell_offset as u16);
1075 interior_write_slot(page, i, cell_offset as u16)?;
1076 }
1077 page.set_cell_count(keys.len() as u16);
1078 page.set_free_start((INTERIOR_DATA_OFFSET + keys.len() * 2) as u16);
1079 page.set_right_child(*children.last().ok_or_else(|| {
1080 BTreeError::Corrupted("write_interior_entries: children empty with non-empty keys".into())
1081 })?);
1082 Ok(())
1083}
1084
1085fn can_insert_interior(page: &Page, key: &[u8]) -> bool {
1086 let needed = 2 + 2 + key.len() + 4;
1087 interior_free_bytes(page) >= needed
1088}
1089
1090fn insert_into_interior(
1095 page: &mut Page,
1096 key: &[u8],
1097 left_child: u32,
1098 right_child: u32,
1099) -> BTreeResult<()> {
1100 let cell_count = page.cell_count() as usize;
1102 let mut low = 0;
1103 let mut high = cell_count;
1104 while low < high {
1105 let mid = (low + high) / 2;
1106 let (cell_key, _) = read_interior_cell(page, mid)?;
1107 match cell_key.as_slice().cmp(key) {
1108 Ordering::Less => low = mid + 1,
1109 Ordering::Greater | Ordering::Equal => high = mid,
1110 }
1111 }
1112 let insert_pos = low;
1113
1114 if insert_pos < cell_count {
1119 let old_offset = interior_read_slot(page, insert_pos)?;
1120 let data = page.as_bytes();
1121 let key_len = u16::from_le_bytes([data[old_offset], data[old_offset + 1]]) as usize;
1122 let child_pos = old_offset + 2 + key_len;
1123 let data = page.as_bytes_mut();
1124 data[child_pos..child_pos + 4].copy_from_slice(&right_child.to_le_bytes());
1125 } else {
1126 page.set_right_child(right_child);
1127 }
1128
1129 let cell_size = 2 + key.len() + 4;
1131 let slot_end_after = INTERIOR_DATA_OFFSET + (cell_count + 1) * 2;
1132 let cells_start = interior_cells_start(page);
1133 if slot_end_after + cell_size > cells_start {
1134 return Err(BTreeError::Corrupted("interior page full".into()));
1135 }
1136 let cell_offset = cells_start - cell_size;
1137 {
1138 let data = page.as_bytes_mut();
1139 data[cell_offset..cell_offset + 2].copy_from_slice(&(key.len() as u16).to_le_bytes());
1140 data[cell_offset + 2..cell_offset + 2 + key.len()].copy_from_slice(key);
1141 data[cell_offset + 2 + key.len()..cell_offset + 2 + key.len() + 4]
1142 .copy_from_slice(&left_child.to_le_bytes());
1143 }
1144 page.set_free_end(cell_offset as u16);
1145
1146 {
1149 let shift_from = interior_slot_offset_for(insert_pos);
1150 let shift_to = shift_from + 2;
1151 let shift_len = (cell_count - insert_pos) * 2;
1152 if shift_len > 0 {
1153 let data = page.as_bytes_mut();
1154 data.copy_within(shift_from..shift_from + shift_len, shift_to);
1155 }
1156 }
1157 interior_write_slot(page, insert_pos, cell_offset as u16)?;
1158 page.set_cell_count((cell_count + 1) as u16);
1159 page.set_free_start((INTERIOR_DATA_OFFSET + (cell_count + 1) * 2) as u16);
1160 Ok(())
1161}
1162
1163fn clear_interior_cells(page: &mut Page) {
1164 {
1165 let data = page.as_bytes_mut();
1166 for byte in &mut data[INTERIOR_DATA_OFFSET..] {
1167 *byte = 0;
1168 }
1169 }
1170 page.set_cell_count(0);
1171 page.set_free_start(INTERIOR_DATA_OFFSET as u16);
1172 page.set_free_end(PAGE_SIZE as u16);
1173}