Skip to content

Commit a526aa4

Browse files
committed
Removed unsafe optimization that is not strictly valid
- Update version to 0.7.1 - Replaced get_at_index and set_at_index macros with inlined functions - Simplified function signature to use slices rather than Vecs where possible
1 parent 0899729 commit a526aa4

File tree

3 files changed

+63
-63
lines changed

3 files changed

+63
-63
lines changed

CHANGELOG.md

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

33
All notable changes to the static_aabb2d_index crate will be documented in this file.
44

5+
## 0.7.1 - 2023-02-22
6+
7+
### Changed
8+
9+
- Removed unsafe optimization involving uninitialized memory, the code did not strictly uphold the
10+
invariants required of a `Vec` at all times which could lead to undefined behavior. To properly
11+
perform this optimization will require more pointer manipulation spread across the code or new
12+
APIs from the Rust standard library. Index bounds checking is still toggled by the
13+
`unsafe_optimizations` feature.
14+
- INTERNAL: replaced `get_at_index` and `set_at_index` macros with simple inlined functions and
15+
simplified some function signatures to use slices rather than `Vec`.
16+
517
## 0.7.0 - 2023-02-18
618

719
### Added ⭐

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ keywords = ["algorithm", "2d", "computational", "geometry", "spatial"]
77
license = "MIT OR Apache-2.0"
88
name = "static_aabb2d_index"
99
repository = "https://github.com/jbuckmccready/static_aabb2d_index"
10-
version = "0.7.0"
10+
version = "0.7.1"
1111

1212
[features]
1313
# allows for some selective use of unsafe for performance gains

src/static_aabb2d_index.rs

Lines changed: 50 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -126,33 +126,31 @@ where
126126
indices: Vec<usize>,
127127
}
128128

