Skip to content

Commit e761a17

Browse files
Use <[T]>::array_windows for exact size windows (#24146)
# Objective - Reduce panicking code and bounds checks. ## Solution - Replace `<[T]>::windows` with `<[T]>::array_windows` when the window size is constant. - Update MSRV in affected crates (and fix the new warnings). ## Testing - Ran `cargo test -p bevy_mesh -p bevy_gizmos -p bevy_math -p bevy_input_focus` ## MSRV The main `bevy` crate has a MSRV of 1.95; I've increased the MSRV to 1.94 (when `array_windows` was stabilised) for `bevy_math` and `bevy_input_focus` in this PR. If it's too early to increase the MSRV, I can drop the changes to these crates in this PR or convert this PR to draft.
1 parent 35b9a21 commit e761a17

10 files changed

Lines changed: 72 additions & 96 deletions

File tree

crates/bevy_gizmos/src/primitives/dim3.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -670,12 +670,9 @@ where
670670
});
671671

672672
// base circle
673-
circle_coords
674-
.windows(2)
675-
.map(|win| (win[0], win[1]))
676-
.for_each(|(start, end)| {
677-
self.gizmos.line(start, end, self.color);
678-
});
673+
circle_coords.array_windows().for_each(|&[start, end]| {
674+
self.gizmos.line(start, end, self.color);
675+
});
679676
}
680677
}
681678

@@ -764,9 +761,9 @@ where
764761
.collect::<Vec<_>>()
765762
});
766763

