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}