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

Avoid bevy_reflect::List::iter wrapping in release mode #13271

Merged
merged 4 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 30 additions & 1 deletion crates/bevy_reflect/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ impl<'a> Iterator for ArrayIter<'a> {
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let value = self.array.get(self.index);
self.index += 1;
self.index += value.is_some() as usize;
Copy link
Member

Choose a reason for hiding this comment

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

Awesome! Thanks for adding these as well!

Would also be able to duplicate that test for each of these changes as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldnt test the overflow for most of them because I couldnt create an iterator with enough elements.
But the logic of not incrementing past the first None I can test, so I've added tests for that

value
}

Expand Down Expand Up @@ -467,3 +467,32 @@ pub fn array_debug(dyn_array: &dyn Array, f: &mut std::fmt::Formatter<'_>) -> st
}
debug.finish()
}
#[cfg(test)]
mod tests {
use crate::{Reflect, ReflectRef};
#[test]
fn next_index_increment() {
const SIZE: usize = if cfg!(debug_assertions) {
4
} else {
// If compiled in release mode, verify we dont overflow
usize::MAX
};

let b = Box::new([(); SIZE]).into_reflect();

let ReflectRef::Array(array) = b.reflect_ref() else {
panic!("Not an array...");
};

let mut iter = array.iter();
iter.index = SIZE - 1;
assert!(iter.next().is_some());

// When None we should no longer increase index
assert!(iter.next().is_none());
assert!(iter.index == SIZE);
assert!(iter.next().is_none());
assert!(iter.index == SIZE);
}
}
57 changes: 56 additions & 1 deletion crates/bevy_reflect/src/enums/enum_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ impl<'a> Iterator for VariantFieldIter<'a> {
Some(VariantField::Struct(name, self.container.field(name)?))
}
};
self.index += 1;
self.index += value.is_some() as usize;
value
}

Expand Down Expand Up @@ -312,3 +312,58 @@ impl<'a> VariantField<'a> {
}
}
}

// Tests that need access to internal fields have to go here rather than in mod.rs
#[cfg(test)]
mod tests {
use crate as bevy_reflect;
use crate::*;

#[derive(Reflect, Debug, PartialEq)]
enum MyEnum {
A,
B(usize, i32),
C { foo: f32, bar: bool },
}
#[test]
fn next_index_increment() {
// unit enums always return none, so index should stay at 0
let unit_enum = MyEnum::A;
let mut iter = unit_enum.iter_fields();
let size = iter.len();
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
// tuple enums we iter over each value (unnamed fields), stop after that
let tuple_enum = MyEnum::B(0, 1);
let mut iter = tuple_enum.iter_fields();
let size = iter.len();
for _ in 0..2 {
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);
}
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}

// struct enums, we iterate over each field in the struct
let struct_enum = MyEnum::C {
foo: 0.,
bar: false,
};
let mut iter = struct_enum.iter_fields();
let size = iter.len();
for _ in 0..2 {
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);
}
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
}
}
28 changes: 27 additions & 1 deletion crates/bevy_reflect/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ impl<'a> Iterator for ListIter<'a> {
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let value = self.list.get(self.index);
self.index += 1;
self.index += value.is_some() as usize;
rmsthebest marked this conversation as resolved.
Show resolved Hide resolved
rmsthebest marked this conversation as resolved.
Show resolved Hide resolved
value
}