767-
let upper_lines = upper_points.windows(2).map(|win| (win[0], win[1]));
768-
let lower_lines = lower_points.windows(2).map(|win| (win[0], win[1]));
769-
upper_lines.chain(lower_lines).for_each(|(start, end)| {
764+
let upper_lines = upper_points.array_windows();
765+
let lower_lines = lower_points.array_windows();
766+
upper_lines.chain(lower_lines).for_each(|&[start, end]| {
770767
self.gizmos.line(start, end, self.color);
771768
});
772769

@@ -875,8 +872,8 @@ where
875872

876873
[&inner, &outer, &top, &bottom]
877874
.iter()
878-
.flat_map(|points| points.windows(2).map(|win| (win[0], win[1])))
879-
.for_each(|(start, end)| {
875+
.flat_map(|points| points.array_windows())
876+
.for_each(|&[start, end]| {
880877
self.gizmos.line(start, end, self.color);
881878
});
882879

crates/bevy_input_focus/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ homepage = "https://bevy.org"
77
repository = "https://github.com/bevyengine/bevy"
88
license = "MIT OR Apache-2.0"
99
keywords = ["bevy"]
10-
rust-version = "1.85.0"
10+
rust-version = "1.94.0"
1111

1212
[features]
1313
default = [

crates/bevy_input_focus/src/directional_navigation.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ impl DirectionalNavigationMap {
357357
///
358358
/// Unlike [`add_looping_edges`](Self::add_looping_edges), this method does not loop back to the first entity.
359359
pub fn add_edges(&mut self, entities: &[Entity], direction: CompassOctant) {
360-
for pair in entities.windows(2) {
361-
self.add_symmetrical_edge(pair[0], pair[1], direction);
360+
for &[a, b] in entities.array_windows() {
361+
self.add_symmetrical_edge(a, b, direction);
362362
}
363363
}
364364

@@ -367,10 +367,10 @@ impl DirectionalNavigationMap {
367367
/// This is useful for creating a circular navigation path between a set of entities, such as a menu.
368368
pub fn add_looping_edges(&mut self, entities: &[Entity], direction: CompassOctant) {
369369
self.add_edges(entities, direction);
370-
if let Some((first_entity, rest)) = entities.split_first() {
371-
if let Some(last_entity) = rest.last() {
372-
self.add_symmetrical_edge(*last_entity, *first_entity, direction);
373-
}
370+
if let Some((first_entity, rest)) = entities.split_first()
371+
&& let Some(last_entity) = rest.last()
372+
{
373+
self.add_symmetrical_edge(*last_entity, *first_entity, direction);
374374
}
375375
}
376376

crates/bevy_input_focus/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,10 +492,10 @@ mod tests {
492492
event: On<FocusedInput<KeyboardInput>>,
493493
mut query: Query<&mut GatherKeyboardEvents>,
494494
) {
495-
if let Ok(mut gather) = query.get_mut(event.focused_entity) {
496-
if let Key::Character(c) = &event.input.logical_key {
497-
gather.0.push_str(c.as_str());
498-
}
495+
if let Ok(mut gather) = query.get_mut(event.focused_entity)
496+
&& let Key::Character(c) = &event.input.logical_key
497+
{
498+
gather.0.push_str(c.as_str());
499499
}
500500
}
501501

crates/bevy_input_focus/src/navigator.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,10 @@ fn score_candidate(
112112
let distance = bevy_math::ops::sqrt(dx * dx + dy * dy);
113113

114114
// Check max distance
115-
if let Some(max_dist) = config.max_search_distance {
116-
if distance > max_dist {
117-
return f32::INFINITY;
118-
}
115+
if let Some(max_dist) = config.max_search_distance
116+
&& distance > max_dist
117+
{
118+
return f32::INFINITY;
119119
}
120120

121121
// Calculate alignment score using center-to-center direction

crates/bevy_input_focus/src/tab_navigation.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,10 @@ impl TabNavigation<'_, '_> {
323323
tab_group_idx: usize,
324324
) {
325325
if let Ok((entity, tabindex, children)) = self.tabindex_query.get(parent) {
326-
if let Some(tabindex) = tabindex {
327-
if tabindex.0 >= 0 {
328-
out.push((entity, *tabindex, tab_group_idx));
329-
}
326+
if let Some(tabindex) = tabindex
327+
&& tabindex.0 >= 0
328+
{
329+
out.push((entity, *tabindex, tab_group_idx));
330330
}
331331
if let Some(children) = children {
332332
for child in children.iter() {
@@ -336,11 +336,11 @@ impl TabNavigation<'_, '_> {
336336
}
337337
}
338338
}
339-
} else if let Ok((_, tabgroup, children)) = self.tabgroup_query.get(parent) {
340-
if !tabgroup.modal {
341-
for child in children.iter() {
342-
self.gather_focusable(out, *child, tab_group_idx);
343-
}
339+
} else if let Ok((_, tabgroup, children)) = self.tabgroup_query.get(parent)
340+
&& !tabgroup.modal
341+
{
342+
for child in children.iter() {
343+
self.gather_focusable(out, *child, tab_group_idx);
344344
}
345345
}
346346
}

crates/bevy_math/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ homepage = "https://bevy.org"
77
repository = "https://github.com/bevyengine/bevy"
88
license = "MIT OR Apache-2.0"
99
keywords = ["bevy"]
10-
rust-version = "1.85.0"
10+
rust-version = "1.94.0"
1111

1212
[dependencies]
1313
glam = { version = "0.32.0", default-features = false, features = ["bytemuck"] }

crates/bevy_math/src/cubic_splines/mod.rs

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,8 @@ impl<P: VectorSpace<Scalar = f32>> CubicGenerator<P> for CubicHermite<P> {
183183
fn to_curve(&self) -> Result<CubicCurve<P>, Self::Error> {
184184
let segments = self
185185
.control_points
186-
.windows(2)
187-
.map(|p| {
188-
let (p0, v0, p1, v1) = (p[0].0, p[0].1, p[1].0, p[1].1);
186+
.array_windows()
187+
.map(|&[(p0, v0), (p1, v1)]| {
189188
CubicSegment::coefficients([p0, v0, p1, v1], self.char_matrix())
190189
})
191190
.collect_vec();
@@ -478,8 +477,8 @@ impl<P: VectorSpace<Scalar = f32>> CubicGenerator<P> for CubicBSpline<P> {
478477
fn to_curve(&self) -> Result<CubicCurve<P>, Self::Error> {
479478
let segments = self
480479
.control_points
481-
.windows(4)
482-
.map(|p| CubicSegment::coefficients([p[0], p[1], p[2], p[3]], self.char_matrix()))
480+
.array_windows()
481+
.map(|&p| CubicSegment::coefficients(p, self.char_matrix()))
483482
.collect_vec();
484483

485484
if segments.is_empty() {
@@ -665,12 +664,12 @@ impl<P: VectorSpace<Scalar = f32>> CubicNurbs<P> {
665664

666665
// Ensure the knots are non-descending (previous element is less than or equal
667666
// to the next)
668-
if knots.windows(2).any(|win| win[0] > win[1]) {
667+
if knots.array_windows().any(|[a, b]| a > b) {
669668
return Err(CubicNurbsError::DescendingKnots);
670669
}
671670

672671
// Ensure the knots are non-constant
673-
if knots.windows(2).all(|win| win[0] == win[1]) {
672+
if knots.array_windows().all(|[a, b]| a == b) {
674673
return Err(CubicNurbsError::ConstantKnots);
675674
}
676675

@@ -788,24 +787,18 @@ impl<P: VectorSpace<Scalar = f32>> RationalGenerator<P> for CubicNurbs<P> {
788787
fn to_curve(&self) -> Result<RationalCurve<P>, Self::Error> {
789788
let segments = self
790789
.control_points
791-
.windows(4)
792-
.zip(self.weights.windows(4))
793-
.zip(self.knots.windows(8))
790+
.array_windows()
791+
.zip(self.weights.array_windows())
792+
.zip(self.knots.array_windows())
794793
.filter(|(_, knots)| knots[4] - knots[3] > 0.0)
795-
.map(|((points, weights), knots)| {
794+
.map(|((&points, &weights), knots)| {
796795
// This is curve segment i. It uses control points P_i, P_i+2, P_i+2 and P_i+3,
797796
// It is associated with knot span i+3 (which is the interval between knots i+3
798797
// and i+4) and its characteristic matrix uses knots i+1 through i+6 (because
799798
// those define the two knot spans on either side).
800799
let span = knots[4] - knots[3];
801-
let coefficient_knots = knots.try_into().expect("Knot windows are of length 6");
802-
let matrix = Self::generate_matrix(coefficient_knots);
803-
RationalSegment::coefficients(
804-
points.try_into().expect("Point windows are of length 4"),
805-
weights.try_into().expect("Weight windows are of length 4"),
806-
span,
807-
matrix,
808-
)
800+
let matrix = Self::generate_matrix(knots);
801+
RationalSegment::coefficients(points, weights, span, matrix)
809802
})
810803
.collect_vec();
811804
if segments.is_empty() {
@@ -865,13 +858,9 @@ impl<P: VectorSpace> CubicGenerator<P> for LinearSpline<P> {
865858
fn to_curve(&self) -> Result<CubicCurve<P>, Self::Error> {
866859
let segments = self
867860
.points
868-
.windows(2)
869-
.map(|points| {
870-
let a = points[0];
871-
let b = points[1];
872-
CubicSegment {
873-
coeff: [a, b - a, P::default(), P::default()],
874-
}
861+
.array_windows()
862+
.map(|&[a, b]| CubicSegment {
863+
coeff: [a, b - a, P::default(), P::default()],
875864
})
876865
.collect_vec();
877866

crates/bevy_math/src/curve/cores.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ impl<T> ChunkedUnevenCore<T> {
589589
});
590590
}
591591

592-
if values.len() % times.len() != 0 {
592+
if !values.len().is_multiple_of(times.len()) {
593593
return Err(ChunkedUnevenCoreError::NonDivisibleLengths {
594594
values_len: values.len(),
595595
times_len: times.len(),

crates/bevy_mesh/src/mesh.rs

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,6 +2205,18 @@ impl Mesh {
22052205
/// [primitive topology]: PrimitiveTopology
22062206
/// [triangles]: Triangle3d
22072207
pub fn triangles(&self) -> Result<impl Iterator<Item = Triangle3d> + '_, MeshTrianglesError> {
2208+
fn indices_to_triangle<T: TryInto<usize> + Copy>(
2209+
vertices: &[[f32; 3]],
2210+
indices: &[T; 3],
2211+
) -> Option<Triangle3d> {
2212+
let vert0 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);
2213+
let vert1 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);
2214+
let vert2 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);
2215+
Some(Triangle3d {
2216+
vertices: [vert0, vert1, vert2],
2217+
})
2218+
}
2219+
22082220
let position_data = self.try_attribute(Mesh::ATTRIBUTE_POSITION)?;
22092221

22102222
let Some(vertices) = position_data.as_float3() else {
@@ -2219,75 +2231,53 @@ impl Mesh {
22192231
// This implicitly truncates the indices to a multiple of 3.
22202232
let iterator = match indices {
22212233
Indices::U16(vec) => FourIterators::First(
2222-
vec.as_chunks::<3>()
2234+
vec.as_chunks()
22232235
.0
22242236
.iter()
22252237
.flat_map(|indices| indices_to_triangle(vertices, indices)),
22262238
),
22272239
Indices::U32(vec) => FourIterators::Second(
2228-
vec.as_chunks::<3>()
2240+
vec.as_chunks()
22292241
.0
22302242
.iter()
22312243
.flat_map(|indices| indices_to_triangle(vertices, indices)),
22322244
),
22332245
};
22342246

2235-
return Ok(iterator);
2247+
Ok(iterator)
22362248
}
2237-
22382249
PrimitiveTopology::TriangleStrip => {
22392250
// When indices reference out-of-bounds vertex data, the triangle is omitted.
22402251
// If there aren't enough indices to make a triangle, then an empty vector will be
22412252
// returned.
22422253
let iterator = match indices {
22432254
Indices::U16(vec) => {
2244-
FourIterators::Third(vec.as_slice().windows(3).enumerate().flat_map(
2245-
move |(i, indices)| {
2255+
FourIterators::Third(vec.array_windows().enumerate().flat_map(
2256+
|(i, indices @ &[idx0, idx1, idx2])| {
22462257
if i % 2 == 0 {
22472258
indices_to_triangle(vertices, indices)
22482259
} else {
2249-
indices_to_triangle(
2250-
vertices,
2251-
&[indices[1], indices[0], indices[2]],
2252-
)
2260+
indices_to_triangle(vertices, &[idx1, idx0, idx2])
22532261
}
22542262
},
22552263
))
22562264
}
22572265
Indices::U32(vec) => {
2258-
FourIterators::Fourth(vec.as_slice().windows(3).enumerate().flat_map(
2259-
move |(i, indices)| {
2266+
FourIterators::Fourth(vec.array_windows().enumerate().flat_map(
2267+
|(i, indices @ &[idx0, idx1, idx2])| {
22602268
if i % 2 == 0 {
22612269
indices_to_triangle(vertices, indices)
22622270
} else {
2263-
indices_to_triangle(
2264-
vertices,
2265-
&[indices[1], indices[0], indices[2]],
2266-
)
2271+
indices_to_triangle(vertices, &[idx1, idx0, idx2])
22672272
}
22682273
},
22692274
))
22702275
}
22712276
};
22722277

2273-
return Ok(iterator);
2278+
Ok(iterator)
22742279
}
2275-
2276-
_ => {
2277-
return Err(MeshTrianglesError::WrongTopology);
2278-
}
2279-
};
2280-
2281-
fn indices_to_triangle<T: TryInto<usize> + Copy>(
2282-
vertices: &[[f32; 3]],
2283-
indices: &[T],
2284-
) -> Option<Triangle3d> {
2285-
let vert0: Vec3 = Vec3::from(*vertices.get(indices[0].try_into().ok()?)?);
2286-
let vert1: Vec3 = Vec3::from(*vertices.get(indices[1].try_into().ok()?)?);
2287-
let vert2: Vec3 = Vec3::from(*vertices.get(indices[2].try_into().ok()?)?);
2288-
Some(Triangle3d {
2289-
vertices: [vert0, vert1, vert2],
2290-
})
2280+
_ => Err(MeshTrianglesError::WrongTopology),
22912281
}
22922282
}
22932283

0 commit comments

Comments
 (0)