Skip to content

Commit 3b6c4fe

Browse files
committed
BTreeMap: stop mistaking node::MIN_LEN as a node level constraint
1 parent 2e8a54a commit 3b6c4fe

File tree

6 files changed

+55
-55
lines changed

6 files changed

+55
-55
lines changed

Diff for: library/alloc/src/collections/btree/map.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ mod entry;
1717
pub use entry::{Entry, OccupiedEntry, VacantEntry};
1818
use Entry::*;
1919

20+
/// Minimum number of elements in nodes that are not a root.
21+
/// We might temporarily have fewer elements during methods.
22+
pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT;
23+
2024
/// A map based on a B-Tree.
2125
///
2226
/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing
@@ -1094,13 +1098,13 @@ impl<K: Ord, V> BTreeMap<K, V> {
10941098
// Check if right-most child is underfull.
10951099
let mut last_edge = internal.last_edge();
10961100
let right_child_len = last_edge.reborrow().descend().len();
1097-
if right_child_len < node::MIN_LEN {
1101+
if right_child_len < MIN_LEN {
10981102
// We need to steal.
10991103
let mut last_kv = match last_edge.left_kv() {
11001104
Ok(left) => left,
11011105
Err(_) => unreachable!(),
11021106
};
1103-
last_kv.bulk_steal_left(node::MIN_LEN - right_child_len);
1107+
last_kv.bulk_steal_left(MIN_LEN - right_child_len);
11041108
last_edge = last_kv.right_edge();
11051109
}
11061110

Diff for: library/alloc/src/collections/btree/map/tests.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@ impl<'a, K: 'a, V: 'a> BTreeMap<K, V> {
5050
{
5151
if let Some(root) = &self.root {
5252
let root_node = root.node_as_ref();
53+
5354
assert!(root_node.ascend().is_err());
5455
root_node.assert_back_pointers();
55-
root_node.assert_ascending();
56-
assert_eq!(self.length, root_node.assert_and_add_lengths());
56+
57+
let counted = root_node.assert_ascending();
58+
assert_eq!(self.length, counted);
59+
assert_eq!(self.length, root_node.calc_length());
60+
61+
root_node.assert_min_len(if root_node.height() > 0 { 1 } else { 0 });
5762
} else {
5863
assert_eq!(self.length, 0);
5964
}
@@ -76,6 +81,18 @@ impl<'a, K: 'a, V: 'a> BTreeMap<K, V> {
7681
}
7782
}
7883

84+
impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> {
85+
pub fn assert_min_len(self, min_len: usize) {
86+
assert!(self.len() >= min_len, "{} < {}", self.len(), min_len);
87+
if let node::ForceResult::Internal(node) = self.force() {
88+
for idx in 0..=node.len() {
89+
let edge = unsafe { Handle::new_edge(node, idx) };
90+
edge.descend().assert_min_len(MIN_LEN);
91+
}
92+
}
93+
}
94+
}
95+
7996
// Test our value of MIN_INSERTS_HEIGHT_2. It may change according to the
8097
// implementation of insertion, but it's best to be aware of when it does.
8198
#[test]

Diff for: library/alloc/src/collections/btree/node.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ use crate::alloc::{AllocRef, Global, Layout};
3838
use crate::boxed::Box;
3939

4040
const B: usize = 6;
41-
pub const MIN_LEN: usize = B - 1;
4241
pub const CAPACITY: usize = 2 * B - 1;
42+
pub const MIN_LEN_AFTER_SPLIT: usize = B - 1;
4343
const KV_IDX_CENTER: usize = B - 1;
4444
const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1;
4545
const EDGE_IDX_RIGHT_OF_CENTER: usize = B;

Diff for: library/alloc/src/collections/btree/node/tests.rs

+16-40
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,26 @@ use crate::string::String;
55
use core::cmp::Ordering::*;
66

77
impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> {
8+
/// Asserts that the back pointer in each reachable node points to its parent.
89
pub fn assert_back_pointers(self) {
9-
match self.force() {
10-
ForceResult::Leaf(_) => {}
11-
ForceResult::Internal(node) => {
12-
for idx in 0..=node.len() {
13-
let edge = unsafe { Handle::new_edge(node, idx) };
14-
let child = edge.descend();
15-
assert!(child.ascend().ok() == Some(edge));
16-
child.assert_back_pointers();
17-
}
10+
if let ForceResult::Internal(node) = self.force() {
11+
for idx in 0..=node.len() {
12+
let edge = unsafe { Handle::new_edge(node, idx) };
13+
let child = edge.descend();
14+
assert!(child.ascend().ok() == Some(edge));
15+
child.assert_back_pointers();
1816
}
1917
}
2018
}
2119

22-
pub fn assert_ascending(self)
20+
/// Asserts that the keys are in strictly ascending order.
21+
/// Returns how many keys it encountered.
22+
pub fn assert_ascending(self) -> usize
2323
where
2424
K: Copy + Debug + Ord,
2525
{
2626
struct SeriesChecker<T> {
27+
num_seen: usize,
2728
previous: Option<T>,
2829
}
2930
impl<T: Copy + Debug + Ord> SeriesChecker<T> {
@@ -32,10 +33,11 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
3233
assert!(previous < next, "{:?} >= {:?}", previous, next);
3334
}
3435
self.previous = Some(next);
36+
self.num_seen += 1;
3537
}
3638
}
3739

38-
let mut checker = SeriesChecker { previous: None };
40+
let mut checker = SeriesChecker { num_seen: 0, previous: None };
3941
self.visit_nodes_in_order(|pos| match pos {
4042
navigate::Position::Leaf(node) => {
4143
for idx in 0..node.len() {
@@ -49,33 +51,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
4951
}
5052
navigate::Position::Internal(_) => {}
5153
});
52-
}
53-
54-
pub fn assert_and_add_lengths(self) -> usize {
55-
let mut internal_length = 0;
56-
let mut internal_kv_count = 0;
57-
let mut leaf_length = 0;
58-
self.visit_nodes_in_order(|pos| match pos {
59-
navigate::Position::Leaf(node) => {
60-
let is_root = self.height() == 0;
61-
let min_len = if is_root { 0 } else { MIN_LEN };
62-
assert!(node.len() >= min_len, "{} < {}", node.len(), min_len);
63-
leaf_length += node.len();
64-
}
65-
navigate::Position::Internal(node) => {
66-
let is_root = self.height() == node.height();
67-
let min_len = if is_root { 1 } else { MIN_LEN };
68-
assert!(node.len() >= min_len, "{} < {}", node.len(), min_len);
69-
internal_length += node.len();
70-
}
71-
navigate::Position::InternalKV(_) => {
72-
internal_kv_count += 1;
73-
}
74-
});
75-
assert_eq!(internal_length, internal_kv_count);
76-
let total = internal_length + leaf_length;
77-
assert_eq!(self.calc_length(), total);
78-
total
54+
checker.num_seen
7955
}
8056

8157
pub fn dump_keys(self) -> String
@@ -124,8 +100,8 @@ fn test_splitpoint() {
124100
right_len += 1;
125101
}
126102
}
127-
assert!(left_len >= MIN_LEN);
128-
assert!(right_len >= MIN_LEN);
103+
assert!(left_len >= MIN_LEN_AFTER_SPLIT);
104+
assert!(right_len >= MIN_LEN_AFTER_SPLIT);
129105
assert!(left_len + right_len == CAPACITY);
130106
}
131107
}

