Skip to content

Commit ea30b90

Browse files
committed
BTree: add split_off_range methods
1 parent 03c8ffa commit ea30b90

File tree

8 files changed

+1070
-52
lines changed

8 files changed

+1070
-52
lines changed

library/alloc/src/collections/btree/fix.rs

+323-11
Large diffs are not rendered by default.

library/alloc/src/collections/btree/map.rs

+60
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,66 @@ impl<K, V> BTreeMap<K, V> {
12041204
BTreeMap { root: Some(right_root), length: right_len }
12051205
}
12061206

1207+
/// Splits the collection into two. Returns a new collection with all keys in the given range.
1208+
///
1209+
/// # Panics
1210+
///
1211+
/// Panics if range `start > end`.
1212+
/// Panics if range `start == end` and both bounds are `Excluded`.
1213+
/// May panic if the [`Ord`] implementation of type `T` is ill-defined,
1214+
/// either because it does not form a total order or because it does not
1215+
/// correspond to the [`Ord`] implementation of type `K`.
1216+
///
1217+
/// # Examples
1218+
///
1219+
/// Basic usage:
1220+
///
1221+
/// ```
1222+
/// #![feature(btree_split_off_range)]
1223+
/// use std::collections::BTreeMap;
1224+
///
1225+
/// let mut a = BTreeMap::new();
1226+
/// a.insert(1, "a");
1227+
/// a.insert(2, "b");
1228+
/// a.insert(3, "c");
1229+
/// a.insert(17, "d");
1230+
/// a.insert(41, "e");
1231+
///
1232+
/// let b = a.split_off_range(&3..&33);
1233+
///
1234+
/// assert_eq!(a.len(), 3);
1235+
/// assert_eq!(b.len(), 2);
1236+
///
1237+
/// assert_eq!(a[&1], "a");
1238+
/// assert_eq!(a[&2], "b");
1239+
/// assert_eq!(a[&41], "e");
1240+
///
1241+
/// assert_eq!(b[&3], "c");
1242+
/// assert_eq!(b[&17], "d");
1243+
/// ```
1244+
#[unstable(feature = "btree_split_off_range", issue = "81074")]
1245+
pub fn split_off_range<T: ?Sized, R>(&mut self, range: R) -> Self
1246+
where
1247+
T: Ord,
1248+
K: Borrow<T> + Ord,
1249+
R: RangeBounds<T>,
1250+
{
1251+
if self.is_empty() {
1252+
return Self::new();
1253+
}
1254+
1255+
let total_num = self.length;
1256+
let left_root = self.root.as_mut().unwrap(); // unwrap succeeds because not empty
1257+
1258+
let mut right_root = left_root.split_off_range(range);
1259+
right_root.fix_both_borders();
1260+
1261+
let (new_left_len, right_len) = Root::calc_split_length(total_num, &left_root, &right_root);
1262+
self.length = new_left_len;
1263+
1264+
BTreeMap { root: Some(right_root), length: right_len }
1265+
}
1266+
12071267
/// Creates an iterator that visits all elements (key-value pairs) in
12081268
/// ascending key order and uses a closure to determine if an element should
12091269
/// be removed. If the closure returns `true`, the element is removed from

library/alloc/src/collections/btree/map/tests.rs

+28
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use std::ops::RangeBounds;
1717
use std::panic::{catch_unwind, AssertUnwindSafe};
1818
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1919

20+
mod split_off_range;
21+
2022
// Minimum number of elements to insert, to guarantee a tree with 2 levels,
2123
// i.e., a tree who's root is an internal node at height 1, with edges to leaf nodes.
2224
// It's not the minimum size: removing an element from such a tree does not always reduce height.
@@ -27,6 +29,12 @@ const MIN_INSERTS_HEIGHT_1: usize = node::CAPACITY + 1;
2729
// It's not the minimum size: removing an element from such a tree does not always reduce height.
2830
const MIN_INSERTS_HEIGHT_2: usize = 89;
2931

32+
// Like MIN_INSERTS_HEIGHT_2, with an additional internal level.
33+
const MIN_INSERTS_HEIGHT_3: usize = 628;
34+
35+
// Like MIN_INSERTS_HEIGHT_3, with an additional internal level.
36+
const MIN_INSERTS_HEIGHT_4: usize = 4401;
37+
3038
// Gathers all references from a mutable iterator and makes sure Miri notices if
3139
// using them is dangerous.
3240
fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>) {
@@ -169,6 +177,26 @@ fn test_levels() {
169177
// - 5 elements in right child's last grandchild
170178
assert_eq!(map.height(), Some(2));
171179
assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2, "{}", map.dump_keys());
180+
181+
if cfg!(miri) {
182+
// Miri is too slow
183+
return;
184+
}
185+
while map.height() == Some(2) {
186+
let last_key = *map.last_key_value().unwrap().0;
187+
map.insert(last_key + 1, ());
188+
}
189+
map.check();
190+
assert_eq!(map.height(), Some(3));
191+
assert_eq!(map.len(), MIN_INSERTS_HEIGHT_3);
192+
193+
while map.height() == Some(3) {
194+
let last_key = *map.last_key_value().unwrap().0;
195+
map.insert(last_key + 1, ());
196+
}
197+
map.check();
198+
assert_eq!(map.height(), Some(4));
199+
assert_eq!(map.len(), MIN_INSERTS_HEIGHT_4);
172200
}
173201

174202
// Ensures the testing infrastructure usually notices order violations.

0 commit comments

Comments
 (0)