Skip to content

Commit 8414312

Browse files
authored
Fix panic when building STR tree with certain number of items; fix hanging when building empty index (#129)
* Fix building STR tree with certain number of items; fix hanging when building empty index * Add changelog
1 parent 6c812f1 commit 8414312

File tree

6 files changed

+51
-6
lines changed

6 files changed

+51
-6
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
**This is the changelog for the core Rust library**. There's a [separate changelog](./python/CHANGELOG.md) for the Python bindings.
44

5+
## [0.3.1] - <RELEASE_DATE>
6+
7+
## Bug fixes
8+
9+
- Fix hanging when building RTree with 0 items by @kontinuation in https://github.com/kylebarron/geo-index/pull/129
10+
- Fix panic when building STR tree with certain number of items by @kontinuation in https://github.com/kylebarron/geo-index/pull/129
11+
512
## [0.3.0] - 2025-06-13
613

714
### Breaking

src/indices.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl MutableIndices<'_> {
7171
}
7272
}
7373

74+
#[allow(dead_code)]
7475
pub(crate) fn chunks_mut(&mut self, chunk_size: usize) -> Vec<MutableIndices> {
7576
match self {
7677
Self::U16(arr) => arr

src/rtree/builder.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ impl<N: IndexableNum> RTreeBuilder<N> {
142142

143143
let (boxes, mut indices) = split_data_borrow(&mut self.data, &self.metadata);
144144

145-
if self.metadata.num_items() == 1 {
146-
// Only one item, we don't even have a root node to fill
145+
if self.metadata.num_items() <= 1 {
146+
// Empty or only one item, we don't even have a root node to fill
147147
return RTree {
148148
buffer: self.data,
149149
metadata: self.metadata,
@@ -253,7 +253,7 @@ fn split_data_borrow<'a, N: IndexableNum>(
253253

254254
#[cfg(test)]
255255
mod test {
256-
use crate::rtree::sort::HilbertSort;
256+
use crate::rtree::sort::{HilbertSort, STRSort};
257257
use crate::rtree::RTreeIndex;
258258

259259
use super::*;
@@ -266,4 +266,37 @@ mod test {
266266
let result = tree.search(0., 0., 0., 0.);
267267
assert_eq!(result, vec![0]);
268268
}
269+
270+
#[test]
271+
fn build_str_index_with_various_items() {
272+
let num_items_arr = [0, 1, 4, 8, 16, 20, 40, 80];
273+
for num_items in num_items_arr {
274+
build_index_with_various_items::<STRSort>(num_items);
275+
}
276+
}
277+
278+
#[test]
279+
fn build_hilbert_index_with_various_items() {
280+
let num_items_arr = [0, 1, 4, 8, 16, 20, 40, 80];
281+
for num_items in num_items_arr {
282+
build_index_with_various_items::<HilbertSort>(num_items);
283+
}
284+
}
285+
286+
fn build_index_with_various_items<S: Sort<f64>>(num_items: u32) {
287+
let mut builder = RTreeBuilder::<f64>::new(num_items);
288+
for i in 0..num_items {
289+
builder.add(i as f64, i as f64, i as f64, i as f64);
290+
}
291+
let tree = builder.finish::<S>();
292+
assert_eq!(tree.num_items(), num_items);
293+
if num_items == 0 {
294+
assert!(tree.search(0., 0., 0., 0.).is_empty());
295+
} else {
296+
for i in 0..num_items {
297+
let results = tree.search(i as f64, i as f64, i as f64, i as f64);
298+
assert_eq!(results, vec![i]);
299+
}
300+
}
301+
}
269302
}

src/rtree/sort/str.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ impl<N: IndexableNum> Sort<N> for STRSort {
4747

4848
let num_leaf_nodes = (params.num_items as f64 / params.node_size as f64).ceil();
4949
let num_vertical_slices = num_leaf_nodes.sqrt().ceil() as usize;
50-
let num_items_per_slice = num_vertical_slices * params.node_size;
50+
let num_items_per_slice =
51+
(params.num_items as f64 / num_vertical_slices as f64).ceil() as usize;
5152

5253
#[cfg(feature = "rayon")]
5354
{

src/rtree/trait.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,11 @@ pub trait RTreeIndex<N: IndexableNum>: Sized {
7474
fn search(&self, min_x: N, min_y: N, max_x: N, max_y: N) -> Vec<u32> {
7575
let boxes = self.boxes();
7676
let indices = self.indices();
77+
if boxes.is_empty() {
78+
return vec![];
79+
}
7780

78-
let mut outer_node_index = Some(boxes.len() - 4);
81+
let mut outer_node_index = boxes.len().checked_sub(4);
7982

8083
let mut queue = vec![];
8184
let mut results = vec![];

src/rtree/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub(crate) fn compute_num_nodes(num_items: u32, node_size: u16) -> (usize, Vec<u
1212
let mut n = num_items;
1313
let mut num_nodes = n;
1414
let mut level_bounds = vec![n * 4];
15-
while n != 1 {
15+
while n > 1 {
1616
n = (n as f64 / node_size as f64).ceil() as usize;
1717
num_nodes += n;
1818
level_bounds.push(num_nodes * 4);

0 commit comments

Comments
 (0)