Skip to content

Commit ee4a84b

Browse files
committed
Fix size_hint for partially consumed QueryIter
Instead of returning the total count of elements in the `QueryIter` in `size_hint`, we return the count of remaining elements in it. This Fixes #5149 (although, if #5148 is merged, #5149 will re-open) - #5149 - #5148
1 parent 5b5013d commit ee4a84b

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

crates/bevy_ecs/src/query/iter.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,7 @@ where
6262
}
6363

6464
fn size_hint(&self) -> (usize, Option<usize>) {
65-
let max_size = self
66-
.query_state
67-
.matched_archetype_ids
68-
.iter()
69-
.map(|id| self.archetypes[*id].len())
70-
.sum();
71-
65+
let max_size = self.cursor.remaining(self.tables, self.archetypes);
7266
let archetype_query = F::Fetch::IS_ARCHETYPAL && QF::IS_ARCHETYPAL;
7367
let min_size = if archetype_query { max_size } else { 0 };
7468
(min_size, Some(max_size))
@@ -264,11 +258,16 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<
264258
return None;
265259
}
266260

267-
// first, iterate from last to first until next item is found
261+
// TODO: can speed up the following code using `cursor.remaining()` instead of `next_item.is_none()`
262+
// when Q::Fetch::IS_ARCHETYPAL && F::Fetch::IS_ARCHETYPAL
263+
//
264+
// let `i` be the index of `c`, the last cursor in `self.cursors` that
265+
// returns `K-i` or more elements.
266+
// Make cursor in index `j` for all `j` in `[i, K)` a copy of `c` advanced `j-i+1` times.
267+
// If no such `c` exists, return `None`
268268
'outer: for i in (0..K).rev() {
269269
match self.cursors[i].next(self.tables, self.archetypes, self.query_state) {
270270
Some(_) => {
271-
// walk forward up to last element, propagating cursor state forward
272271
for j in (i + 1)..K {
273272
self.cursors[j] = self.cursors[j - 1].clone();
274273
match self.cursors[j].next(self.tables, self.archetypes, self.query_state) {
@@ -364,11 +363,7 @@ where
364363
F: WorldQuery + ArchetypeFilter,
365364
{
366365
fn len(&self) -> usize {
367-
self.query_state
368-
.matched_archetype_ids
369-
.iter()
370-
.map(|id| self.archetypes[*id].len())
371-
.sum()
366+
self.size_hint().0
372367
}
373368
}
374369

@@ -473,6 +468,18 @@ where
473468
}
474469
}
475470

471+
/// How many values will this cursor return?
472+
fn remaining(&self, tables: &'w Tables, archetypes: &'w Archetypes) -> usize {
473+
let remaining_matched: usize = if Self::IS_DENSE {
474+
let ids = self.table_id_iter.clone();
475+
ids.map(|id| tables[*id].len()).sum()
476+
} else {
477+
let ids = self.archetype_id_iter.clone();
478+
ids.map(|id| archetypes[*id].len()).sum()
479+
};
480+
remaining_matched + self.current_len - self.current_index
481+
}
482+
476483
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
477484
// QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
478485
/// # Safety

crates/bevy_ecs/src/query/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,30 @@ mod tests {
144144
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
145145
assert_eq!(values.iter(&world).len(), n);
146146
assert_eq!(values.iter(&world).count(), n);
147+
let mut iterator = values.iter(&world);
148+
let _ = iterator.next();
149+
assert_eq!(iterator.len(), n - 1);
150+
147151
let mut values = world.query_filtered::<&A, Or<(With<B>, Without<C>)>>();
148152
let n = 7;
149153
assert_eq!(values.iter(&world).size_hint().0, n);
150154
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
151155
assert_eq!(values.iter(&world).len(), n);
152156
assert_eq!(values.iter(&world).count(), n);
157+
let mut iterator = values.iter(&world);
158+
let _ = iterator.next();
159+
assert_eq!(iterator.len(), n - 1);
160+
153161
let mut values = world.query_filtered::<&A, Or<(Without<B>, With<C>)>>();
154162
let n = 8;
155163
assert_eq!(values.iter(&world).size_hint().0, n);
156164
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
157165
assert_eq!(values.iter(&world).len(), n);
158166
assert_eq!(values.iter(&world).count(), n);
167+
let mut iterator = values.iter(&world);
168+
let _ = iterator.next();
169+
assert_eq!(iterator.len(), n - 1);
170+
159171
let mut values = world.query_filtered::<&A, Or<(Without<B>, Without<C>)>>();
160172
let n = 9;
161173
assert_eq!(values.iter(&world).size_hint().0, n);
@@ -169,6 +181,12 @@ mod tests {
169181
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
170182
assert_eq!(values.iter(&world).len(), n);
171183
assert_eq!(values.iter(&world).count(), n);
184+
let mut iterator = values.iter(&world);
185+
let _ = iterator.next();
186+
assert_eq!(iterator.len(), 0);
187+
let _ = iterator.next();
188+
assert_eq!(iterator.len(), 0);
189+
172190
let mut values = world.query_filtered::<&A, Or<(Or<(With<B>, With<C>)>, With<D>)>>();
173191
let n = 6;
174192
assert_eq!(values.iter(&world).size_hint().0, n);

0 commit comments

Comments
 (0)