129-
// get_at_index! and set_at_index! macros to toggle bounds checking at compile time
129+
// get_at_index and set_at_index helper functions to toggle bounds checking at compile time
130130
#[cfg(not(feature = "unsafe_optimizations"))]
131-
macro_rules! get_at_index {
132-
($container:expr, $index:expr) => {
133-
&$container[$index]
134-
};
131+
#[inline(always)]
132+
fn get_at_index<T>(container: &[T], index: usize) -> &T {
133+
&container[index]
135134
}
136135

137136
#[cfg(feature = "unsafe_optimizations")]
138-
macro_rules! get_at_index {
139-
($container:expr, $index:expr) => {
140-
unsafe { $container.get_unchecked($index) }
141-
};
137+
#[inline(always)]
138+
fn get_at_index<T>(container: &[T], index: usize) -> &T {
139+
unsafe { container.get_unchecked(index) }
142140
}
143141

144142
#[cfg(not(feature = "unsafe_optimizations"))]
145-
macro_rules! set_at_index {
146-
($container:expr, $index:expr, $value:expr) => {
147-
$container[$index] = $value
148-
};
143+
#[inline(always)]
144+
fn set_at_index<T>(container: &mut [T], index: usize, value: T) {
145+
container[index] = value;
149146
}
150147

151148
#[cfg(feature = "unsafe_optimizations")]
152-
macro_rules! set_at_index {
153-
($container:expr, $index:expr, $value:expr) => {
154-
unsafe { *$container.get_unchecked_mut($index) = $value }
155-
};
149+
#[inline(always)]
150+
fn set_at_index<T>(container: &mut [T], index: usize, value: T) {
151+
unsafe {
152+
*container.get_unchecked_mut(index) = value;
153+
}
156154
}
157155

158156
impl<T> StaticAABB2DIndexBuilder<T>
@@ -193,21 +191,7 @@ where
193191
}
194192
}
195193

196-
// unsafe alternative for performance (uninitialized memory rather than initialize to zero)
197-
// since it is all initialized later before use
198-
#[cfg(feature = "unsafe_optimizations")]
199-
let init_boxes = || {
200-
let mut boxes = Vec::with_capacity(num_nodes);
201-
unsafe {
202-
boxes.set_len(num_nodes);
203-
}
204-
boxes
205-
};
206-
207-
#[cfg(not(feature = "unsafe_optimizations"))]
208-
let init_boxes = || vec![AABB::default(); num_nodes];
209-
210-
let boxes = init_boxes();
194+
let boxes = vec![AABB::default(); num_nodes];
211195

212196
StaticAABB2DIndexBuilder {
213197
min_x: T::max_value(),
@@ -259,7 +243,11 @@ where
259243
debug_assert!(min_x <= max_x);
260244
debug_assert!(min_y <= max_y);
261245

262-
set_at_index!(self.boxes, self.pos, AABB::new(min_x, min_y, max_x, max_y));
246+
set_at_index(
247+
&mut self.boxes,
248+
self.pos,
249+
AABB::new(min_x, min_y, max_x, max_y),
250+
);
263251
self.pos += 1;
264252

265253
self.min_x = T::min(self.min_x, min_x);
@@ -291,12 +279,12 @@ where
291279
// if number of items is less than node size then skip sorting since each node of boxes must
292280
// be fully scanned regardless and there is only one node
293281
if self.num_items <= self.node_size {
294-
set_at_index!(self.indices, self.pos, 0);
282+
set_at_index(&mut self.indices, self.pos, 0);
295283
// fill root box with total extents
296-
set_at_index!(
297-
self.boxes,
284+
set_at_index(
285+
&mut self.boxes,
298286
self.pos,
299-
AABB::new(self.min_x, self.min_y, self.max_x, self.max_y)
287+
AABB::new(self.min_x, self.min_y, self.max_x, self.max_y),
300288
);
301289
return Ok(StaticAABB2DIndex {
302290
min_x: self.min_x,
@@ -354,7 +342,7 @@ where
354342
// generate nodes at each tree level, bottom-up
355343
let mut pos = 0;
356344
for i in 0..self.level_bounds.len() - 1 {
357-
let end = *get_at_index!(self.level_bounds, i);
345+
let end = *get_at_index(&self.level_bounds, i);
358346

359347
// generate a parent node for each block of consecutive node_size nodes
360348
while pos < end {
@@ -367,7 +355,7 @@ where
367355
// calculate bounding box for the new node
368356
let mut j = 0;
369357
while j < self.node_size && pos < end {
370-
let aabb = get_at_index!(self.boxes, pos);
358+
let aabb = get_at_index(&self.boxes, pos);
371359
pos += 1;
372360
node_min_x = T::min(node_min_x, aabb.min_x);
373361
node_min_y = T::min(node_min_y, aabb.min_y);
@@ -377,11 +365,11 @@ where
377365
}
378366

379367
// add the new node to the tree
380-
set_at_index!(self.indices, self.pos, node_index);
381-
set_at_index!(
382-
self.boxes,
368+
set_at_index(&mut self.indices, self.pos, node_index);
369+
set_at_index(
370+
&mut self.boxes,
383371
self.pos,
384-
AABB::new(node_min_x, node_min_y, node_max_x, node_max_y)
372+
AABB::new(node_min_x, node_min_y, node_max_x, node_max_y),
385373
);
386374
self.pos += 1;
387375
}
@@ -468,9 +456,9 @@ pub fn hilbert_xy_to_index(x: u16, y: u16) -> u32 {
468456

469457
// modified quick sort that skips sorting boxes within the same node
470458
fn sort<T>(
471-
values: &mut Vec<u32>,
472-
boxes: &mut Vec<AABB<T>>,
473-
indices: &mut Vec<usize>,
459+
values: &mut [u32],
460+
boxes: &mut [AABB<T>],
461+
indices: &mut [usize],
474462
left: usize,
475463
right: usize,
476464
node_size: usize,
@@ -485,21 +473,21 @@ fn sort<T>(
485473
return;
486474
}
487475

488-
let pivot = *get_at_index!(values, (left + right) >> 1);
476+
let pivot = *get_at_index(values, (left + right) >> 1);
489477
let mut i = left.wrapping_sub(1);
490478
let mut j = right.wrapping_add(1);
491479

492480
loop {
493481
loop {
494482
i = i.wrapping_add(1);
495-
if *get_at_index!(values, i) >= pivot {
483+
if *get_at_index(values, i) >= pivot {
496484
break;
497485
}
498486
}
499487

500488
loop {
501489
j = j.wrapping_sub(1);
502-
if *get_at_index!(values, j) <= pivot {
490+
if *get_at_index(values, j) <= pivot {
503491
break;
504492
}
505493
}
@@ -558,7 +546,7 @@ where
558546
let level = aabb_index.level_bounds.len() - 1;
559547
let end = min(
560548
node_index + aabb_index.node_size,
561-
*get_at_index!(aabb_index.level_bounds, level),
549+
*get_at_index(&aabb_index.level_bounds, level),
562550
);
563551
QueryIterator {
564552
aabb_index,
@@ -589,13 +577,13 @@ where
589577
let current_pos = self.pos;
590578
self.pos += 1;
591579

592-
let aabb = get_at_index!(self.aabb_index.boxes, current_pos);
580+
let aabb = get_at_index(&self.aabb_index.boxes, current_pos);
593581
if !aabb.overlaps(self.min_x, self.min_y, self.max_x, self.max_y) {
594582
// no overlap
595583
continue;
596584
}
597585

598-
let index = *get_at_index!(self.aabb_index.indices, current_pos);
586+
let index = *get_at_index(&self.aabb_index.indices, current_pos);
599587
if self.node_index < self.aabb_index.num_items {
600588
return Some(index);
601589
} else {
@@ -610,7 +598,7 @@ where
610598
self.pos = self.node_index;
611599
self.end = min(
612600
self.node_index + self.aabb_index.node_size,
613-
*get_at_index!(self.aabb_index.level_bounds, self.level),
601+
*get_at_index(&self.aabb_index.level_bounds, self.level),
614602
);
615603
} else {
616604
return None;
@@ -664,7 +652,7 @@ where
664652
let level = aabb_index.level_bounds.len() - 1;
665653
let end = min(
666654
node_index + aabb_index.node_size,
667-
*get_at_index!(aabb_index.level_bounds, level),
655+
*get_at_index(&aabb_index.level_bounds, level),
668656
);
669657

670658
// ensure the stack is empty for use
@@ -699,13 +687,13 @@ where
699687
let current_pos = self.pos;
700688
self.pos += 1;
701689

702-
let aabb = get_at_index!(self.aabb_index.boxes, current_pos);
690+
let aabb = get_at_index(&self.aabb_index.boxes, current_pos);
703691
if !aabb.overlaps(self.min_x, self.min_y, self.max_x, self.max_y) {
704692
// no overlap
705693
continue;
706694
}
707695

708-
let index = *get_at_index!(self.aabb_index.indices, current_pos);
696+
let index = *get_at_index(&self.aabb_index.indices, current_pos);
709697
if self.node_index < self.aabb_index.num_items {
710698
return Some(index);
711699
} else {
@@ -720,7 +708,7 @@ where
720708
self.pos = self.node_index;
721709
self.end = min(
722710
self.node_index + self.aabb_index.node_size,
723-
*get_at_index!(self.aabb_index.level_bounds, self.level),
711+
*get_at_index(&self.aabb_index.level_bounds, self.level),
724712
);
725713
} else {
726714
return None;
@@ -994,17 +982,17 @@ where
994982
loop {
995983
let end = min(
996984
node_index + self.node_size,
997-
*get_at_index!(self.level_bounds, level),
985+
*get_at_index(&self.level_bounds, level),
998986
);
999987

1000988
for pos in node_index..end {
1001-
let aabb = get_at_index!(self.boxes, pos);
989+
let aabb = get_at_index(&self.boxes, pos);
1002990
if !aabb.overlaps(min_x, min_y, max_x, max_y) {
1003991
// no overlap
1004992
continue;
1005993
}
1006994

1007-
let index = *get_at_index!(self.indices, pos);
995+
let index = *get_at_index(&self.indices, pos);
1008996
if node_index < self.num_items {
1009997
try_control!(visitor.visit(index))
1010998
} else {
@@ -1091,11 +1079,11 @@ where
10911079

10921080
// add nodes to queue
10931081
for pos in node_index..end {
1094-
let aabb = get_at_index!(self.boxes, pos);
1082+
let aabb = get_at_index(&self.boxes, pos);
10951083
let dx = axis_dist(x, aabb.min_x, aabb.max_x);
10961084
let dy = axis_dist(y, aabb.min_y, aabb.max_y);
10971085
let dist = dx * dx + dy * dy;
1098-
let index = *get_at_index!(self.indices, pos);
1086+
let index = *get_at_index(&self.indices, pos);
10991087
let is_leaf_node = node_index < self.num_items;
11001088
queue.push(NeighborsState::new(index, is_leaf_node, dist));
11011089
}

0 commit comments

Comments
 (0)