Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove panic! instances #69

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions rstar-benches/benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn bulk_load_comparison(c: &mut Criterion) {
b.iter(move || {
let mut rtree = rstar::RTree::new();
for point in &points {
rtree.insert(*point);
rtree.insert(*point).unwrap();
}
});
});
Expand All @@ -67,7 +67,7 @@ fn tree_creation_quality(c: &mut Criterion) {
let tree_bulk_loaded = RTree::<_, Params>::bulk_load_with_params(points.clone());
let mut tree_sequential = RTree::new();
for point in &points {
tree_sequential.insert(*point);
tree_sequential.insert(*point).unwrap();
}

let query_points = create_random_points(100, SEED_2);
Expand Down
15 changes: 9 additions & 6 deletions rstar-demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ fn handle_input(window: &Window, scene: &mut Scene) -> Option<RenderData> {
&Vector2::new(width as f32, height as f32),
);

scene.tree_2d.insert(unprojected.coords.into());
scene.tree_2d.insert(unprojected.coords.into()).unwrap();
is_dirty = true;
}
WindowEvent::CursorPos(x, y, _) if scene.render_mode == RenderMode::TwoD => {
Expand All @@ -201,12 +201,15 @@ fn handle_input(window: &Window, scene: &mut Scene) -> Option<RenderData> {
if is_dirty {
for point in points_to_add {
if scene.render_mode == RenderMode::ThreeD {
scene.tree_3d.insert(point);
scene.tree_3d.insert(point).unwrap();
} else {
scene.tree_2d.insert([
point[0] * window.width() as f32 * 0.5,
point[1] * window.height() as f32 * 0.5,
]);
scene
.tree_2d
.insert([
point[0] * window.width() as f32 * 0.5,
point[1] * window.height() as f32 * 0.5,
])
.unwrap();
}
}
create_render_data_from_scene(scene).into()
Expand Down
2 changes: 1 addition & 1 deletion rstar/src/algorithm/iterators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ mod test {
let points = create_random_points(NUM_POINTS, SEED_1);
let mut tree = RTree::new();
for p in &points {
tree.insert(*p);
tree.insert(*p).unwrap();
}
let mut count = 0usize;
for p in tree.iter() {
Expand Down
4 changes: 2 additions & 2 deletions rstar/src/algorithm/removal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ mod test {
let mut tree = RTree::bulk_load(points.clone());
for (point_to_remove, point_to_add) in points.iter().zip(later_insertions.iter()) {
assert!(tree.remove_at_point(point_to_remove).is_some());
tree.insert(*point_to_add);
tree.insert(*point_to_add).unwrap();
}
assert_eq!(tree.size(), SIZE);
assert!(points.iter().all(|p| !tree.contains(p)));
Expand All @@ -98,7 +98,7 @@ mod test {
initial_rectangles.iter().zip(new_rectangles.iter())
{
assert!(tree.remove(rectangle_to_remove).is_some());
tree.insert(*rectangle_to_add);
tree.insert(*rectangle_to_add).unwrap();
}
assert_eq!(tree.size(), SIZE);
assert!(initial_rectangles.iter().all(|p| !tree.contains(p)));
Expand Down
31 changes: 17 additions & 14 deletions rstar/src/algorithm/rstar.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error;

use crate::envelope::Envelope;
use crate::node::{envelope_for_children, ParentNode, RTreeNode};
use crate::object::RTreeObject;
Expand Down Expand Up @@ -26,7 +28,7 @@ where
}

impl InsertionStrategy for RStarInsertionStrategy {
fn insert<T, Params>(tree: &mut RTree<T, Params>, t: T)
fn insert<T, Params>(tree: &mut RTree<T, Params>, t: T) -> Result<(), Box<dyn error::Error>>
where
Params: RTreeParams,
T: RTreeObject,
Expand All @@ -42,15 +44,16 @@ impl InsertionStrategy for RStarInsertionStrategy {
let mut target_height = 0;
let mut insertion_stack = Vec::new();
match first {
InsertionResult::Split(node) => insertion_stack.push(PerformSplit(node)),
InsertionResult::Reinsert(nodes_to_reinsert, real_target_height) => {
Ok(InsertionResult::Split(node)) => insertion_stack.push(PerformSplit(node)),
Ok(InsertionResult::Reinsert(nodes_to_reinsert, real_target_height)) => {
insertion_stack.extend(nodes_to_reinsert.into_iter().map(PerformReinsert));
target_height = real_target_height;
}
InsertionResult::Complete => {}
Ok(InsertionResult::Complete) => {}
_ => (),
};

while let Some(next) = insertion_stack.pop() {
Ok(while let Some(next) = insertion_stack.pop() {
match next {
PerformSplit(node) => {
// The root node was split, create a new root and increase height
Expand All @@ -68,13 +71,13 @@ impl InsertionStrategy for RStarInsertionStrategy {
match forced_insertion::<T, Params>(root, node_to_reinsert, target_height) {
InsertionResult::Split(node) => insertion_stack.push(PerformSplit(node)),
InsertionResult::Reinsert(_, _) => {
panic!("Unexpected reinsert. This is a bug in rstar.")
Err("Unexpected reinsert. This is a bug in rstar.")?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Actually, I think running cargo fmt --all -- --check and cargo clippy --all -- --deny warnings as part of the CI would be nice.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To give a bit more background about why this panic should never be hit: The algorithm will from time to time (when a node overflows) attempt to reinsert (remove and insert again) a point to improve the tree's internal structure. This is what InsertionResult::Reinsert(_,_) means - it tells the top level function (the beginning of the recursion) that some point should be reinserted.
Since inserting that point can lead to another reinsertion (and possibly even an endless loop), reinsertion is only attempted once. That's where forced_insertion differs - it will never allow re-inserting an element.

I think the best way to get rid of the panic would be to modify the return type of forced_insert to a new ForcedInsertionResult enum which doesn't contain a reinsert entry. Then this branch would not appear in the first place.

}
InsertionResult::Complete => {}
}
}
}
}
})
}
}

Expand Down Expand Up @@ -114,7 +117,7 @@ fn recursive_insert<T, Params>(
node: &mut ParentNode<T>,
t: RTreeNode<T>,
current_height: usize,
) -> InsertionResult<T>
) -> Result<InsertionResult<T>, Box<dyn error::Error>>
where
T: RTreeObject,
Params: RTreeParams,
Expand All @@ -125,24 +128,24 @@ where
if node.children.len() < expand_index {
// Force insertion into this node
node.children.push(t);
return resolve_overflow::<_, Params>(node, current_height);
return Ok(resolve_overflow::<_, Params>(node, current_height));
}

let expand = if let RTreeNode::Parent(ref mut follow) = node.children[expand_index] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line also good to compare to the panic invocation below. This indexing into node.children could panic and would do so for the same reason the explicitly visible panic would be triggered, i.e. because the internal function choose_subtree gave an incorrect result.

recursive_insert::<_, Params>(follow, t, current_height + 1)
} else {
panic!("This is a bug in rstar.")
return Err("Something has gone badly wrong while attempting to insert a value".into());
};

match expand {
InsertionResult::Split(child) => {
Ok(InsertionResult::Split(child)) => {
node.envelope.merge(&child.envelope());
node.children.push(child);
resolve_overflow::<_, Params>(node, current_height)
Ok(resolve_overflow::<_, Params>(node, current_height))
}
InsertionResult::Reinsert(a, b) => {
Ok(InsertionResult::Reinsert(a, b)) => {
node.envelope = envelope_for_children(&node.children);
InsertionResult::Reinsert(a, b)
Ok(InsertionResult::Reinsert(a, b))
}
other => other,
}
Expand Down
4 changes: 3 additions & 1 deletion rstar/src/params.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error;

use crate::algorithm::rstar::RStarInsertionStrategy;
use crate::{Envelope, Point, RTree, RTreeObject};

Expand Down Expand Up @@ -79,7 +81,7 @@ impl RTreeParams for DefaultParams {
/// This trait is not meant to be implemented by the user.
pub trait InsertionStrategy {
#[doc(hidden)]
fn insert<T, Params>(tree: &mut RTree<T, Params>, t: T)
fn insert<T, Params>(tree: &mut RTree<T, Params>, t: T) -> Result<(), Box<dyn error::Error>>
where
Params: RTreeParams,
T: RTreeObject;
Expand Down
13 changes: 8 additions & 5 deletions rstar/src/rtree.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error;

use crate::algorithm::bulk_load;
use crate::algorithm::intersection_iterator::IntersectionIterator;
use crate::algorithm::iterators::*;
Expand Down Expand Up @@ -742,9 +744,10 @@ where
/// This method runs in `O(log(n))`.
/// The [r-tree documentation](RTree) contains more information about
/// r-tree performance.
pub fn insert(&mut self, t: T) {
Params::DefaultInsertionStrategy::insert(self, t);
pub fn insert(&mut self, t: T) -> Result<(), Box<dyn error::Error>> {
Params::DefaultInsertionStrategy::insert(self, t)?;
self.size += 1;
Ok(())
}
}

Expand Down Expand Up @@ -807,7 +810,7 @@ mod test {
#[test]
fn test_insert_single() {
let mut tree: RTree<_> = RTree::new();
tree.insert([0.02f32, 0.4f32]);
tree.insert([0.02f32, 0.4f32]).unwrap();
assert_eq!(tree.size(), 1);
assert!(tree.contains(&[0.02, 0.4]));
assert!(!tree.contains(&[0.3, 0.2]));
Expand All @@ -819,7 +822,7 @@ mod test {
let points = create_random_points(NUM_POINTS, SEED_1);
let mut tree = RTree::new();
for p in &points {
tree.insert(*p);
tree.insert(*p).unwrap();
tree.root.sanity_check::<DefaultParams>(true);
}
assert_eq!(tree.size(), NUM_POINTS);
Expand Down Expand Up @@ -920,7 +923,7 @@ mod test {
for node in nodes {
// Bulk loading will create nodes larger than Params::MAX_SIZE,
// which is intentional and not harmful.
tree.insert(node);
tree.insert(node).unwrap();
tree.root().sanity_check::<DefaultParams>(false);
}
}
Expand Down