merkle_search_tree/page.rs
1use std::{cmp::Ordering, hash::Hasher, ops::DerefMut};
2
3use siphasher::sip128::{Hasher128, SipHasher24};
4
5use crate::{
6 digest::{Digest, PageDigest, ValueDigest},
7 node::Node,
8 visitor::Visitor,
9};
10
11#[derive(Debug)]
12pub(crate) enum UpsertResult<K> {
13 /// The key & value hash were successfully upserted.
14 Complete,
15
16 /// An intermediate page must be inserted between the caller and the callee.
17 InsertIntermediate(K),
18}
19
20/// A group of [`Node`] instances at the same location within the tree.
21///
22/// A page within an MST is a probabilistically sized structure, with varying
23/// numbers of [`Node`] within. A page has a min/max key range defined by the
24/// nodes within it, and the page hash acts as a content hash, describing the
25/// state of the page and the nodes within it.
26#[derive(Debug, PartialEq, Eq, Clone)]
27pub struct Page<const N: usize, K> {
28 level: u8,
29
30 /// The cached hash in this page; the cumulation of the hashes of the
31 /// sub-tree rooted at this page.
32 tree_hash: Option<PageDigest>,
33
34 /// A vector of nodes in this page, ordered min to max by key.
35 nodes: Vec<Node<N, K>>,
36
37 /// The page for keys greater-than all keys in nodes.
38 high_page: Option<Box<Page<N, K>>>,
39}
40
41impl<const N: usize, K> Page<N, K> {
42 pub(super) const fn new(level: u8, nodes: Vec<Node<N, K>>) -> Self {
43 Self {
44 level,
45 tree_hash: None,
46 nodes,
47 high_page: None,
48 }
49 }
50
51 /// Returns the the [`Node`] in this page.
52 pub fn nodes(&self) -> &[Node<N, K>] {
53 &self.nodes
54 }
55
56 /// Returns the tree level / deterministic / logical hight of this page in
57 /// the tree.
58 pub const fn level(&self) -> u8 {
59 self.level
60 }
61
62 /// Return the cached hash of this page if any, covering the nodes and the
63 /// sub-tree rooted at `self`.
64 pub fn hash(&self) -> Option<&PageDigest> {
65 self.tree_hash.as_ref()
66 }
67
68 /// Set the high page pointer for this page.
69 ///
70 /// # Panics
71 ///
72 /// Panics if this page already has a high page linked, or `p` contains no
73 /// nodes.
74 pub(crate) fn insert_high_page(&mut self, p: Box<Self>) {
75 debug_assert!(self.high_page.is_none());
76 debug_assert!(!p.nodes().is_empty());
77
78 // Invalidate the hash of this page.
79 self.tree_hash = None;
80 self.high_page = Some(p)
81 }
82
83 /// Return a pointer to the linked high page, if any.
84 pub(crate) fn high_page(&self) -> Option<&Self> {
85 self.high_page.as_deref()
86 }
87
88 /// Perform a depth-first, in-order traversal, yielding each [`Page`] and
89 /// [`Node`] to `visitor`.
90 ///
91 /// If `high_page` is true, this page was linked to from the parent via a
92 /// high page pointer.
93 pub(crate) fn in_order_traversal<'a, T>(&'a self, visitor: &mut T, high_page: bool) -> bool
94 where
95 T: Visitor<'a, N, K>,
96 {
97 if !visitor.visit_page(self, high_page) {
98 return false;
99 }
100
101 for node in &self.nodes {
102 if !node.depth_first(visitor) {
103 return false;
104 }
105 }
106
107 if !visitor.post_visit_page(self) {
108 return false;
109 }
110
111 if let Some(h) = &self.high_page {
112 if !h.in_order_traversal(visitor, true) {
113 return false;
114 }
115 }
116
117 true
118 }
119
120 /// Return the minimum key stored in this page.
121 ///
122 /// This is an `O(1)` operation.
123 ///
124 /// # Panics
125 ///
126 /// Panics if there are no nodes in this page.
127 #[inline]
128 pub(crate) fn min_key(&self) -> &K {
129 self.nodes.first().unwrap().key()
130 }
131
132 /// Return the maximum key stored in this page.
133 ///
134 /// This is an `O(1)` operation.
135 ///
136 /// # Panics
137 ///
138 /// Panics if there are no nodes in this page.
139 #[inline]
140 pub(crate) fn max_key(&self) -> &K {
141 self.nodes.last().unwrap().key()
142 }
143
144 /// Descend down the minimum (left most) path (if any) and return the
145 /// minimum key in the subtree rooted at `p`.
146 ///
147 /// This is an `O(logN)` operation.
148 #[inline]
149 pub(crate) fn min_subtree_key(&self) -> &K {
150 // This is mildly faster than the iterator chain approach.
151 let v = self.nodes().first().and_then(|v| v.lt_pointer());
152 if let Some(v) = v {
153 return v.min_subtree_key();
154 }
155
156 self.min_key()
157 }
158
159 /// Chase the high page pointers to the maximum page value of the subtree
160 /// rooted at `p`.
161 ///
162 /// This is an `O(logN)` operation.
163 #[inline]
164 pub(crate) fn max_subtree_key(&self) -> &K {
165 self.high_page()
166 .map(|v| v.max_subtree_key())
167 .unwrap_or_else(|| self.max_key())
168 }
169}
170
171impl<const N: usize, K> Page<N, K>
172where
173 K: AsRef<[u8]>,
174{
175 /// Generate the page hash and cache the value, covering the nodes and the
176 /// sub-tree rooted at `self`.
177 pub(crate) fn maybe_generate_hash(&mut self, hasher: &SipHasher24) {
178 if self.tree_hash.is_some() {
179 return;
180 }
181
182 let mut h = *hasher;
183
184 // NOTE: changing the ordering of the hashed elements is a breaking
185 // change.
186 //
187 // This order may be changed only if releasing a new major version, as
188 // it invalidates existing hashes.
189
190 // Hash all nodes & their child pages
191 for n in &mut self.nodes {
192 // Hash the lt child page of this node, if any
193 if let Some(child_hash) = n.lt_pointer_mut().as_deref_mut().map(|v| {
194 v.maybe_generate_hash(hasher);
195 v.hash().unwrap()
196 }) {
197 h.write(child_hash.as_ref());
198 }
199
200 // Hash the node value itself
201 h.write(n.key().as_ref());
202 h.write(n.value_hash().as_ref());
203 }
204
205 // Hash the high page, if any
206 if let Some(high_hash) = self.high_page.as_deref_mut().map(|v| {
207 v.maybe_generate_hash(hasher);
208 v.hash().unwrap()
209 }) {
210 h.write(high_hash.as_ref());
211 }
212
213 self.tree_hash = Some(PageDigest::from(Digest::new(h.finish128().as_bytes())));
214 }
215}
216
217impl<const N: usize, K> Page<N, K>
218where
219 K: PartialOrd,
220{
221 /// Insert or update the value hash of `key`, setting it to `value`, found
222 /// at tree `level`.
223 ///
224 /// Returns true if the key was found, or false otherwise.
225 ///
226 /// If the key is found/modified, the cached page hash is invalidated.
227 pub(crate) fn upsert(&mut self, key: K, level: u8, value: ValueDigest<N>) -> UpsertResult<K> {
228 match level.cmp(&self.level) {
229 // Level is less than this page's level - descend down the tree.
230 Ordering::Less => {
231 // A non-zero page can never be empty, and level is less than
232 // this page, which means this page must be non-zero.
233 debug_assert_ne!(!self.level, 0);
234 debug_assert!(!self.nodes.is_empty());
235
236 // Find the node that is greater-than-or-equal-to key to descend
237 // into.
238 //
239 // Otherwise insert this node into the high page.
240 let ptr = find_idx(&self.nodes, &key);
241
242 let page = match self.nodes.get_mut(ptr) {
243 Some(v) => {
244 debug_assert!(*v.key() > key);
245 v.lt_pointer_mut()
246 }
247 None => &mut self.high_page,
248 };
249
250 let page = page.get_or_insert_with(|| Box::new(Self::new(level, vec![])));
251 if let UpsertResult::InsertIntermediate(key) =
252 page.upsert(key, level, value.clone())
253 {
254 insert_intermediate_page(page, key, level, value);
255 }
256 }
257 Ordering::Equal => self.upsert_node(key, value),
258 // Level is more than this page's level
259 Ordering::Greater => {
260 // This level is lower than the desired level, the parent is
261 // higher than the desired level.
262 //
263 // Returning false will case the parent will insert a new page.
264 return UpsertResult::InsertIntermediate(key); // No need to
265 // update the hash
266 // of this subtree
267 }
268 }
269
270 // This page, or one below it was modified. Invalidate the pre-computed
271 // page hash, if any.
272 //
273 // This marks the page as "dirty" causing the hash to be recomputed on
274 // demand, coalescing multiple updates instead of hashing for each.
275 self.tree_hash = None;
276
277 UpsertResult::Complete
278 }
279
280 /// Insert a node into this page, splitting any child pages as necessary.
281 pub(crate) fn upsert_node(&mut self, key: K, value: ValueDigest<N>) {
282 // Find the appropriate child pointer to follow.
283 let idx = find_idx(&self.nodes, &key);
284
285 // At this point the new key should be inserted has been identified -
286 // node_idx points to the first node greater-than-or-equal to key.
287 //
288 // In this example, we're inserting the key "C":
289 //
290 // node_idx
291 // ║
292 // ║
293 // ▼
294 // ┌──────────┬──────────┐
295 // │ LT Node │ GTE Node │
296 // │ A │ E │
297 // └──────────┴──────────┘
298 // │ │
299 // ┌──────┘ │
300 // ▼ ▼
301 // ┌─Page──────┐ ┌─Page──────┐
302 // │ │ │ ┌───┬───┐ │
303 // │ Always LT │ │ │ B │ D │ │
304 // │ new key │ │ └───┴───┘ │
305 // └───────────┘ └───────────┘
306 //
307 // The less-than node never needs splitting, because all the keys within
308 // it are strictly less than the insert key.
309 //
310 // The GTE child page does need splitting - all the keys less than "C"
311 // need moving into the new node's less-than page.
312 //
313 // If the new "C" node will be inserted at the end of the node array,
314 // there's no GTE node to check - instead the high page may contain
315 // relevant nodes that must be split.
316
317 let page_to_split = match self.nodes.get_mut(idx) {
318 Some(n) if *n.key() == key => {
319 n.update_value_hash(value);
320 return;
321 }
322 Some(n) => n.lt_pointer_mut(),
323 None => &mut self.high_page,
324 };
325
326 // Split the higher-page, either within a GTE node or the high page.
327 let mut new_lt_page = split_off_lt(page_to_split, &key).map(Box::new);
328
329 if let Some(lt_page) = &mut new_lt_page {
330 debug_assert!(self.level > lt_page.level);
331 debug_assert!(!lt_page.nodes.is_empty());
332 debug_assert!(*lt_page.max_key() < key);
333
334 let high_page_lt = split_off_lt(&mut lt_page.high_page, &key);
335 let gte_page = std::mem::replace(&mut lt_page.high_page, high_page_lt.map(Box::new));
336 if let Some(gte_page) = gte_page {
337 debug_assert!(self.level > gte_page.level);
338 debug_assert!(!gte_page.nodes.is_empty());
339 debug_assert!(*gte_page.max_key() > key);
340
341 self.insert_high_page(gte_page);
342 }
343 }
344
345 self.nodes.insert(idx, Node::new(key, value, new_lt_page));
346 }
347}
348
349/// Split `page`, mutating it such that it contains only nodes with keys ordered
350/// strictly-less than `key`, returning a new [`Page`] containing the
351/// greater-than-or-equal-to nodes.
352///
353/// If splitting `page` would leave it with no nodes, it is set to [`None`].
354///
355/// NOTE: this only splits the page provided - it is up to the caller to split
356/// any high pages as necessary.
357///
358/// # Panics
359///
360/// This method panics if attempting to split a non-empty page (root pages are
361/// never split).
362fn split_off_lt<const N: usize, T, K>(page: &mut Option<T>, key: &K) -> Option<Page<N, K>>
363where
364 K: PartialOrd,
365 T: DerefMut<Target = Page<N, K>>,
366{
367 let page_ref = page.as_deref_mut()?;
368 debug_assert!(!page_ref.nodes.is_empty());
369
370 // A page should be split into two parts - one page containing the elements
371 // less-than "key", and one containing parts greater-or-equal to "key".
372 let partition_idx = find_idx(&page_ref.nodes, key);
373
374 // All the nodes are greater-than-or-equal-to "key" - there's no less-than
375 // nodes to return.
376 if partition_idx == 0 {
377 debug_assert!(page_ref.min_key() > key);
378
379 // The first gte node may have a lt_pointer with nodes that are lt key.
380 return match split_off_lt(page_ref.nodes[0].lt_pointer_mut(), key) {
381 Some(v) => {
382 // Invalidate the page hash as the lt_page was split or the keys
383 // moved, changing the content the hash covers.
384 page_ref.tree_hash = None;
385 Some(v)
386 }
387 None => None,
388 };
389 }
390
391 // All the nodes are less than key.
392 //
393 // As an optimisation, simply return the existing page as the new page
394 // (retaining the pre-computed hash if possible) and invalidate the old
395 // page.
396 if partition_idx == page_ref.nodes.len() {
397 debug_assert!(page_ref.max_key() < key);
398
399 // The page may have a high page, which may have nodes within the
400 // (max(nodes.key), key) range
401 let lt_high_nodes = split_off_lt(&mut page_ref.high_page, key);
402
403 // If existing the high page was split (both sides are non-empty) then
404 // invalidate the page hash.
405 //
406 // This effectively invalidates the page range of the returned lt_page
407 // as the cached hash covers the high page (which has now been split,
408 // changing the content).
409 if lt_high_nodes.is_some() && page_ref.high_page.is_some() {
410 page_ref.tree_hash = None;
411 }
412
413 // Put the lt nodes back into the high page, taking the gte nodes from
414 // the high page.
415 //
416 // This leaves the lt_high_nodes in the high page link of page_ref.
417 let gte_high_page = std::mem::replace(&mut page_ref.high_page, lt_high_nodes.map(Box::new));
418
419 // Initialise the page we're about to return.
420 //
421 // This puts an empty page into page_ref, taking the new lt nodes in
422 // page (potentially with the high page linked to lt_high_nodes)
423 let lt_page = Some(std::mem::replace(
424 page_ref,
425 Page::new(page_ref.level, vec![]),
426 ));
427
428 // Put the gte nodes into the input page, if any (page should contain
429 // all gte nodes after this split).
430 match gte_high_page {
431 Some(p) => *page_ref = *p,
432 None => *page = None,
433 }
434
435 return lt_page;
436 }
437
438 // Invalidate the page hash as at least one node will be removed.
439 page_ref.tree_hash = None;
440
441 // Obtain the set of nodes that are greater-than-or-equal-to "key".
442 let gte_nodes: Vec<_> = page_ref.nodes.drain(partition_idx..).collect();
443 debug_assert!(!gte_nodes.is_empty());
444
445 // page_ref now contains the lt nodes, and a high page that may be non-empty
446 // and gte than key.
447
448 // Initialise a new page to hold the gte nodes.
449 let mut gte_page = Page::new(page_ref.level, gte_nodes);
450 debug_assert!(gte_page.max_key() > key);
451
452 // Move the input high page onto the new gte page (which continues to be gte
453 // than the nodes taken from the input page).
454 if let Some(h) = page_ref.high_page.take() {
455 debug_assert!(!h.nodes.is_empty());
456 debug_assert!(h.level < page_ref.level);
457 debug_assert!(h.min_key() > key);
458 gte_page.insert_high_page(h);
459 }
460
461 // The first gte node may contain a lt_pointer with keys lt key, recurse
462 // into it.
463 let lt_key_high_nodes = split_off_lt(gte_page.nodes.first_mut().unwrap().lt_pointer_mut(), key);
464
465 // In which case it is gte all node keys in the lt page (or it wouldn't have
466 // been on the gte node).
467 //
468 // Add this to the new lt_page's high page next.
469
470 // Replace the input page with the gte nodes, taking the page containing the
471 // lt nodes and returning them to the caller.
472 let mut lt_page = std::mem::replace(page_ref, gte_page);
473 debug_assert!(!lt_page.nodes.is_empty());
474 debug_assert!(lt_page.max_key() < key);
475
476 // Insert the high page, if any.
477 if let Some(h) = lt_key_high_nodes {
478 debug_assert!(h.level < page_ref.level);
479 debug_assert!(h.max_key() < key);
480 debug_assert!(!h.nodes.is_empty());
481 lt_page.insert_high_page(Box::new(h));
482 }
483
484 Some(lt_page)
485}
486
487pub(crate) fn insert_intermediate_page<const N: usize, T, K>(
488 child_page: &mut T,
489 key: K,
490 level: u8,
491 value: ValueDigest<N>,
492) where
493 K: PartialOrd,
494 T: DerefMut<Target = Page<N, K>>,
495{
496 // Terminology:
497 //
498 // * parent_page: top of the stack, parent of child_page
499 // * intermediate/new page: intermediate page with level between parent_page
500 // and child_page to be inserted between them.
501 // * child_page: the lower page, child of parent_page
502 //
503
504 // The child page asked this page to insert a new intermediate page at this
505 // location.
506 //
507 // ┌──────────┐
508 // │ New Root │
509 // ┌────│ B │─────┐ Level N
510 // │ └──────────┘ │
511 // lt_pointer high_page
512 // │ │
513 // │ │
514 // ┌ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
515 // ┌─────▼────┐ ┌─────▼────┐
516 // │ │ LT Node │ │ GTE Node │ Child Page │
517 // │ A │ │ C │ Level 0
518 // │ └──────────┘ └──────────┘ │
519 // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
520 //
521 // The child page must be split into nodes less-than key, and those
522 // greater-than-or-equal to key to preserve the ordering once this new page
523 // containing key is inserted. Both halves must be linked into the new page.
524
525 debug_assert!(child_page.level() < level);
526 debug_assert!(!child_page.nodes.is_empty());
527
528 // Split the child page into (less-than, greater-than) pages, split at the
529 // point where key would reside.
530 //
531 // NOTE: this may leave "page" empty if all the nodes moved to the lt page.
532 let mut lt_page = {
533 let child_page2 = child_page.deref_mut();
534 let mut child_page_ref = Some(&mut *child_page2);
535 split_off_lt(&mut child_page_ref, &key)
536 };
537
538 // If all the nodes moved out of the child_page and into lt_page it
539 // indicates that all nodes had keys less-than the new key, meaning there
540 // may be nodes in the lt_page high page that need splitting, as it may
541 // contain values between max(lt_page.nodes) and key.
542 //
543 // For example, when inserting 4:
544 //
545 // ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─
546 // ┌───┐ New Parent │
547 // ┌──│ │ 4 │ Level 2
548 // │ └───┘ │
549 // │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─
550 // │
551 // ┌ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
552 // ┌───┬───▼───────┐ Child Page │
553 // │ │ 1 │ 2 │ high │ Level 1
554 // └───┴───┴───────┘ │
555 // └ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─
556 // │
557 // ┌ ─ ▼ ─ ─ ─ ─ ─ ─ ─ ─ ┐
558 // ┌───┬───┐
559 // │ │ 3 │ 5 │ Level 0 │
560 // └───┴───┘
561 // └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
562 //
563 // The existing entry of 5 must be moved, as it is greater than the new
564 // parent:
565 //
566 // ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
567 // New Parent │
568 // │ ┌───┬───────┐ Level 2
569 // ┌───│ 4 │ high │───┐ │
570 // │ │ └───┴───────┘ │
571 // │ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ┘
572 // ▼ │
573 // ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
574 // ┌───┬───┬───────┐ Child Page │ │
575 // │ │ 1 │ 2 │ high │ Level 1 │
576 // └───┴───┴───────┘ │ │
577 // └ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ │
578 // ▼ ▼
579 // ┌ ─ ─ ─ ─ ─ ─ ─ ┌ ─ ─ ─ ─ ─ ─ ─
580 // ┌───┐ │ ┌───┐ │
581 // ││ 3 │ Level 0 ││ 5 │ Level 0
582 // └───┘ │ └───┘ │
583 // └ ─ ─ ─ ─ ─ ─ ─ └ ─ ─ ─ ─ ─ ─ ─
584 //
585 // To do this, we split the high page, attaching the lt_nodes to the lt_page
586 // created above, and attach the remaining gte_nodes to the high_page of the
587 // intermediate_page.
588 let mut gte_page = None;
589 if let Some(lt_page) = &mut lt_page {
590 debug_assert!(level > lt_page.level);
591 debug_assert!(!lt_page.nodes.is_empty());
592 debug_assert!(*lt_page.max_key() < key);
593
594 let high_page_lt = split_off_lt(&mut lt_page.high_page, &key);
595 gte_page = std::mem::replace(&mut lt_page.high_page, high_page_lt.map(Box::new));
596 if let Some(gte_page) = >e_page {
597 debug_assert!(level > gte_page.level);
598 debug_assert!(!gte_page.nodes.is_empty());
599 debug_assert!(*gte_page.max_key() > key);
600 }
601 }
602
603 // Create the new node.
604 let node = Node::new(key, value, None);
605
606 // Create the new intermediate page, between the parent page and the child
607 // page.
608 let mut intermediate_page = Page::new(level, vec![node]);
609 if let Some(gte_page) = gte_page {
610 intermediate_page.insert_high_page(gte_page);
611 }
612
613 // Replace the page pointer at this level to point to the new page, taking
614 // the page that now contains the lt nodes after the split.
615 let gte_page = std::mem::replace(child_page.deref_mut(), intermediate_page);
616
617 // At this point, we have this structure:
618 //
619 // ┌─────────────┐
620 // │ This Page │
621 // └─────────────┘
622 // │
623 // ▼
624 // ┌───────────────────┐
625 // │ Intermediate Page │
626 // └───────────────────┘
627 //
628 // The lt_page and gtw_pages need linking into the new node within the new
629 // intermediate page.
630
631 *child_page.nodes[0].lt_pointer_mut() = lt_page.map(Box::new);
632 if !gte_page.nodes.is_empty() {
633 debug_assert!(gte_page.max_key() > child_page.nodes[0].key()); // "key"
634 debug_assert!(level > gte_page.level);
635 child_page.high_page = Some(Box::new(gte_page));
636 }
637}
638
639/// Return the index into `nodes` at which `key` should be located.
640fn find_idx<const N: usize, K>(nodes: &[Node<N, K>], key: &K) -> usize
641where
642 K: PartialOrd,
643{
644 nodes
645 .iter()
646 .position(|v| *key <= *v.key())
647 .unwrap_or(nodes.len())
648}
649
650#[cfg(test)]
651mod tests {
652 use assert_matches::assert_matches;
653
654 use super::*;
655 use crate::{assert_tree, digest::Digest};
656
657 const MOCK_VALUE: ValueDigest<1> = ValueDigest::new(Digest::new([0; 1]));
658 const MOCK_PAGE_HASH: PageDigest = PageDigest::new([0; 16]);
659
660 #[test]
661 #[should_panic(expected = "!page_ref.nodes.is_empty()")]
662 fn test_split_page_empty() {
663 let mut gte_page = Some(Box::new(Page::<1, _>::new(42, vec![])));
664 let _lt_page = split_off_lt(&mut gte_page, &5);
665 }
666
667 #[test]
668 fn test_split_page_single_node_lt() {
669 let mut gte_page = Some(Box::new(Page::new(
670 42,
671 vec![Node::new(2, MOCK_VALUE, None)],
672 )));
673
674 gte_page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
675
676 let lt_page = split_off_lt(&mut gte_page, &5);
677 assert_matches!(gte_page, None);
678
679 assert_matches!(lt_page, Some(p) => {
680 assert_eq!(p.level, 42);
681
682 // The unmodified page has the hash retained.
683 assert_eq!(p.tree_hash, Some(MOCK_PAGE_HASH));
684
685 assert_eq!(p.nodes, [
686 Node::new(2, MOCK_VALUE, None),
687 ]);
688 });
689 }
690
691 #[test]
692 fn test_split_page_single_node_gt() {
693 let mut gte_page = Some(Box::new(Page::new(
694 42,
695 vec![Node::new(2, MOCK_VALUE, None)],
696 )));
697
698 gte_page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
699
700 let lt_page = split_off_lt(&mut gte_page, &1);
701 assert_matches!(gte_page, Some(p) => {
702 assert_eq!(p.level, 42);
703
704 // The unmodified page has the hash retained.
705 assert_eq!(p.tree_hash, Some(MOCK_PAGE_HASH));
706
707 assert_eq!(p.nodes, [
708 Node::new(2, MOCK_VALUE, None),
709 ]);
710 });
711
712 assert_matches!(lt_page, None);
713 }
714
715 /// Test that a page containing entirely gte nodes, but with a linked high
716 /// page that requires splitting, has the page has invalidated.
717 #[test]
718 fn test_split_page_single_node_gt_with_high_page_split() {
719 let mut high_page = Box::new(Page::new(
720 40,
721 vec![
722 Node::new(10, MOCK_VALUE, None),
723 Node::new(15, MOCK_VALUE, None),
724 ],
725 ));
726 high_page.tree_hash = Some(MOCK_PAGE_HASH);
727
728 let mut page = Box::new(Page::new(42, vec![Node::new(5, MOCK_VALUE, None)]));
729 page.tree_hash = Some(MOCK_PAGE_HASH);
730 page.insert_high_page(high_page);
731
732 let mut page = Some(page);
733
734 let lt_page = split_off_lt(&mut page, &12);
735 assert_matches!(page, Some(p) => {
736 assert_eq!(p.level, 40);
737
738 // The modified page has the hash invalidated as the high page was
739 // split.
740 assert_eq!(p.tree_hash, None);
741
742 assert_eq!(p.nodes, [
743 Node::new(15, MOCK_VALUE, None),
744 ]);
745 assert_eq!(p.high_page, None);
746 });
747
748 assert_matches!(lt_page, Some(p) => {
749 assert_eq!(p.level, 42);
750 assert_eq!(p.tree_hash, None);
751
752 assert_eq!(p.nodes, [
753 Node::new(5, MOCK_VALUE, None),
754 ]);
755
756 assert_eq!(p.high_page.as_ref().unwrap().nodes, [
757 Node::new(10, MOCK_VALUE, None),
758 ]);
759 assert_eq!(p.high_page.as_ref().unwrap().tree_hash, None);
760 });
761 }
762
763 /// Test that a page containing entirely lt nodes, but with a recursively
764 /// followed child page that requires splitting, has the page has
765 /// invalidated.
766 ///
767 /// Toe ensure page hashes are recursively invalidated, the split child page
768 /// is actually two steps away from the target page.
769 #[test]
770 fn test_split_page_single_node_gt_with_child_page_split() {
771 // The bottom-most/deepest child page that requires splitting.
772 let child_2 = Some(Box::new(Page::new(
773 40,
774 vec![
775 Node::new(1, MOCK_VALUE, None),
776 // Split at 2
777 Node::new(3, MOCK_VALUE, None),
778 ],
779 )));
780 // The parent of child_2
781 let child_1 = Some(Box::new(Page::new(
782 41,
783 vec![Node::new(4, MOCK_VALUE, child_2)],
784 )));
785
786 // The parent of child_1
787 let mut page = Some(Box::new(Page::new(
788 42,
789 vec![Node::new(5, MOCK_VALUE, child_1)],
790 )));
791
792 page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
793
794 let lt_page = split_off_lt(&mut page, &2);
795 assert_matches!(page, Some(p) => {
796 assert_eq!(p.level, 42);
797
798 // The modified page has the hash invalidated as the child page was
799 // split.
800 assert_eq!(p.tree_hash, None);
801
802 assert_eq!(p.nodes, [
803 Node::new(5, MOCK_VALUE, Some(Box::new(Page::new(
804 41,
805 vec![Node::new(4, MOCK_VALUE, Some(Box::new(Page::new(
806 40,
807 // 1 split away
808 vec![Node::new(3, MOCK_VALUE, None)],
809 ))))],
810 )))),
811 ]);
812 });
813
814 assert_matches!(lt_page, Some(p) => {
815 assert_eq!(p.level, 40);
816
817 assert_eq!(p.nodes, [
818 Node::new(1, MOCK_VALUE, None),
819 ]);
820
821 assert_eq!(p.tree_hash, None);
822 });
823 }
824
825 #[test]
826 fn test_split_page_eq() {
827 let mut gte_page = Some(Box::new(Page::new(
828 42,
829 vec![
830 Node::new(1, MOCK_VALUE, None),
831 Node::new(2, MOCK_VALUE, None),
832 Node::new(4, MOCK_VALUE, None),
833 ],
834 )));
835
836 gte_page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
837
838 let lt_page = split_off_lt(&mut gte_page, &2);
839 assert_matches!(gte_page, Some(p) => {
840 assert_eq!(p.level, 42);
841 assert_eq!(p.tree_hash, None);
842
843 assert_eq!(p.nodes, [
844 Node::new(2, MOCK_VALUE, None),
845 Node::new(4, MOCK_VALUE, None)
846 ]);
847 });
848
849 assert_matches!(lt_page, Some(p) => {
850 assert_eq!(p.level, 42);
851
852 // The modified page has the hash invalidated.
853 assert_eq!(p.tree_hash, None);
854
855 assert_eq!(p.nodes, [
856 Node::new(1, MOCK_VALUE, None),
857 ]);
858 });
859 }
860
861 #[test]
862 fn test_split_page_lt() {
863 let mut gte_page = Some(Box::new(Page::new(
864 42,
865 vec![
866 Node::new(1, MOCK_VALUE, None),
867 Node::new(2, MOCK_VALUE, None),
868 Node::new(4, MOCK_VALUE, None),
869 ],
870 )));
871
872 gte_page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
873
874 let lt_page = split_off_lt(&mut gte_page, &3);
875 assert_matches!(gte_page, Some(p) => {
876 assert_eq!(p.level, 42);
877 assert_eq!(p.tree_hash, None);
878
879 assert_eq!(p.nodes, [
880 Node::new(4, MOCK_VALUE, None)
881 ]);
882 });
883
884 assert_matches!(lt_page, Some(p) => {
885 assert_eq!(p.level, 42);
886
887 // The modified page has the hash invalidated.
888 assert_eq!(p.tree_hash, None);
889
890 assert_eq!(p.nodes, [
891 Node::new(1, MOCK_VALUE, None),
892 Node::new(2, MOCK_VALUE, None),
893 ]);
894 });
895 }
896
897 #[test]
898 fn test_split_page_all_gt() {
899 let mut gte_page = Some(Box::new(Page::new(
900 42,
901 vec![
902 Node::new(1, MOCK_VALUE, None),
903 Node::new(2, MOCK_VALUE, None),
904 Node::new(4, MOCK_VALUE, None),
905 ],
906 )));
907
908 gte_page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
909
910 let lt_page = split_off_lt(&mut gte_page, &0);
911 assert_matches!(gte_page, Some(p) => {
912 assert_eq!(p.level, 42);
913
914 // The new page containing all the nodes retains the pre-computed
915 // hash.
916 assert_eq!(p.tree_hash, Some(MOCK_PAGE_HASH));
917
918 assert_eq!(p.nodes, [
919 Node::new(1, MOCK_VALUE, None),
920 Node::new(2, MOCK_VALUE, None),
921 Node::new(4, MOCK_VALUE, None),
922 ]);
923 });
924
925 assert_matches!(lt_page, None);
926 }
927
928 #[test]
929 fn test_split_page_all_lt() {
930 let mut gte_page = Some(Box::new(Page::new(
931 42,
932 vec![
933 Node::new(1, MOCK_VALUE, None),
934 Node::new(2, MOCK_VALUE, None),
935 Node::new(4, MOCK_VALUE, None),
936 ],
937 )));
938
939 gte_page.as_mut().unwrap().tree_hash = Some(MOCK_PAGE_HASH);
940
941 let lt_page = split_off_lt(&mut gte_page, &10);
942 assert_matches!(gte_page, None);
943
944 assert_matches!(lt_page, Some(p) => {
945 assert_eq!(p.level, 42);
946
947 // The unmodified page retains the pre-computed hash.
948 assert_eq!(p.tree_hash, Some(MOCK_PAGE_HASH));
949
950 assert_eq!(p.nodes, [
951 Node::new(1, MOCK_VALUE, None),
952 Node::new(2, MOCK_VALUE, None),
953 Node::new(4, MOCK_VALUE, None),
954 ]);
955 });
956 }
957
958 #[test]
959 fn test_upsert_less_than_split_child() {
960 let mut p = Page::new(1, vec![Node::new(4, MOCK_VALUE, None)]);
961
962 p.upsert(3, 0, MOCK_VALUE);
963 p.upsert(1, 0, MOCK_VALUE);
964 p.upsert(2, 1, MOCK_VALUE);
965
966 assert_tree!(page = p);
967 }
968
969 #[test]
970 fn test_split_page_recursive_lt_pointer() {
971 let mut lt_pointer_page = Page::new(52, vec![Node::new(86, MOCK_VALUE, None)]);
972 lt_pointer_page.tree_hash = Some(MOCK_PAGE_HASH);
973
974 let mut root = Box::new(Page::new(
975 42,
976 vec![Node::new(161, MOCK_VALUE, Some(Box::new(lt_pointer_page)))],
977 ));
978 root.tree_hash = Some(MOCK_PAGE_HASH);
979
980 let key = 160;
981
982 let mut root = Some(root);
983 let lt_page = split_off_lt(&mut root, &key);
984
985 assert_matches!(lt_page, Some(p) => {
986 assert_eq!(p.level, 52);
987 assert_matches!(p.nodes(), [n] if *n.key() == 86)
988 });
989 }
990
991 #[test]
992 fn test_split_page_recursive_high_page() {
993 let mut high_page = Page::new(32, vec![Node::new(44, MOCK_VALUE, None)]);
994 high_page.tree_hash = Some(MOCK_PAGE_HASH);
995
996 let mut root = Box::new(Page::new(42, vec![Node::new(42, MOCK_VALUE, None)]));
997 root.tree_hash = Some(MOCK_PAGE_HASH);
998 root.insert_high_page(Box::new(high_page));
999
1000 let key = 43;
1001
1002 let mut root = Some(root);
1003 let lt_page = split_off_lt(&mut root, &key);
1004
1005 assert_matches!(lt_page, Some(p) => {
1006 assert_eq!(p.level, 42);
1007 assert_matches!(p.nodes(), [n] if *n.key() == 42)
1008 });
1009 assert_matches!(root, Some(p) => {
1010 assert_eq!(p.level, 32);
1011 assert_matches!(p.nodes(), [n] if *n.key() == 44)
1012 });
1013 }
1014}