Dont segfault if btree range is not in order

This commit is contained in:
Brian Vincent 2017-02-02 00:58:18 -06:00
parent 24a70eb598
commit fb9104768c
3 changed files with 134 additions and 115 deletions

View File

@ -714,6 +714,11 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
/// range from 4 to 10.
///
/// # Panics
///
/// Panics if range `start > end`.
/// Panics if range `start == end` and both bounds are `Excluded`.
///
/// # Examples
///
/// Basic usage:
@ -739,64 +744,11 @@ impl<K: Ord, V> BTreeMap<K, V> {
pub fn range<T: ?Sized, R>(&self, range: R) -> Range<K, V>
where T: Ord, K: Borrow<T>, R: RangeArgument<T>
{
let min = range.start();
let max = range.end();
let front = match min {
Included(key) => {
match search::search_tree(self.root.as_ref(), key) {
Found(kv_handle) => {
match kv_handle.left_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => last_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Excluded(key) => {
match search::search_tree(self.root.as_ref(), key) {
Found(kv_handle) => {
match kv_handle.right_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => first_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Unbounded => first_leaf_edge(self.root.as_ref()),
};
let root1 = self.root.as_ref();
let root2 = self.root.as_ref();
let (f, b) = range_search(root1, root2, range);
let back = match max {
Included(key) => {
match search::search_tree(self.root.as_ref(), key) {
Found(kv_handle) => {
match kv_handle.right_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => first_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Excluded(key) => {
match search::search_tree(self.root.as_ref(), key) {
Found(kv_handle) => {
match kv_handle.left_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => last_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Unbounded => last_leaf_edge(self.root.as_ref()),
};
Range {
front: front,
back: back,
}
Range { front: f, back: b}
}
/// Constructs a mutable double-ended iterator over a sub-range of elements in the map.
@ -806,6 +758,11 @@ impl<K: Ord, V> BTreeMap<K, V> {
/// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
/// range from 4 to 10.
///
/// # Panics
///
/// Panics if range `start > end`.
/// Panics if range `start == end` and both bounds are `Excluded`.
///
/// # Examples
///
/// Basic usage:
@ -831,66 +788,13 @@ impl<K: Ord, V> BTreeMap<K, V> {
pub fn range_mut<T: ?Sized, R>(&mut self, range: R) -> RangeMut<K, V>
where T: Ord, K: Borrow<T>, R: RangeArgument<T>
{
let min = range.start();
let max = range.end();
let root1 = self.root.as_mut();
let root2 = unsafe { ptr::read(&root1) };
let front = match min {
Included(key) => {
match search::search_tree(root1, key) {
Found(kv_handle) => {
match kv_handle.left_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => last_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Excluded(key) => {
match search::search_tree(root1, key) {
Found(kv_handle) => {
match kv_handle.right_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => first_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Unbounded => first_leaf_edge(root1),
};
let back = match max {
Included(key) => {
match search::search_tree(root2, key) {
Found(kv_handle) => {
match kv_handle.right_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => first_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Excluded(key) => {
match search::search_tree(root2, key) {
Found(kv_handle) => {
match kv_handle.left_edge().force() {
Leaf(bottom) => bottom,
Internal(internal) => last_leaf_edge(internal.descend()),
}
}
GoDown(bottom) => bottom,
}
}
Unbounded => last_leaf_edge(root2),
};
let (f, b) = range_search(root1, root2, range);
RangeMut {
front: front,
back: back,
front: f,
back: b,
_marker: PhantomData,
}
}
@ -1827,6 +1731,80 @@ fn last_leaf_edge<BorrowType, K, V>
}
}
fn range_search<BorrowType, K, V, Q: ?Sized, R: RangeArgument<Q>>(
root1: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
root2: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
range: R
)-> (Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>,
Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>)
where Q: Ord, K: Borrow<Q>
{
match (range.start(), range.end()) {
(Excluded(s), Excluded(e)) if s==e =>
panic!("range start and end are equal and excluded in BTreeMap"),
(Included(s), Included(e)) |
(Included(s), Excluded(e)) |
(Excluded(s), Included(e)) |
(Excluded(s), Excluded(e)) if s>e =>
panic!("range start is greater than range end in BTreeMap"),
_ => {},
};
let mut min_node = root1;
let mut max_node = root2;
let mut min_found = false;
let mut max_found = false;
let mut diverged = false;
loop {
let min_edge = match (min_found, range.start()) {
(false, Included(key)) => match search::search_linear(&min_node, key) {
(i, true) => { min_found = true; i },
(i, false) => i,
},
(false, Excluded(key)) => match search::search_linear(&min_node, key) {
(i, true) => { min_found = true; i+1 },
(i, false) => i,
},
(_, Unbounded) => 0,
(true, Included(_)) => min_node.keys().len(),
(true, Excluded(_)) => 0,
};
let max_edge = match (max_found, range.end()) {
(false, Included(key)) => match search::search_linear(&max_node, key) {
(i, true) => { max_found = true; i+1 },
(i, false) => i,
},
(false, Excluded(key)) => match search::search_linear(&max_node, key) {
(i, true) => { max_found = true; i },
(i, false) => i,
},
(_, Unbounded) => max_node.keys().len(),
(true, Included(_)) => 0,
(true, Excluded(_)) => max_node.keys().len(),
};
if !diverged {
if max_edge < min_edge { panic!("Ord is ill-defined in BTreeMap range") }
if min_edge != max_edge { diverged = true; }
}
let front = Handle::new_edge(min_node, min_edge);
let back = Handle::new_edge(max_node, max_edge);
match (front.force(), back.force()) {
(Leaf(f), Leaf(b)) => {
return (f, b);
},
(Internal(min_int), Internal(max_int)) => {
min_node = min_int.descend();
max_node = max_int.descend();
},
_ => unreachable!("BTreeMap has different depths"),
};
}
}
#[inline(always)]
unsafe fn unwrap_unchecked<T>(val: Option<T>) -> T {
val.unwrap_or_else(|| {

View File

@ -58,7 +58,7 @@ pub fn search_node<BorrowType, K, V, Type, Q: ?Sized>(
}
}
fn search_linear<BorrowType, K, V, Type, Q: ?Sized>(
pub fn search_linear<BorrowType, K, V, Type, Q: ?Sized>(
node: &NodeRef<BorrowType, K, V, Type>,
key: &Q
) -> (usize, bool)
@ -73,4 +73,3 @@ fn search_linear<BorrowType, K, V, Type, Q: ?Sized>(
}
(node.keys().len(), false)
}

View File

@ -178,6 +178,48 @@ fn test_range_small() {
assert_eq!(j, size - 2);
}
#[test]
fn test_range_equal_empty_cases() {
let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
assert_eq!(map.range((Included(2), Excluded(2))).next(), None);
assert_eq!(map.range((Excluded(2), Included(2))).next(), None);
}
#[test]
#[should_panic]
fn test_range_equal_excluded() {
let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
map.range((Excluded(2), Excluded(2)));
}
#[test]
#[should_panic]
fn test_range_backwards_1() {
let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
map.range((Included(3), Included(2)));
}
#[test]
#[should_panic]
fn test_range_backwards_2() {
let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
map.range((Included(3), Excluded(2)));
}
#[test]
#[should_panic]
fn test_range_backwards_3() {
let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
map.range((Excluded(3), Included(2)));
}
#[test]
#[should_panic]
fn test_range_backwards_4() {
let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
map.range((Excluded(3), Excluded(2)));
}
#[test]
fn test_range_1000() {
let size = 1000;