sanakirja_core/btree/del.rs
1//! Deletions from a B tree.
2use super::cursor::*;
3use super::put::{Ok, Put};
4use super::*;
5use crate::LoadPage;
6
7/// Represents the result of a merge or rebalance, without touching
8/// the parent of the merge/rebalance.
9#[derive(Debug)]
10pub enum Op<'a, T, K: ?Sized, V: ?Sized> {
11 Merged {
12 // New merged page.
13 page: MutPage,
14 // Pages freed by the merge (0 meaning "no page").
15 freed: [u64; 2],
16 marker: core::marker::PhantomData<T>,
17 },
18 Rebalanced {
19 // New middle key.
20 k: &'a K,
21 // New middle value.
22 v: &'a V,
23 // New left page.
24 l: u64,
25 // New right page.
26 r: u64,
27 // Pages freed by the rebalance (0 meaning "no page").
28 freed: [u64; 2],
29 },
30 Put(crate::btree::put::Put<'a, K, V>),
31}
32
33/// Represents a page with modifications from a merge.
34#[derive(Debug)]
35pub struct ModifiedPage<'a, K: ?Sized, V: ?Sized, P: BTreePage<K, V>> {
36 pub page: CowPage,
37 // Whether the page is mutable with another tree.
38 pub mutable: bool,
39 // Start copying c0 (keep `page`'s left child).
40 pub c0: P::Cursor,
41 // If > 0, replace the right child of c0's last element with `l`.
42 pub l: u64,
43 // If > 0, replace the left child of c1's last element with `r`.
44 pub r: u64,
45 // Possibly insert a new binding.
46 pub ins: Option<(&'a K, &'a V)>,
47 // If a rebalance causes a split, we might need to insert an entry
48 // after the replacement.
49 pub ins2: Option<(&'a K, &'a V)>,
50 // The first element of c1 is to be deleted, the others must be copied.
51 pub c1: P::Cursor,
52 // Whether to skip `c1`'s first element.
53 pub skip_first: bool,
54 // Whether the page we just modified is `self.l`.
55 pub mod_is_left: bool,
56}
57
58/// Represents the concatenation of a modified page and one of its
59/// sibling (left or right).
60#[derive(Debug)]
61pub struct Concat<'a, K: ?Sized, V: ?Sized, P: BTreePage<K, V>> {
62 /// Modified page.
63 pub modified: ModifiedPage<'a, K, V, P>,
64 /// Middle element.
65 pub mid: (&'a K, &'a V),
66 /// Sibling of the modified page.
67 pub other: CowPage,
68 /// Is the modified field on the left or on the right of the
69 /// concatenation?
70 pub mod_is_left: bool,
71 /// Is the other page owned by this tree? If not, it means `other`
72 /// is shared with another tree, and hence we need to increase the
73 /// reference count of entries coming from `other`.
74 pub other_is_mutable: bool,
75}
76
77/// If `value` is `None`, delete the first entry for `key` from the
78/// database, if present. Else, delete the entry for `(key, value)`,
79/// if present.
80pub fn del<
81 T: AllocPage + LoadPage,
82 K: Storable + ?Sized + PartialEq,
83 V: Storable + ?Sized + PartialEq,
84 P: BTreeMutPage<K, V>,
85>(
86 txn: &mut T,
87 db: &mut Db_<K, V, P>,
88 key: &K,
89 value: Option<&V>,
90) -> Result<bool, T::Error> {
91 let mut cursor = Cursor::new(txn, db)?;
92 // If the key and value weren't found, return.
93 match (cursor.set(txn, key, value)?, value) {
94 (Some((k, v)), Some(value)) if k == key && v == value => {}
95 (Some((k, _)), None) if k == key => {}
96 _ => return Ok(false),
97 }
98 // Else, the cursor is set on `(key, value)` if `value.is_some()`
99 // or on the first entry with key `key` else.
100 //
101 // We delete that position, which might be in a leaf or in an
102 // internal node.
103 del_at_cursor(txn, db, &mut cursor, true)
104}
105
106/// If `value` is `None`, delete the first entry for `key` from the
107/// database, if present. Else, delete the entry for `(key, value)`,
108/// if present.
109pub fn del_no_decr<
110 T: AllocPage + LoadPage,
111 K: Storable + ?Sized + PartialEq,
112 V: Storable + ?Sized + PartialEq,
113 P: BTreeMutPage<K, V>,
114>(
115 txn: &mut T,
116 db: &mut Db_<K, V, P>,
117 key: &K,
118 value: Option<&V>,
119) -> Result<bool, T::Error> {
120 let mut cursor = Cursor::new(txn, db)?;
121 // If the key and value weren't found, return.
122 match (cursor.set(txn, key, value)?, value) {
123 (Some((k, v)), Some(value)) if k == key && v == value => {}
124 (Some((k, _)), None) if k == key => {}
125 _ => return Ok(false),
126 }
127 // Else, the cursor is set on `(key, value)` if `value.is_some()`
128 // or on the first entry with key `key` else.
129 //
130 // We delete that position, which might be in a leaf or in an
131 // internal node.
132 del_at_cursor(txn, db, &mut cursor, false)
133}
134
135/// Delete the entry pointed to by `cursor`. The cursor can't be used
136/// after this.
137#[doc(hidden)]
138pub fn del_at_cursor<
139 T: AllocPage + LoadPage,
140 K: Storable + ?Sized + PartialEq,
141 V: Storable + ?Sized,
142 P: BTreeMutPage<K, V>,
143>(
144 txn: &mut T,
145 db: &mut Db_<K, V, P>,
146 cursor: &mut Cursor<K, V, P>,
147 decr_del_rc: bool,
148) -> Result<bool, T::Error> {
149 let p0 = cursor.len();
150
151 // If p0 < cursor.first_rc_level, the page containing the deleted
152 // element is not shared with another table. Therefore, the RC of
153 // the pages referenced by the deleted element needs to be
154 // decreased.
155 //
156 // Else, that RC doesn't need to be changed, since it's still
157 // referenced by the same pages as before, just not by the pages
158 // that are cloned by this deletion.
159 //
160 // Note in particular that if p0 == cursor.first_rc_len(), then we
161 // don't need to touch the RC, since we're cloning this page, but
162 // without including a reference to the deleted entry.
163 if decr_del_rc && p0 < cursor.first_rc_len() {
164 let (delk, delv, _) = {
165 let cur = cursor.cur();
166 P::current(txn, cur.page.as_page(), &cur.cursor).unwrap()
167 };
168 unsafe {
169 delk.drop(txn)?;
170 delv.drop(txn)?;
171 }
172 }
173
174 // Find the leftmost page in the right subtree of the deleted
175 // element, in order to find a replacement.
176 let cur = cursor.cur();
177 let right_subtree = P::right_child(cur.page.as_page(), &cur.cursor);
178 cursor.set_first_leaf(txn, right_subtree)?;
179 let leaf_is_shared = cursor.len() >= cursor.first_rc_len();
180
181 // In the leaf, mark the replacement for deletion, and keep a
182 // reference to it in k0v0 if the deletion happens in an internal
183 // node (k0v0 is `Option::None` else).
184 let (mut last_op, k0v0) = leaf_delete(txn, cursor, p0)?;
185
186 // If the leaf is shared with another tree, then since we're
187 // cloning the leaf, update the reference counts of all the
188 // references contained in the leaf.
189 if leaf_is_shared {
190 modify_rc(txn, &last_op)?;
191 }
192
193 let mut free = [[0, 0]; N_CURSORS];
194
195 // Then, climb up the stack, performing the operations lazily. At
196 // each step, we are one level above the page that we plan to
197 // modify, since `last_op` is the result of popping the
198 // stack.
199 //
200 // We iterate up to the root. The last iteration builds a modified
201 // page for the root, but doesn't actually execute it.
202 while cursor.len() > 0 {
203 // Prepare a plan for merging the current modified page (which
204 // is stored in `last_op`) with one of its neighbours.
205 let concat = concat(txn, cursor, p0, &k0v0, last_op)?;
206 let mil = concat.mod_is_left;
207
208 let incr_page = if !concat.other_is_mutable {
209 Some(CowPage {
210 offset: concat.other.offset,
211 data: concat.other.data,
212 })
213 } else {
214 None
215 };
216 let incr_mid = if cursor.len() >= cursor.first_rc_len() {
217 Some(concat.mid)
218 } else {
219 None
220 };
221
222 // Execute the modification plan, resulting in one of four
223 // different outcomes (described in the big match in
224 // `handle_merge`). This mutates or clones the current
225 // modified page, returning an `Op` describing what happened
226 // (between update, merge, rebalance and split).
227 let merge = unsafe { P::merge_or_rebalance(txn, concat)? };
228 // Prepare a description (`last_op`) of the next page
229 // modification, without mutating nor cloning that page.
230 last_op = handle_merge(
231 txn, cursor, p0, &k0v0, incr_page, incr_mid, mil, merge, &mut free,
232 )?;
233 // If the modified page is shared with another tree, we will
234 // clone it in the next round. Therefore, update the reference
235 // counts of all the references in the modified page.
236 //
237 // Since `handle_merge` pops the stack, the modified page is
238 // at level `cursor.len() + 1`.
239 if cursor.len() + 1 >= cursor.first_rc_len() {
240 modify_rc(txn, &last_op)?;
241 }
242 }
243
244 // The last modification plan was on the root, and still needs to
245 // be executed.
246 let root_is_shared = cursor.first_rc_len() == 1;
247 unsafe { update_root(txn, db, last_op, k0v0, root_is_shared, &mut free)? };
248
249 // Finally, free all the pages marked as free during the deletion,
250 // now that we don't need to read them anymore.
251 unsafe {
252 for p in free.iter() {
253 if p[0] & 1 == 1 {
254 txn.decr_rc_owned(p[0] ^ 1)?;
255 } else if p[0] > 0 {
256 txn.decr_rc(p[0])?;
257 }
258 if p[1] & 1 == 1 {
259 txn.decr_rc_owned(p[1] ^ 1)?;
260 } else if p[1] > 0 {
261 txn.decr_rc(p[1])?;
262 }
263 }
264 }
265 Ok(true)
266}
267
268/// Preparing a modified page for the leaf.
269fn leaf_delete<
270 'a,
271 T: AllocPage + LoadPage,
272 K: Storable + ?Sized,
273 V: Storable + ?Sized,
274 P: BTreeMutPage<K, V> + 'a,
275>(
276 txn: &mut T,
277 cursor: &mut Cursor<K, V, P>,
278 p0: usize,
279) -> Result<(ModifiedPage<'a, K, V, P>, Option<P::Saved>), T::Error> {
280 let is_rc = cursor.len() >= cursor.first_rc_len();
281 let del_at_internal = p0 < cursor.len();
282 let curs0 = cursor.pop().unwrap();
283 let (c0, c1) = P::split_at(&curs0.cursor);
284 let mut deleted = None;
285 if del_at_internal {
286 // In this case, we need to save the replacement, and if this
287 // leaf is shared with another tree, we also need increment
288 // the replacement's references, since we are copying it.
289 let (k, v, _) = P::current(txn, curs0.page.as_page(), &c1).unwrap();
290 if is_rc {
291 for o in k.page_references().chain(v.page_references()) {
292 txn.incr_rc(o)?;
293 }
294 }
295 deleted = Some(P::save_deleted_leaf_entry(k, v))
296 }
297 Ok((
298 ModifiedPage {
299 page: curs0.page,
300 mutable: !is_rc,
301 c0,
302 c1,
303 skip_first: true,
304 l: 0,
305 r: 0,
306 ins: None,
307 ins2: None,
308 // The following (`mod_is_left`) is meaningless in this
309 // context, and isn't actually used: indeed, the only
310 // place where this field is used is when modifying the
311 // root, when `ins.is_some()`.
312 mod_is_left: true,
313 },
314 deleted,
315 ))
316}
317
318/// From a cursor at level `p = cursor.len()`, form the
319/// concatenation of `last_op` (which is a modified page at level p +
320/// 1`, and its left or right sibling, depending on the case.
321fn concat<
322 'a,
323 T: AllocPage + LoadPage,
324 K: Storable + ?Sized,
325 V: Storable + ?Sized,
326 P: BTreeMutPage<K, V>,
327>(
328 txn: &mut T,
329 cursor: &mut Cursor<K, V, P>,
330 p0: usize,
331 k0v0: &Option<P::Saved>,
332 last_op: ModifiedPage<'a, K, V, P>,
333) -> Result<Concat<'a, K, V, P>, T::Error> {
334 let p = cursor.len();
335 let rc_level = cursor.first_rc_len();
336 let curs = cursor.current_mut();
337
338 if p == p0 {
339 // Here, p == p0, meaning that we're deleting at an internal
340 // node. If that's the case, k0v0 is `Some`, hence we can
341 // safely unwrap the replacement.
342 let (k0, v0) = unsafe { P::from_saved(k0v0.as_ref().unwrap()) };
343
344 // Since we've picked the leftmost entry of the right child of
345 // the deleted entry, the other page to consider in the
346 // concatenation is the left child of the deleted entry.
347 let other = unsafe { txn.load_page(P::left_child(curs.page.as_page(), &curs.cursor))? };
348 // Then, if the page at level `p` or one of its ancestor, is
349 // pointed at least twice (i.e. if `p >= rc_level`, or
350 // alternatively if `other` is itself pointed at least twice,
351 // the page is immutable, meaning that we can't delete on it
352 // when rebalancing.
353 let other_is_mutable = (p < rc_level) && txn.rc(other.offset)? < 2;
354 Ok(Concat {
355 modified: last_op,
356 mid: (k0, v0),
357 other,
358 mod_is_left: false,
359 other_is_mutable,
360 })
361 } else {
362 // Here, `p` is not a leaf (but `last_op` might be), and does
363 // not contain the deleted entry.
364
365 // In this case, the visited page at level `p+1` is always on
366 // the left-hand side of the cursor at level `p` (this is an
367 // invariant of cursors). However, if the cursor at level `p`
368 // is past the end of the page, we need to move one step back
369 // to find a middle element for the concatenation, in which
370 // case the visited page becomes the right-hand side of the
371 // cursor.
372 let ((k, v, r), mod_is_left) =
373 if let Some(x) = P::current(txn, curs.page.as_page(), &curs.cursor) {
374 // Not the last element of the page.
375 (x, true)
376 } else {
377 // Last element of the page.
378 let (k, v, _) = P::prev(txn, curs.page.as_page(), &mut curs.cursor).unwrap();
379 let l = P::left_child(curs.page.as_page(), &curs.cursor);
380 ((k, v, l), false)
381 };
382 let other = unsafe { txn.load_page(r)? };
383 let other_is_mutable = (p < rc_level) && txn.rc(other.offset)? < 2;
384 Ok(Concat {
385 modified: last_op,
386
387 // The middle element comes from the page above this
388 // concatenation, and hence has the same lifetime as that
389 // page. However, our choice of lifetimes can't express
390 // this, but we also know that we are not going to mutate
391 // the parent before rebalancing or merging the
392 // concatenation, and hence this pointer will be alive
393 // during these operations (i.e. during the merge or
394 // rebalance).
395 mid: unsafe { (core::mem::transmute(k), core::mem::transmute(v)) },
396
397 other,
398 mod_is_left,
399 other_is_mutable,
400 })
401 }
402}
403
404/// Prepare a modified version of the page at the current level `p =
405/// cursor.len()`.
406fn handle_merge<
407 'a,
408 T: AllocPage + LoadPage,
409 K: Storable + ?Sized + PartialEq,
410 V: Storable + ?Sized,
411 P: BTreeMutPage<K, V>,
412>(
413 txn: &mut T,
414 cursor: &mut Cursor<K, V, P>,
415 p0: usize,
416 k0v0: &Option<P::Saved>,
417 incr_other: Option<CowPage>,
418 incr_mid: Option<(&K, &V)>,
419
420 mod_is_left: bool, // The modified page in the `merge` is the left one.
421
422 merge: Op<'a, T, K, V>,
423 free: &mut [[u64; 2]; N_CURSORS],
424) -> Result<ModifiedPage<'a, K, V, P>, T::Error> {
425 let mutable = cursor.len() < cursor.first_rc_len();
426 let mut last_op = {
427 // Beware, a stack pop happens here, all subsequent references
428 // to the pointer must be updated.
429 let curs = cursor.pop().unwrap();
430 let (c0, c1) = P::split_at(&curs.cursor);
431 ModifiedPage {
432 page: curs.page,
433 mutable,
434 c0,
435 l: 0,
436 r: 0,
437 ins: None,
438 ins2: None,
439 c1,
440 skip_first: true,
441 mod_is_left,
442 }
443 };
444
445 // For merges and rebalances, take care of the reference counts of
446 // pages and key/values.
447 match merge {
448 Op::Merged { .. } | Op::Rebalanced { .. } => {
449 // Increase the RC of the "other page's" descendants. In
450 // the case of a rebalance, this has the effect of
451 // increasing the RC of the new middle entry if that entry
452 // comes from a shared page, which is what we want.
453 if let Some(other) = incr_other {
454 let mut curs = P::cursor_first(&other);
455 let left = P::left_child(other.as_page(), &curs);
456 if left > 0 {
457 txn.incr_rc(left)?;
458 }
459 while let Some((k0, v0, r)) = P::next(txn, other.as_page(), &mut curs) {
460 for o in k0.page_references().chain(v0.page_references()) {
461 txn.incr_rc(o)?;
462 }
463 if r != 0 {
464 txn.incr_rc(r)?;
465 }
466 }
467 }
468
469 // If the middle element comes from a shared page,
470 // increment its references.
471 if let Some((ref k, ref v)) = incr_mid {
472 for o in k.page_references().chain(v.page_references()) {
473 txn.incr_rc(o)?;
474 }
475 }
476 }
477 _ => {}
478 }
479
480 // Here, we match on `merge`, the operation that just happened at
481 // level `cursor.len() + 1`, and build the modification plan
482 // for the page at the current level (i.e. at level
483 // `cursor.len()`).
484 let freed = match merge {
485 Op::Merged {
486 page,
487 freed,
488 marker: _,
489 } => {
490 // If we're deleting at an internal node, the
491 // replacement has already been included in the merged
492 // page.
493 last_op.l = page.0.offset;
494 freed
495 }
496 Op::Rebalanced { k, v, l, r, freed } => {
497 // If we're deleting at an internal node, the
498 // replacement is already in pages `l` or `r`.
499 last_op.l = l;
500 last_op.r = r;
501 last_op.ins = Some((k, v));
502 freed
503 }
504 Op::Put(Put::Ok(Ok { page, freed })) => {
505 // No merge, split or rebalance has happened. If we're
506 // deleting at an internal node (i.e. if cursor.len ==
507 // p0), we need to mark the replacement here.
508 if cursor.len() + 1 == p0 {
509 // We have already incremented the references of the
510 // replacement at the leaf.
511 if let Some(k0v0) = k0v0 {
512 last_op.ins = Some(unsafe { P::from_saved(k0v0) });
513 }
514 last_op.r = page.0.offset;
515 } else {
516 last_op.skip_first = false;
517 if mod_is_left {
518 last_op.l = page.0.offset;
519 } else {
520 last_op.r = page.0.offset;
521 }
522 }
523 [freed, 0]
524 }
525 Op::Put(Put::Split {
526 left,
527 right,
528 split_key,
529 split_value,
530 freed,
531 }) => {
532 // This case only happens if `(K, V)` is not `Sized`, when
533 // either (1) a rebalance replaces a key/value pair with a
534 // larger one or (2) another split has happened in a page
535 // below.
536 let split_key_is_k0 = if cursor.len() == p0 {
537 // In this case, ins2 comes after ins, since the
538 // replacement is in the right child of the deleted
539 // key.
540 if let Some(k0v0) = k0v0 {
541 last_op.ins = Some(unsafe { P::from_saved(k0v0) });
542 }
543 last_op.ins2 = Some((split_key, split_value));
544 false
545 } else {
546 last_op.ins = Some((split_key, split_value));
547 last_op.skip_first = false;
548
549 // If the page modified in the last step is the one on
550 // the right of the current entry, move right one step
551 // before inserting the split key/value.
552 //
553 // (remember that we popped the stack upon entering
554 // this function).
555 //
556 // We need to do this because the split key/value is
557 // inserted immediately *before* the current cursor
558 // position, which is incorrect if the split key/value
559 // comes from the right-hand side of the current
560 // cursor position.
561 //
562 // This can happen in exactly two situations:
563 // - when the element we are deleting is the one we are
564 // skipping here.
565 // - when we are deleting in the rightmost child of a
566 // page.
567 if cursor.len() > 0 && !mod_is_left {
568 P::move_next(&mut last_op.c1);
569 }
570 // If the split key is the replacement element, we have
571 // already increased its counter when we deleted it from
572 // its original position at the bottom of the tree.
573 //
574 // This can happen if we replaced an element and that
575 // replacement caused a split with the replacement as the
576 // middle element.
577 if let Some(k0v0) = k0v0 {
578 assert!(cursor.len() + 1 < p0);
579 let (k0, _) = unsafe { P::from_saved(k0v0) };
580 core::ptr::eq(k0, split_key)
581 } else {
582 false
583 }
584 };
585 // The `l` and `r` fields are relative to `ins2` if
586 // `ins2.is_some()` or to `ins` else.
587 last_op.l = left.0.offset;
588 last_op.r = right.0.offset;
589 if cursor.len() + 2 >= cursor.first_rc_len() && !split_key_is_k0 {
590 for o in split_key
591 .page_references()
592 .chain(split_value.page_references())
593 {
594 txn.incr_rc(o)?;
595 }
596 }
597 [freed, 0]
598 }
599 };
600 // Free the page(s) at level `cursor.len() + 1` if it isn't
601 // shared with another tree, or if it is the first level shared
602 // with another tree.
603 if cursor.len() + 1 < cursor.first_rc_len() {
604 free[cursor.len() + 1] = freed;
605 }
606 Ok(last_op)
607}
608
609// This function modifies the reference counts of references of the
610// modified page, which is the page *about to be* modified.
611//
612// This function is always called when `m` is an internal node.
613fn modify_rc<
614 T: AllocPage + LoadPage,
615 K: Storable + ?Sized,
616 V: Storable + ?Sized,
617 P: BTreePage<K, V>,
618>(
619 txn: &mut T,
620 m: &ModifiedPage<K, V, P>,
621) -> Result<(), T::Error> {
622 let mut c0 = m.c0.clone();
623 let mut c1 = m.c1.clone();
624 let mut left = P::left_child(m.page.as_page(), &c0);
625 while let Some((k, v, r)) = P::next(txn, m.page.as_page(), &mut c0) {
626 for o in k.page_references().chain(v.page_references()) {
627 txn.incr_rc(o)?;
628 }
629 if left != 0 {
630 txn.incr_rc(left)?;
631 }
632 left = r;
633 }
634 // The insertions are taken care of in `handle_merge` above,
635 // so we can directly move to the `c1` part of the
636 // modification.
637 if m.skip_first {
638 P::move_next(&mut c1);
639 }
640 // If we are not updating the left child of c1's first
641 // element, increment that left child.
642 if m.l == 0 {
643 if left != 0 {
644 txn.incr_rc(left)?;
645 }
646 }
647
648 // If we are not updating the right child of the first
649 // element of `c1`, increment that right child's RC.
650 if let Some((k, v, r)) = P::next(txn, m.page.as_page(), &mut c1) {
651 for o in k.page_references().chain(v.page_references()) {
652 txn.incr_rc(o)?;
653 }
654 // If `m.skip_first`, we have already skipped the entry above,
655 // so this `r` has nothing to do with any update.
656 //
657 // Else, if we aren't skipping, but also aren't updating the
658 // right child of the current entry, also increase the RC.
659 if (m.skip_first || m.r == 0) && r != 0 {
660 txn.incr_rc(r)?;
661 }
662 }
663 // Finally, increment the RCs of all other elements in `c1`.
664 while let Some((k, v, r)) = P::next(txn, m.page.as_page(), &mut c1) {
665 for o in k.page_references().chain(v.page_references()) {
666 txn.incr_rc(o)?;
667 }
668 if r != 0 {
669 txn.incr_rc(r)?;
670 }
671 }
672 Ok(())
673}
674
675unsafe fn update_root<
676 T: AllocPage + LoadPage,
677 K: Storable + ?Sized,
678 V: Storable + ?Sized,
679 P: BTreeMutPage<K, V>,
680>(
681 txn: &mut T,
682 db: &mut Db_<K, V, P>,
683 last_op: ModifiedPage<K, V, P>,
684 k0v0: Option<P::Saved>,
685 is_rc: bool,
686 free: &mut [[u64; 2]; N_CURSORS],
687) -> Result<(), T::Error> {
688 if let Some(d) = single_child(&last_op) {
689 // If the root had only one element, and the last operation
690 // was a merge, this decreases the depth of the tree.
691 //
692 // We don't do this if the table is empty (i.e. if `d == 0`),
693 // in order to be consistent with put and drop.
694 if d > 0 {
695 if last_op.page.is_dirty() {
696 txn.decr_rc_owned(last_op.page.offset)?;
697 } else {
698 txn.decr_rc(last_op.page.offset)?;
699 }
700 db.db = core::num::NonZeroU64::new_unchecked(d);
701 } else {
702 // The page becomes empty.
703 let (page, freed) = P::del(txn, last_op.page, last_op.mutable, &last_op.c1, d)?;
704 free[0][0] = freed;
705 db.db = core::num::NonZeroU64::new_unchecked(page.0.offset);
706 }
707 return Ok(());
708 }
709
710 // Else, the last operation `last_op` was relative to the root,
711 // but it hasn't been applied yet. We apply it now:
712 match modify::<_, K, V, P>(txn, last_op)? {
713 Put::Ok(Ok { page, freed }) => {
714 // Here, the root was simply updated (i.e. some elements
715 // might have been deleted/inserted/updated), so we just
716 // need to update the Db.
717 free[0][0] = freed;
718 db.db = core::num::NonZeroU64::new_unchecked(page.0.offset)
719 }
720 Put::Split {
721 split_key: k,
722 split_value: v,
723 left: MutPage(CowPage { offset: l, .. }),
724 right: MutPage(CowPage { offset: r, .. }),
725 freed,
726 } => {
727 // Else, the root has split, and we need to allocate a new
728 // page to hold the split key/value and the two sides of
729 // the split.
730 free[0][0] = freed;
731 let mut page = unsafe { txn.alloc_page()? };
732 P::init(&mut page);
733 let mut c = P::cursor_before(&page.0);
734 P::move_next(&mut c);
735 let page = if let Put::Ok(p) =
736 unsafe { P::put(txn, page.0, true, false, &c, k, v, None, l, r)? }
737 {
738 p.page
739 } else {
740 unreachable!()
741 };
742 let split_key_is_k0 = if let Some(ref k0v0) = k0v0 {
743 let (k0, _) = unsafe { P::from_saved(k0v0) };
744 core::ptr::eq(k0, k)
745 } else {
746 false
747 };
748 // If the root isn't shared with another tree, and the
749 // split key is different from the replacement, increment
750 // the RCs of the references contained in the split
751 // key/value.
752 //
753 // The replacement need not be incremented again, since it
754 // was already increment when we took it from the page in
755 // `leaf_delete`.
756 if is_rc && !split_key_is_k0 {
757 for o in k.page_references().chain(v.page_references()) {
758 txn.incr_rc(o)?;
759 }
760 }
761 // Finally, update the database.
762 db.db = core::num::NonZeroU64::new_unchecked(page.0.offset)
763 }
764 }
765 Ok(())
766}
767
768fn single_child<K: Storable + ?Sized, V: Storable + ?Sized, P: BTreeMutPage<K, V>>(
769 m: &ModifiedPage<K, V, P>,
770) -> Option<u64> {
771 let mut c1 = m.c1.clone();
772 if m.skip_first {
773 P::move_next(&mut c1);
774 }
775 if P::is_empty(&m.c0) && m.ins.is_none() && P::is_empty(&c1) {
776 Some(m.l)
777 } else {
778 None
779 }
780}
781
782/// Apply the modifications to a page. This is exclusively used for
783/// the root page, because other modifications are applied during a
784/// merge/rebalance attempts.
785fn modify<'a, T: AllocPage, K: Storable + ?Sized, V: Storable + ?Sized, P: BTreeMutPage<K, V>>(
786 txn: &mut T,
787 m: ModifiedPage<'a, K, V, P>,
788) -> Result<super::put::Put<'a, K, V>, T::Error> {
789 if let Some((k, v)) = m.ins {
790 // The following call might actually replace the first element
791 // of `m.c1` if `m.skip_first`.
792 let mut c1 = m.c1.clone();
793 if !m.skip_first && !m.mod_is_left {
794 // This means that the page below just split, since we
795 // have to insert an extra entry on the root page.
796 //
797 // However, the extra entry is to be inserted (by
798 // `P::put`) *before* `c1`'s first element, which is
799 // incorrect since the page that split is the right child
800 // of `c1`'s first element. Therefore, we need to move
801 // `c1` one notch to the right.
802 assert!(m.ins2.is_none());
803 P::move_next(&mut c1);
804 }
805 unsafe {
806 P::put(
807 txn,
808 m.page,
809 m.mutable,
810 m.skip_first,
811 &c1,
812 k,
813 v,
814 m.ins2,
815 m.l,
816 m.r,
817 )
818 }
819 } else {
820 // If there's no insertion to do, we might still need to
821 // delete elements, or update children.
822 if m.skip_first {
823 let (page, freed) = unsafe { P::del(txn, m.page, m.mutable, &m.c1, m.l)? };
824 Ok(Put::Ok(Ok { freed, page }))
825 } else if m.l > 0 {
826 // If the left page of the current entry has changed,
827 // update it.
828 Ok(Put::Ok(unsafe {
829 P::update_left_child(txn, m.page, m.mutable, &m.c1, m.l)?
830 }))
831 } else {
832 // If there's no insertion, and `m.l == 0`, we need to
833 // update the right child of the current entry. The
834 // following moves one step to the right and updates the
835 // left child:
836 let mut c1 = m.c1.clone();
837 P::move_next(&mut c1);
838 Ok(Put::Ok(unsafe {
839 P::update_left_child(txn, m.page, m.mutable, &c1, m.r)?
840 }))
841 }
842 }
843}