Skip to content

Commit

Permalink
Rollup merge of rust-lang#80353 - ssomers:btree_test_split_off, r=Mar…
Browse files Browse the repository at this point in the history
…k-Simulacrum

BTreeMap: test split_off (and append) more thoroughly

Using DeterministicRng as a poor man's property based testing rig.
r? `@Mark-Simulacrum`
  • Loading branch information
Dylan-DPC authored Dec 28, 2020
2 parents 14c42d8 + d473cbe commit 2660e04
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 7 deletions.
9 changes: 6 additions & 3 deletions library/alloc/src/collections/btree/append.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,18 @@ impl<K, V> Root<K, V> {
// the appended elements even if advancing the iterator panicks.
*length += 1;
}
self.fix_right_edge();
self.fix_right_border_of_plentiful();
}

fn fix_right_edge(&mut self) {
// Handle underfull nodes, start from the top.
/// Stock up any underfull nodes on the right border of the tree.
/// The other nodes, those that are not the root nor a rightmost edge,
/// must have MIN_LEN elements to spare.
fn fix_right_border_of_plentiful(&mut self) {
let mut cur_node = self.borrow_mut();
while let Internal(internal) = cur_node.force() {
// Check if right-most child is underfull.
let mut last_kv = internal.last_kv().consider_for_balancing();
debug_assert!(last_kv.left_child_len() >= MIN_LEN * 2);
let right_child_len = last_kv.right_child_len();
if right_child_len < MIN_LEN {
// We need to steal.
Expand Down
20 changes: 19 additions & 1 deletion library/alloc/src/collections/btree/map/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1821,7 +1821,6 @@ fn test_append_ord_chaos() {
}

fn rand_data(len: usize) -> Vec<(u32, u32)> {
assert!(len * 2 <= 70029); // from that point on numbers repeat
let mut rng = DeterministicRng::new();
Vec::from_iter((0..len).map(|_| (rng.next(), rng.next())))
}
Expand Down Expand Up @@ -1886,6 +1885,25 @@ fn test_split_off_tiny_right_height_2() {
assert_eq!(*right.last_key_value().unwrap().0, last);
}

#[test]
fn test_split_off_halfway() {
let mut rng = DeterministicRng::new();
for &len in &[NODE_CAPACITY, 25, 50, 75, 100] {
let mut data = Vec::from_iter((0..len).map(|_| (rng.next(), ())));
// Insertion in non-ascending order creates some variation in node length.
let mut map = BTreeMap::from_iter(data.iter().copied());
data.sort();
let small_keys = data.iter().take(len / 2).map(|kv| kv.0);
let large_keys = data.iter().skip(len / 2).map(|kv| kv.0);
let split_key = large_keys.clone().next().unwrap();
let right = map.split_off(&split_key);
map.check();
right.check();
assert!(map.keys().copied().eq(small_keys));
assert!(right.keys().copied().eq(large_keys));
}
}

#[test]
fn test_split_off_large_random_sorted() {
// Miri is too slow
Expand Down
7 changes: 5 additions & 2 deletions library/alloc/src/collections/btree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub unsafe fn unwrap_unchecked<T>(val: Option<T>) -> T {
#[cfg(test)]
/// XorShiftRng
struct DeterministicRng {
count: usize,
x: u32,
y: u32,
z: u32,
Expand All @@ -47,11 +48,13 @@ struct DeterministicRng {
#[cfg(test)]
impl DeterministicRng {
fn new() -> Self {
DeterministicRng { x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb }
DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb }
}

/// Guarantees that the first 70029 results are unique.
/// Guarantees that each returned number is unique.
fn next(&mut self) -> u32 {
self.count += 1;
assert!(self.count <= 70029);
let x = self.x;
let t = x ^ (x << 11);
self.x = self.y;
Expand Down
4 changes: 3 additions & 1 deletion library/alloc/src/collections/btree/set/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,8 +696,10 @@ fn test_first_last() {
assert_eq!(a.pop_last(), None);
}

// Unlike the function with the same name in map/tests, returns no values.
// Which also means it returns different predetermined pseudo-random keys,
// and the test cases using this function explore slightly different trees.
fn rand_data(len: usize) -> Vec<u32> {
assert!(len <= 70029); // from that point on numbers repeat
let mut rng = DeterministicRng::new();
Vec::from_iter((0..len).map(|_| rng.next()))
}
Expand Down
5 changes: 5 additions & 0 deletions library/alloc/src/collections/btree/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ impl<K, V> Root<K, V> {
}
}

/// Stock up or merge away any underfull nodes on the right border of the
/// tree. The other nodes, those that are not the root nor a rightmost edge,
/// must already have at least MIN_LEN elements.
fn fix_right_border(&mut self) {
self.fix_top();

Expand All @@ -72,6 +75,7 @@ impl<K, V> Root<K, V> {
}
cur_node = last_kv.into_right_child();
}
debug_assert!(cur_node.len() > MIN_LEN);
}
}

Expand All @@ -98,6 +102,7 @@ impl<K, V> Root<K, V> {
}
cur_node = first_kv.into_left_child();
}
debug_assert!(cur_node.len() > MIN_LEN);
}
}

Expand Down

0 comments on commit 2660e04

Please sign in to comment.