Diff for: library/alloc/src/collections/btree/remove.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use super::node::{self, marker, ForceResult, Handle, NodeRef};
1+
use super::map::MIN_LEN;
2+
use super::node::{marker, ForceResult, Handle, NodeRef};
23
use super::unwrap_unchecked;
34
use core::mem;
45
use core::ptr;
@@ -40,7 +41,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInter
4041
// Handle underflow
4142
let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() };
4243
let mut at_leaf = true;
43-
while cur_node.len() < node::MIN_LEN {
44+
while cur_node.len() < MIN_LEN {
4445
match handle_underfull_node(cur_node) {
4546
UnderflowResult::AtRoot => break,
4647
UnderflowResult::Merged(edge, merged_with_left, offset) => {

Diff for: library/alloc/src/collections/btree/split.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use super::node::{self, ForceResult::*, Root};
2-
use super::search::{self, SearchResult::*};
1+
use super::map::MIN_LEN;
2+
use super::node::{ForceResult::*, Root};
3+
use super::search::{search_node, SearchResult::*};
34
use core::borrow::Borrow;
45

56
impl<K, V> Root<K, V> {
@@ -20,7 +21,7 @@ impl<K, V> Root<K, V> {
2021
let mut right_node = right_root.node_as_mut();
2122

2223
loop {
23-
let mut split_edge = match search::search_node(left_node, key) {
24+
let mut split_edge = match search_node(left_node, key) {
2425
// key is going to the right tree
2526
Found(handle) => handle.left_edge(),
2627
GoDown(handle) => handle,
@@ -65,9 +66,9 @@ impl<K, V> Root<K, V> {
6566
cur_node = last_kv.merge().descend();
6667
} else {
6768
let right_len = last_kv.reborrow().right_edge().descend().len();
68-
// `MINLEN + 1` to avoid readjust if merge happens on the next level.
69-
if right_len < node::MIN_LEN + 1 {
70-
last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len);
69+
// `MIN_LEN + 1` to avoid readjust if merge happens on the next level.
70+
if right_len < MIN_LEN + 1 {
71+
last_kv.bulk_steal_left(MIN_LEN + 1 - right_len);
7172
}
7273
cur_node = last_kv.right_edge().descend();
7374
}
@@ -91,8 +92,9 @@ impl<K, V> Root<K, V> {
9192
cur_node = first_kv.merge().descend();
9293
} else {
9394
let left_len = first_kv.reborrow().left_edge().descend().len();
94-
if left_len < node::MIN_LEN + 1 {
95-
first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len);
95+
// `MIN_LEN + 1` to avoid readjust if merge happens on the next level.
96+
if left_len < MIN_LEN + 1 {
97+
first_kv.bulk_steal_right(MIN_LEN + 1 - left_len);
9698
}
9799
cur_node = first_kv.left_edge().descend();
98100
}

0 commit comments

Comments
 (0)