Expand Down Expand Up @@ -508,6 +508,7 @@ pub fn list_debug(dyn_list: &dyn List, f: &mut Formatter<'_>) -> std::fmt::Resul
#[cfg(test)]
mod tests {
use super::DynamicList;
use crate::{Reflect, ReflectRef};
use std::assert_eq;

#[test]
Expand All @@ -522,4 +523,29 @@ mod tests {
assert_eq!(index, value);
}
}

#[test]
rmsthebest marked this conversation as resolved.
Show resolved Hide resolved
fn next_index_increment() {
const SIZE: usize = if cfg!(debug_assertions) {
4
} else {
// If compiled in release mode, verify we dont overflow
usize::MAX
};
let b = Box::new(vec![(); SIZE]).into_reflect();

let ReflectRef::List(list) = b.reflect_ref() else {
panic!("Not a list...");
};

let mut iter = list.iter();
iter.index = SIZE - 1;
assert!(iter.next().is_some());

// When None we should no longer increase index
assert!(iter.next().is_none());
assert!(iter.index == SIZE);
assert!(iter.next().is_none());
assert!(iter.index == SIZE);
}
}
25 changes: 24 additions & 1 deletion crates/bevy_reflect/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ impl<'a> Iterator for MapIter<'a> {

fn next(&mut self) -> Option<Self::Item> {
let value = self.map.get_at(self.index);
self.index += 1;
self.index += value.is_some() as usize;
value
}

Expand Down Expand Up @@ -594,4 +594,27 @@ mod tests {

assert!(map.get_at(2).is_none());
}

#[test]
fn next_index_increment() {
let values = ["first", "last"];
let mut map = DynamicMap::default();
map.insert(0usize, values[0]);
map.insert(1usize, values[1]);

let mut iter = map.iter();
let size = iter.len();

for _ in 0..2 {
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);
}

// When None we should no longer increase index
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
}
}
30 changes: 29 additions & 1 deletion crates/bevy_reflect/src/struct_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl<'a> Iterator for FieldIter<'a> {

fn next(&mut self) -> Option<Self::Item> {
let value = self.struct_val.field_at(self.index);
self.index += 1;
self.index += value.is_some() as usize;
value
}

Expand Down Expand Up @@ -557,3 +557,31 @@ pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut Formatter<'_>) -> std::fmt:
}
debug.finish()
}

#[cfg(test)]
mod tests {
use crate as bevy_reflect;
use crate::*;
#[derive(Reflect, Default)]
struct MyStruct {
a: (),
b: (),
c: (),
}
#[test]
fn next_index_increment() {
let my_struct = MyStruct::default();
let mut iter = my_struct.iter_fields();
iter.index = iter.len() - 1;
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);

// When None we should no longer increase index
let prev_index = iter.index;
assert!(iter.next().is_none());
assert_eq!(prev_index, iter.index);
assert!(iter.next().is_none());
assert_eq!(prev_index, iter.index);
}
}
23 changes: 22 additions & 1 deletion crates/bevy_reflect/src/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'a> Iterator for TupleFieldIter<'a> {

fn next(&mut self) -> Option<Self::Item> {
let value = self.tuple.field(self.index);
self.index += 1;
self.index += value.is_some() as usize;
value
}

Expand Down Expand Up @@ -683,3 +683,24 @@ macro_rules! impl_type_path_tuple {
}

all_tuples!(impl_type_path_tuple, 0, 12, P);

#[cfg(test)]
mod tests {
use super::Tuple;

#[test]
fn next_index_increment() {
let mut iter = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11).iter_fields();
let size = iter.len();
iter.index = size - 1;
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);

// When None we should no longer increase index
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
}
25 changes: 24 additions & 1 deletion crates/bevy_reflect/src/tuple_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl<'a> Iterator for TupleStructFieldIter<'a> {

fn next(&mut self) -> Option<Self::Item> {
let value = self.tuple_struct.field(self.index);
self.index += 1;
self.index += value.is_some() as usize;
value
}

Expand Down Expand Up @@ -469,3 +469,26 @@ pub fn tuple_struct_debug(
}
debug.finish()
}

#[cfg(test)]
mod tests {
use crate as bevy_reflect;
use crate::*;
#[derive(Reflect)]
struct Ts(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8);
#[test]
fn next_index_increment() {
let mut iter = Ts(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11).iter_fields();
let size = iter.len();
iter.index = size - 1;
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);

// When None we should no longer increase index
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
}