1use alloc::rc::Rc;
9use alloc::vec::Vec;
10use core::marker::PhantomData;
11
12use crate::entity::EntityId;
13use crate::query::{Query1, Query2, QueryCursor, QueryEffects, QueryError, QuerySpec};
14use crate::time::ChangeTick;
15use crate::world::query::cache::QueryTopologySnapshot;
16use crate::world::query::collect::{collect_query1_entities, collect_query1_structural_members};
17use crate::world::query::filter::validate_exact_ids;
18use crate::world::query::plan::{ResolvedPlan, TraversalSource};
19use crate::world::{World, WorldOwner};
20
21#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
23#[non_exhaustive]
24pub enum QueryPolicy {
25 #[default]
27 Prepared,
28 Membership,
30 DeltaMembership,
32 Result,
34}
35
36pub enum QueryWindow<'a> {
38 All,
40 Since(ChangeTick),
42 Cursor(&'a mut QueryCursor),
44}
45
46impl<'a> QueryWindow<'a> {
47 pub const fn all() -> Self {
49 Self::All
50 }
51
52 pub const fn since(tick: ChangeTick) -> Self {
54 Self::Since(tick)
55 }
56
57 pub fn cursor(cursor: &mut QueryCursor) -> QueryWindow<'_> {
59 QueryWindow::Cursor(cursor)
60 }
61
62 pub(crate) fn into_parts(
63 self,
64 world: &World,
65 fingerprint: u64,
66 ) -> Result<(ChangeTick, Option<&'a mut QueryCursor>), QueryError> {
67 match self {
68 Self::All => Ok((ChangeTick::ZERO, None)),
69 Self::Since(tick) => Ok((tick, None)),
70 Self::Cursor(cursor) => {
71 cursor.validate(world, fingerprint)?;
72 Ok((cursor.since(), Some(cursor)))
73 }
74 }
75 }
76}
77
78pub struct PreparedQuery1<T: 'static> {
80 pub(crate) owner: WorldOwner,
81 pub(crate) plan: Rc<ResolvedPlan>,
82 materialization: Materialization,
83 mutation_scratch: Vec<EntityId>,
84 marker: PhantomData<fn() -> T>,
85}
86
87pub struct PreparedQuery2<A: 'static, B: 'static> {
89 pub(crate) owner: WorldOwner,
90 pub(crate) plan: Rc<ResolvedPlan>,
91 pub(crate) second_index: usize,
92 pub(crate) second_is_table: bool,
93 driver_revisions: (u64, u64),
94 materialization: Materialization,
95 mutation_scratch: Vec<EntityId>,
96 marker: PhantomData<fn() -> (A, B)>,
97}
98
99enum Materialization {
100 Prepared,
101 Membership(MaterializedSet),
102 DeltaMembership(DeltaSet),
103 Result(MaterializedSet),
104}
105
106struct MaterializedSet {
107 topology: QueryTopologySnapshot,
108 ids: Vec<EntityId>,
109}
110
111struct DeltaSet {
112 topology: QueryTopologySnapshot,
113 ids: Vec<EntityId>,
114 reverse: Vec<Option<usize>>,
115 changed: Vec<EntityId>,
116 changed_reverse: Vec<Option<usize>>,
117 cursor: Rc<core::cell::Cell<u64>>,
118}
119
120impl Materialization {
121 fn build(
122 world: &mut World,
123 plan: &ResolvedPlan,
124 policy: QueryPolicy,
125 ) -> Result<Self, QueryError> {
126 if !matches!(policy, QueryPolicy::Prepared)
127 && matches!(plan.traversal, TraversalSource::Exact { .. })
128 {
129 return Err(QueryError::UnsupportedCachePolicy {
130 detail: alloc::string::String::from(
131 "materialized policies do not support exact-id query order",
132 ),
133 });
134 }
135 if matches!(policy, QueryPolicy::Result)
136 && (!plan.added_indices.is_empty() || !plan.changed_indices.is_empty())
137 {
138 return Err(QueryError::MovingChangeWindow);
139 }
140
141 let topology = || QueryTopologySnapshot::capture(world, plan);
142 Ok(match policy {
143 QueryPolicy::Prepared => Self::Prepared,
144 QueryPolicy::Membership => Self::Membership(MaterializedSet {
145 topology: topology(),
146 ids: collect_query1_structural_members(world, plan),
147 }),
148 QueryPolicy::DeltaMembership => {
149 let ids = collect_query1_structural_members(world, plan);
150 let reverse = build_reverse(&ids);
151 Self::DeltaMembership(DeltaSet {
152 topology: topology(),
153 ids,
154 reverse,
155 changed: Vec::new(),
156 changed_reverse: Vec::new(),
157 cursor: world.register_query_delta_cursor(),
158 })
159 }
160 QueryPolicy::Result => Self::Result(MaterializedSet {
161 topology: topology(),
162 ids: collect_query1_entities(world, plan, ChangeTick::ZERO, world.change_tick()),
163 }),
164 })
165 }
166
167 fn refresh(&mut self, world: &World, plan: &ResolvedPlan) {
168 match self {
169 Self::Prepared => {}
170 Self::Membership(set) => {
171 if topology_changed(&mut set.topology, world) {
172 set.ids = collect_query1_structural_members(world, plan);
173 set.topology = QueryTopologySnapshot::capture(world, plan);
174 }
175 }
176 Self::DeltaMembership(set) => {
177 if set.topology.observed_global_revision() != world.query_topology_revision() {
178 world.collect_query_delta_entities(
179 &set.cursor,
180 plan,
181 &mut set.changed,
182 &mut set.changed_reverse,
183 );
184 for index in 0..set.changed.len() {
185 let entity = set.changed[index];
186 update_delta_entity(set, world, plan, entity);
187 }
188 set.topology = QueryTopologySnapshot::capture(world, plan);
189 }
190 }
191 Self::Result(set) => {
192 if topology_changed(&mut set.topology, world) {
193 set.ids =
194 collect_query1_entities(world, plan, ChangeTick::ZERO, world.change_tick());
195 set.topology = QueryTopologySnapshot::capture(world, plan);
196 }
197 }
198 }
199 }
200
201 fn ids_and_temporal_filter(&self, plan: &ResolvedPlan) -> Option<(&[EntityId], bool)> {
202 let apply_temporal = !plan.added_indices.is_empty() || !plan.changed_indices.is_empty();
203 match self {
204 Self::Prepared => None,
205 Self::Membership(set) => Some((&set.ids, apply_temporal)),
206 Self::DeltaMembership(set) => Some((&set.ids, apply_temporal)),
207 Self::Result(set) => Some((&set.ids, false)),
208 }
209 }
210}
211
212fn topology_changed(topology: &mut QueryTopologySnapshot, world: &World) -> bool {
213 let revision = world.query_topology_revision();
214 if topology.observed_global_revision() == revision {
215 return false;
216 }
217 if topology.dependencies_are_current(world) {
218 topology.observe_global_revision(revision);
219 false
220 } else {
221 true
222 }
223}
224
225fn build_reverse(ids: &[EntityId]) -> Vec<Option<usize>> {
226 let Some(max_slot) = ids.iter().map(|entity| entity.slot() as usize).max() else {
227 return Vec::new();
228 };
229 let mut reverse = alloc::vec![None; max_slot + 1];
230 for (index, entity) in ids.iter().enumerate() {
231 reverse[entity.slot() as usize] = Some(index);
232 }
233 reverse
234}
235
236fn update_delta_entity(set: &mut DeltaSet, world: &World, plan: &ResolvedPlan, entity: EntityId) {
237 let slot = entity.slot() as usize;
238 let existing = set.reverse.get(slot).and_then(|index| *index);
239 let matches = crate::world::query::filter::entity_matches_structural(world, entity, plan);
240
241 if let Some(index) = existing {
242 if set.ids.get(index) == Some(&entity) && matches {
243 return;
244 }
245 remove_delta_index(set, index);
246 }
247 if matches {
248 if set.reverse.len() <= slot {
249 set.reverse.resize(slot + 1, None);
250 }
251 let index = set.ids.len();
252 set.ids.push(entity);
253 set.reverse[slot] = Some(index);
254 }
255}
256
257fn remove_delta_index(set: &mut DeltaSet, index: usize) {
258 let removed = set.ids.swap_remove(index);
259 set.reverse[removed.slot() as usize] = None;
260 if let Some(&moved) = set.ids.get(index) {
261 set.reverse[moved.slot() as usize] = Some(index);
262 }
263}
264
265impl World {
266 pub fn prepare_query1<T: 'static>(
267 &mut self,
268 spec: QuerySpec,
269 policy: QueryPolicy,
270 ) -> Result<PreparedQuery1<T>, QueryError> {
271 let plan = self.resolve_query1_plan::<T>(&spec)?;
272 validate_exact_ids(self, &plan)?;
273 let materialization = Materialization::build(self, &plan, policy)?;
274 Ok(PreparedQuery1 {
275 owner: self.owner_token(),
276 plan,
277 materialization,
278 mutation_scratch: Vec::new(),
279 marker: PhantomData,
280 })
281 }
282
283 pub fn prepare_query2<A: 'static, B: 'static>(
284 &mut self,
285 spec: QuerySpec,
286 policy: QueryPolicy,
287 ) -> Result<PreparedQuery2<A, B>, QueryError> {
288 let (plan, second_index, second_is_table) = self.resolve_query2_plan::<A, B>(&spec)?;
289 validate_exact_ids(self, &plan)?;
290 let materialization = Materialization::build(self, &plan, policy)?;
291 Ok(PreparedQuery2 {
292 owner: self.owner_token(),
293 plan,
294 second_index,
295 second_is_table,
296 driver_revisions: (u64::MAX, u64::MAX),
300 materialization,
301 mutation_scratch: Vec::new(),
302 marker: PhantomData,
303 })
304 }
305}
306
307impl<T: 'static> PreparedQuery1<T> {
308 pub fn iter<'w, 'c>(
310 &'w mut self,
311 world: &'w mut World,
312 window: QueryWindow<'c>,
313 ) -> Result<Query1<'w, 'c, T>, QueryError> {
314 self.validate_world(world)?;
315 validate_exact_ids(world, &self.plan)?;
316 let captured_now = world.change_tick();
317 let (since, cursor) = window.into_parts(world, self.plan.fingerprint)?;
318 self.materialization.refresh(world, &self.plan);
319 let materialized = self.materialization.ids_and_temporal_filter(&self.plan);
320
321 let table_component = match self.plan.traversal {
322 TraversalSource::Table { component_index } => Some(component_index),
323 _ => None,
324 };
325 if let Some(index) = table_component {
326 world.ensure_table_archetypes(index);
327 }
328 let table_archetypes = table_component.map(|index| {
329 world
330 .table_archetypes(index)
331 .expect("table archetypes prepared")
332 });
333 Query1::new_prepared(
334 world,
335 self.plan.clone(),
336 since,
337 captured_now,
338 cursor,
339 materialized,
340 table_archetypes,
341 )
342 }
343
344 pub fn for_each_mut(
346 &mut self,
347 world: &mut World,
348 window: QueryWindow<'_>,
349 f: impl FnMut(EntityId, &mut T) -> Result<(), QueryError>,
350 ) -> Result<(), QueryError> {
351 self.for_each_mut_inner(world, window, f)
352 }
353
354 pub fn for_each_mut_with_effects(
356 &mut self,
357 world: &mut World,
358 window: QueryWindow<'_>,
359 f: impl FnMut(EntityId, &mut T, &mut QueryEffects<'_>) -> Result<(), QueryError>,
360 ) -> Result<(), QueryError> {
361 self.for_each_mut_effects_inner(world, window, f)
362 }
363
364 fn for_each_mut_inner(
365 &mut self,
366 world: &mut World,
367 window: QueryWindow<'_>,
368 mut f: impl FnMut(EntityId, &mut T) -> Result<(), QueryError>,
369 ) -> Result<(), QueryError> {
370 self.for_each_mut_effects_inner(world, window, |entity, value, _| f(entity, value))
371 }
372
373 fn for_each_mut_effects_inner(
374 &mut self,
375 world: &mut World,
376 window: QueryWindow<'_>,
377 f: impl FnMut(EntityId, &mut T, &mut QueryEffects<'_>) -> Result<(), QueryError>,
378 ) -> Result<(), QueryError> {
379 self.validate_world(world)?;
380 validate_exact_ids(world, &self.plan)?;
381 let captured_now = world.change_tick();
382 let (since, mut cursor) = window.into_parts(world, self.plan.fingerprint)?;
383 self.materialization.refresh(world, &self.plan);
384 world.for_each_mut_resolved(
385 &self.plan,
386 self.materialization.ids_and_temporal_filter(&self.plan),
387 &mut self.mutation_scratch,
388 since,
389 captured_now,
390 f,
391 )?;
392 if let Some(cursor) = cursor.as_mut() {
393 cursor.commit(captured_now);
394 }
395 Ok(())
396 }
397
398 fn validate_world(&self, world: &World) -> Result<(), QueryError> {
399 if self.owner.same(&world.owner_token()) {
400 Ok(())
401 } else {
402 Err(QueryError::WrongOwner)
403 }
404 }
405}
406
407impl<A: 'static, B: 'static> PreparedQuery2<A, B> {
408 pub fn iter<'w, 'c>(
410 &'w mut self,
411 world: &'w mut World,
412 window: QueryWindow<'c>,
413 ) -> Result<Query2<'w, 'c, A, B>, QueryError> {
414 self.validate_world(world)?;
415 self.refresh_physical_plan(world);
416 validate_exact_ids(world, &self.plan)?;
417 let captured_now = world.change_tick();
418 let (since, cursor) = window.into_parts(world, self.plan.fingerprint)?;
419 self.materialization.refresh(world, &self.plan);
420 let materialized = self.materialization.ids_and_temporal_filter(&self.plan);
421
422 let table_component = match self.plan.traversal {
423 TraversalSource::Table { component_index } => Some(component_index),
424 _ => None,
425 };
426 if let Some(index) = table_component {
427 world.ensure_table_archetypes(index);
428 }
429 let table_archetypes = table_component.map(|index| {
430 world
431 .table_archetypes(index)
432 .expect("table archetypes prepared")
433 });
434 Query2::new_prepared(
435 world,
436 self.plan.clone(),
437 since,
438 captured_now,
439 cursor,
440 materialized,
441 table_archetypes,
442 self.second_index,
443 self.second_is_table,
444 )
445 }
446
447 pub fn for_each_mut_mut(
449 &mut self,
450 world: &mut World,
451 window: QueryWindow<'_>,
452 mut f: impl FnMut(EntityId, &mut A, &mut B) -> Result<(), QueryError>,
453 ) -> Result<(), QueryError> {
454 self.for_each_mut_mut_with_effects(world, window, |entity, a, b, _| f(entity, a, b))
455 }
456
457 pub fn for_each_mut_mut_with_effects(
459 &mut self,
460 world: &mut World,
461 window: QueryWindow<'_>,
462 f: impl FnMut(EntityId, &mut A, &mut B, &mut QueryEffects<'_>) -> Result<(), QueryError>,
463 ) -> Result<(), QueryError> {
464 self.execute_mut(world, window, f)
465 }
466
467 pub fn for_each_mut_read(
469 &mut self,
470 world: &mut World,
471 window: QueryWindow<'_>,
472 mut f: impl FnMut(EntityId, &mut A, &B) -> Result<(), QueryError>,
473 ) -> Result<(), QueryError> {
474 self.for_each_mut_read_with_effects(world, window, |entity, a, b, _| f(entity, a, b))
475 }
476
477 pub fn for_each_mut_read_with_effects(
479 &mut self,
480 world: &mut World,
481 window: QueryWindow<'_>,
482 f: impl FnMut(EntityId, &mut A, &B, &mut QueryEffects<'_>) -> Result<(), QueryError>,
483 ) -> Result<(), QueryError> {
484 self.execute_mut_read(world, window, f)
485 }
486
487 fn execute_mut(
488 &mut self,
489 world: &mut World,
490 window: QueryWindow<'_>,
491 f: impl FnMut(EntityId, &mut A, &mut B, &mut QueryEffects<'_>) -> Result<(), QueryError>,
492 ) -> Result<(), QueryError> {
493 self.validate_world(world)?;
494 self.refresh_physical_plan(world);
495 validate_exact_ids(world, &self.plan)?;
496 let captured_now = world.change_tick();
497 let (since, mut cursor) = window.into_parts(world, self.plan.fingerprint)?;
498 self.materialization.refresh(world, &self.plan);
499 world.for_each2_mut_resolved(
500 &self.plan,
501 self.second_index,
502 self.second_is_table,
503 self.materialization.ids_and_temporal_filter(&self.plan),
504 &mut self.mutation_scratch,
505 since,
506 captured_now,
507 f,
508 )?;
509 if let Some(cursor) = cursor.as_mut() {
510 cursor.commit(captured_now);
511 }
512 Ok(())
513 }
514
515 fn execute_mut_read(
516 &mut self,
517 world: &mut World,
518 window: QueryWindow<'_>,
519 f: impl FnMut(EntityId, &mut A, &B, &mut QueryEffects<'_>) -> Result<(), QueryError>,
520 ) -> Result<(), QueryError> {
521 self.validate_world(world)?;
522 self.refresh_physical_plan(world);
523 validate_exact_ids(world, &self.plan)?;
524 let captured_now = world.change_tick();
525 let (since, mut cursor) = window.into_parts(world, self.plan.fingerprint)?;
526 self.materialization.refresh(world, &self.plan);
527 world.for_each2_mut_read_resolved(
528 &self.plan,
529 self.second_index,
530 self.second_is_table,
531 self.materialization.ids_and_temporal_filter(&self.plan),
532 &mut self.mutation_scratch,
533 since,
534 captured_now,
535 f,
536 )?;
537 if let Some(cursor) = cursor.as_mut() {
538 cursor.commit(captured_now);
539 }
540 Ok(())
541 }
542
543 fn validate_world(&self, world: &World) -> Result<(), QueryError> {
544 if self.owner.same(&world.owner_token()) {
545 Ok(())
546 } else {
547 Err(QueryError::WrongOwner)
548 }
549 }
550
551 fn refresh_physical_plan(&mut self, world: &World) {
552 if matches!(self.plan.traversal, TraversalSource::Exact { .. }) {
553 return;
554 }
555
556 let revisions = (
557 world.query_component_topology_revision(self.plan.primary_index),
558 world.query_component_topology_revision(self.second_index),
559 );
560 if self.driver_revisions == revisions {
561 return;
562 }
563
564 let primary_len =
565 world.query_component_population(self.plan.primary_index, self.plan.primary_is_table);
566 let second_len = world.query_component_population(self.second_index, self.second_is_table);
567 let (component_index, is_table) = if second_len < primary_len {
568 (self.second_index, self.second_is_table)
569 } else {
570 (self.plan.primary_index, self.plan.primary_is_table)
571 };
572 let traversal = if is_table {
573 TraversalSource::Table { component_index }
574 } else {
575 TraversalSource::Sparse { component_index }
576 };
577
578 if !same_traversal(&self.plan.traversal, &traversal) {
579 let mut plan = (*self.plan).clone();
580 plan.traversal = traversal;
581 self.plan = Rc::new(plan);
582 }
583 self.driver_revisions = revisions;
584 }
585}
586
587fn same_traversal(left: &TraversalSource, right: &TraversalSource) -> bool {
588 match (left, right) {
589 (TraversalSource::All, TraversalSource::All) => true,
590 (
591 TraversalSource::Sparse {
592 component_index: left,
593 },
594 TraversalSource::Sparse {
595 component_index: right,
596 },
597 )
598 | (
599 TraversalSource::Table {
600 component_index: left,
601 },
602 TraversalSource::Table {
603 component_index: right,
604 },
605 ) => left == right,
606 (TraversalSource::Exact { ids: left }, TraversalSource::Exact { ids: right }) => {
607 left == right
608 }
609 _ => false,
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616 use crate::component::ComponentOptions;
617 use crate::world::WorldBuilder;
618
619 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
620 struct A(i32);
621
622 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
623 struct B(i32);
624
625 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
626 struct C(i32);
627
628 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
629 struct D(i32);
630
631 fn world() -> World {
632 let mut builder = WorldBuilder::new();
633 builder
634 .register_component::<A>(ComponentOptions::sparse())
635 .expect("A");
636 builder
637 .register_component::<B>(ComponentOptions::table())
638 .expect("B");
639 builder
640 .register_component::<C>(ComponentOptions::sparse())
641 .expect("C");
642 builder
643 .register_component::<D>(ComponentOptions::table())
644 .expect("D");
645 builder.build().expect("world")
646 }
647
648 fn delta_state(world: &mut World, plan: &ResolvedPlan) -> DeltaSet {
649 let ids = collect_query1_structural_members(world, plan);
650 let reverse = build_reverse(&ids);
651 let topology = QueryTopologySnapshot::capture(world, plan);
652 let cursor = world.register_query_delta_cursor();
653 DeltaSet {
654 topology,
655 ids,
656 reverse,
657 changed: Vec::new(),
658 changed_reverse: Vec::new(),
659 cursor,
660 }
661 }
662
663 #[test]
664 fn reverse_indexed_delta_handles_add_remove_and_slot_reuse() {
665 let mut world = world();
666 let first = world.spawn().expect("first");
667 world.insert(first, A(1)).expect("A");
668 let plan = world
669 .resolve_query1_plan::<A>(&QuerySpec::new())
670 .expect("plan");
671 let mut state = delta_state(&mut world, &plan);
672
673 let second = world.spawn().expect("second");
674 world.insert(second, A(2)).expect("A2");
675 let third = world.spawn().expect("third");
676 world.insert(third, A(3)).expect("A3");
677 world.collect_query_delta_entities(
678 &state.cursor,
679 &plan,
680 &mut state.changed,
681 &mut state.changed_reverse,
682 );
683 let changed = state.changed.clone();
684 assert_eq!(changed, alloc::vec![second, third]);
685 for entity in changed {
686 update_delta_entity(&mut state, &world, &plan, entity);
687 }
688 assert_eq!(state.ids, alloc::vec![first, second, third]);
689 assert_eq!(state.reverse[second.slot() as usize], Some(1));
690
691 world.remove::<A>(first).expect("remove");
692 world.collect_query_delta_entities(
693 &state.cursor,
694 &plan,
695 &mut state.changed,
696 &mut state.changed_reverse,
697 );
698 let changed = state.changed.clone();
699 assert_eq!(changed, alloc::vec![first]);
700 for entity in changed {
701 update_delta_entity(&mut state, &world, &plan, entity);
702 }
703 assert_eq!(state.ids.len(), 2);
704 assert!(state.ids.contains(&second));
705 assert!(state.ids.contains(&third));
706 assert_eq!(state.reverse[first.slot() as usize], None);
707 assert_eq!(
708 state.reverse[second.slot() as usize],
709 state.ids.iter().position(|&entity| entity == second)
710 );
711
712 world.despawn(second).expect("despawn");
713 let replacement = world.spawn().expect("replacement");
714 assert_eq!(replacement.slot(), second.slot());
715 world.insert(replacement, A(4)).expect("replacement A");
716 world.collect_query_delta_entities(
717 &state.cursor,
718 &plan,
719 &mut state.changed,
720 &mut state.changed_reverse,
721 );
722 let changed = state.changed.clone();
723 assert_eq!(changed, alloc::vec![replacement]);
725 for entity in changed {
726 update_delta_entity(&mut state, &world, &plan, entity);
727 }
728 assert_eq!(state.ids.len(), 2);
729 assert!(state.ids.contains(&third));
730 assert!(state.ids.contains(&replacement));
731 assert_eq!(
732 state.reverse[third.slot() as usize],
733 state.ids.iter().position(|&entity| entity == third)
734 );
735 assert_eq!(
736 state.reverse[replacement.slot() as usize],
737 state.ids.iter().position(|&entity| entity == replacement)
738 );
739 }
740
741 #[test]
742 fn materialized_policies_track_structural_changes() {
743 for policy in [
744 QueryPolicy::Membership,
745 QueryPolicy::DeltaMembership,
746 QueryPolicy::Result,
747 ] {
748 let mut world = world();
749 let mut query = world
750 .prepare_query1::<A>(QuerySpec::new(), policy)
751 .expect("prepare");
752 let entity = world.spawn().expect("spawn");
753 world.insert(entity, A(3)).expect("insert");
754 assert_eq!(
755 query
756 .iter(&mut world, QueryWindow::All)
757 .expect("iter")
758 .count(),
759 1
760 );
761 world.remove::<A>(entity).expect("remove");
762 assert_eq!(
763 query
764 .iter(&mut world, QueryWindow::All)
765 .expect("iter")
766 .count(),
767 0
768 );
769 }
770 }
771
772 #[test]
773 fn current_delta_query_skips_prefix_retained_for_lagging_query() {
774 let mut world = world();
775 let entity = world.spawn().expect("spawn");
776 let mut current = world
777 .prepare_query1::<A>(QuerySpec::new(), QueryPolicy::DeltaMembership)
778 .expect("current");
779 let mut lagging = world
780 .prepare_query1::<A>(QuerySpec::new(), QueryPolicy::DeltaMembership)
781 .expect("lagging");
782
783 for value in 0..32 {
784 if value % 2 == 0 {
785 world.insert(entity, A(value)).expect("insert");
786 } else {
787 world.remove::<A>(entity).expect("remove");
788 }
789 let expected = usize::from(value % 2 == 0);
790 assert_eq!(
791 current
792 .iter(&mut world, QueryWindow::All)
793 .expect("current refresh")
794 .count(),
795 expected
796 );
797 }
798
799 assert_eq!(world.query_delta_log_len_for_test(), 32);
802 assert_eq!(
803 lagging
804 .iter(&mut world, QueryWindow::All)
805 .expect("lagging refresh")
806 .count(),
807 0
808 );
809 assert_eq!(
810 current
811 .iter(&mut world, QueryWindow::All)
812 .expect("current remains current")
813 .count(),
814 0
815 );
816 }
817
818 #[test]
819 fn delta_sequence_exhaustion_rebases_retained_log_and_cursors() {
820 let mut world = world();
821 let entity = world.spawn().expect("spawn");
822 let plan = world
823 .resolve_query1_plan::<A>(&QuerySpec::new())
824 .expect("plan");
825 let cursor = world.register_query_delta_cursor();
826 world.seed_query_delta_exhaustion_for_test(&cursor, entity, plan.primary_index);
827
828 world.insert(entity, A(1)).expect("insert after rebase");
829 assert_eq!(cursor.get(), 0);
830 assert_eq!(world.query_delta_sequences_for_test(), alloc::vec![0, 1]);
831
832 let mut changed = Vec::new();
833 let mut reverse = Vec::new();
834 world.collect_query_delta_entities(&cursor, &plan, &mut changed, &mut reverse);
835 assert_eq!(changed, alloc::vec![entity]);
836 assert_eq!(cursor.get(), 2);
837 }
838
839 #[test]
840 fn delta_cursor_offsets_clamp_before_and_after_retained_range() {
841 let mut world = world();
842 let entity = world.spawn().expect("spawn");
843 let plan = world
844 .resolve_query1_plan::<A>(&QuerySpec::new())
845 .expect("plan");
846 let cursor = world.register_query_delta_cursor();
847 world.insert(entity, A(1)).expect("insert one");
848 world.remove::<A>(entity).expect("remove one");
849 world.insert(entity, A(2)).expect("insert two");
850
851 cursor.set(2);
852 world.remove::<A>(entity).expect("remove two");
853 assert_eq!(world.query_delta_sequences_for_test(), alloc::vec![2, 3]);
854
855 let mut changed = Vec::new();
856 let mut reverse = Vec::new();
857 cursor.set(0);
858 world.collect_query_delta_entities(&cursor, &plan, &mut changed, &mut reverse);
859 assert_eq!(changed, alloc::vec![entity]);
860 assert_eq!(cursor.get(), 4);
861
862 cursor.set(99);
863 world.collect_query_delta_entities(&cursor, &plan, &mut changed, &mut reverse);
864 assert!(changed.is_empty());
865 assert_eq!(cursor.get(), 4);
866 }
867
868 #[test]
869 fn mixed_mut_read_updates_only_a() {
870 let mut world = world();
871 let entity = world.spawn().expect("spawn");
872 world.insert(entity, A(2)).expect("A");
873 world.insert(entity, B(5)).expect("B");
874 let mut query = world
875 .prepare_query2::<A, B>(QuerySpec::new(), QueryPolicy::Prepared)
876 .expect("prepare");
877 let before_b =
878 world.component_changed_tick(entity, world.component_index::<B>().expect("B index"));
879 query
880 .for_each_mut_read(&mut world, QueryWindow::All, |_, a, b| {
881 a.0 += b.0;
882 Ok(())
883 })
884 .expect("execute");
885 assert_eq!(world.get::<A>(entity).expect("get").expect("A"), &A(7));
886 assert_eq!(world.get::<B>(entity).expect("get").expect("B"), &B(5));
887 assert_eq!(
888 world.component_changed_tick(entity, world.component_index::<B>().expect("B index"),),
889 before_b
890 );
891 }
892
893 #[test]
894 fn query2_exact_ids_preserve_order_independent_of_driver() {
895 let mut world = world();
896 let first = world.spawn().expect("first");
897 let second = world.spawn().expect("second");
898 for (entity, value) in [(first, 1), (second, 2)] {
899 world.insert(entity, A(value)).expect("A");
900 world.insert(entity, B(value)).expect("B");
901 }
902 let spec = QuerySpec::new().exact_ids(
903 alloc::vec![second, first],
904 crate::query::ExactIdPolicy::SkipUnavailable,
905 );
906 let mut query = world
907 .prepare_query2::<A, B>(spec, QueryPolicy::Prepared)
908 .expect("prepare");
909 let ids: Vec<_> = query
910 .iter(&mut world, QueryWindow::All)
911 .expect("iter")
912 .map(|(entity, _, _)| entity)
913 .collect();
914 assert_eq!(ids, alloc::vec![second, first]);
915 }
916
917 #[test]
918 fn query2_reselects_driver_after_cardinality_crossover_with_stable_cursors() {
919 {
921 let mut world = world();
922 let first = world.spawn().expect("first");
923 for entity in [
924 first,
925 world.spawn().expect("a2"),
926 world.spawn().expect("a3"),
927 ] {
928 world.insert(entity, A(1)).expect("A");
929 }
930 world.insert(first, C(1)).expect("C");
931 let spec = QuerySpec::new();
932 let mut query = world
933 .prepare_query2::<A, C>(spec.clone(), QueryPolicy::Prepared)
934 .expect("prepare sparse/sparse");
935 let c_index = world.component_index::<C>().expect("C index");
936 assert!(matches!(
937 query.plan.traversal,
938 TraversalSource::Sparse { component_index } if component_index == c_index
939 ));
940 let mut before =
941 QueryCursor::from_spec2_start::<A, C>(&mut world, &spec).expect("before cursor");
942 for _ in 0..3 {
943 let entity = world.spawn().expect("C-only");
944 world.insert(entity, C(2)).expect("C-only insert");
945 }
946 assert_eq!(
947 query
948 .iter(&mut world, QueryWindow::Cursor(&mut before))
949 .expect("sparse/sparse before cursor")
950 .count(),
951 1
952 );
953 let a_index = world.component_index::<A>().expect("A index");
954 assert!(matches!(
955 query.plan.traversal,
956 TraversalSource::Sparse { component_index } if component_index == a_index
957 ));
958 let mut after =
959 QueryCursor::from_spec2_start::<A, C>(&mut world, &spec).expect("after cursor");
960 assert_eq!(
961 query
962 .iter(&mut world, QueryWindow::Cursor(&mut after))
963 .expect("sparse/sparse after cursor")
964 .count(),
965 1
966 );
967 }
968
969 {
971 let mut world = world();
972 let first = world.spawn().expect("first");
973 for entity in [
974 first,
975 world.spawn().expect("b2"),
976 world.spawn().expect("b3"),
977 ] {
978 world.insert(entity, B(1)).expect("B");
979 }
980 world.insert(first, D(1)).expect("D");
981 let spec = QuerySpec::new();
982 let mut query = world
983 .prepare_query2::<B, D>(spec.clone(), QueryPolicy::Prepared)
984 .expect("prepare table/table");
985 let d_index = world.component_index::<D>().expect("D index");
986 assert!(matches!(
987 query.plan.traversal,
988 TraversalSource::Table { component_index } if component_index == d_index
989 ));
990 let mut before =
991 QueryCursor::from_spec2_start::<B, D>(&mut world, &spec).expect("before cursor");
992 for _ in 0..3 {
993 let entity = world.spawn().expect("D-only");
994 world.insert(entity, D(2)).expect("D-only insert");
995 }
996 assert_eq!(
997 query
998 .iter(&mut world, QueryWindow::Cursor(&mut before))
999 .expect("table/table before cursor")
1000 .count(),
1001 1
1002 );
1003 let b_index = world.component_index::<B>().expect("B index");
1004 assert!(matches!(
1005 query.plan.traversal,
1006 TraversalSource::Table { component_index } if component_index == b_index
1007 ));
1008 let mut after =
1009 QueryCursor::from_spec2_start::<B, D>(&mut world, &spec).expect("after cursor");
1010 assert_eq!(
1011 query
1012 .iter(&mut world, QueryWindow::Cursor(&mut after))
1013 .expect("table/table after cursor")
1014 .count(),
1015 1
1016 );
1017 }
1018
1019 {
1021 let mut world = world();
1022 let first = world.spawn().expect("first");
1023 for entity in [
1024 first,
1025 world.spawn().expect("a2"),
1026 world.spawn().expect("a3"),
1027 ] {
1028 world.insert(entity, A(1)).expect("A");
1029 }
1030 world.insert(first, B(1)).expect("B");
1031 let spec = QuerySpec::new();
1032 let mut query = world
1033 .prepare_query2::<A, B>(spec.clone(), QueryPolicy::Prepared)
1034 .expect("prepare sparse/table");
1035 let b_index = world.component_index::<B>().expect("B index");
1036 assert!(matches!(
1037 query.plan.traversal,
1038 TraversalSource::Table { component_index } if component_index == b_index
1039 ));
1040 let mut before =
1041 QueryCursor::from_spec2_start::<A, B>(&mut world, &spec).expect("before cursor");
1042 for _ in 0..3 {
1043 let entity = world.spawn().expect("B-only");
1044 world.insert(entity, B(2)).expect("B-only insert");
1045 }
1046 assert_eq!(
1047 query
1048 .iter(&mut world, QueryWindow::Cursor(&mut before))
1049 .expect("sparse/table before cursor")
1050 .count(),
1051 1
1052 );
1053 let a_index = world.component_index::<A>().expect("A index");
1054 assert!(matches!(
1055 query.plan.traversal,
1056 TraversalSource::Sparse { component_index } if component_index == a_index
1057 ));
1058 let mut after =
1059 QueryCursor::from_spec2_start::<A, B>(&mut world, &spec).expect("after cursor");
1060 assert_eq!(
1061 query
1062 .iter(&mut world, QueryWindow::Cursor(&mut after))
1063 .expect("sparse/table after cursor")
1064 .count(),
1065 1
1066 );
1067 }
1068 }
1069
1070 #[test]
1071 fn mixed_mut_read_covers_all_storage_pairs() {
1072 let mut world = world();
1073 let entity = world.spawn().expect("spawn");
1074 world.insert(entity, A(1)).expect("A");
1075 world.insert(entity, B(2)).expect("B");
1076 world.insert(entity, C(3)).expect("C");
1077 world.insert(entity, D(4)).expect("D");
1078
1079 let mut sparse_sparse = world
1080 .prepare_query2::<A, C>(QuerySpec::new(), QueryPolicy::Prepared)
1081 .expect("sparse/sparse");
1082 sparse_sparse
1083 .for_each_mut_read(&mut world, QueryWindow::All, |_, a, c| {
1084 a.0 += c.0;
1085 Ok(())
1086 })
1087 .expect("sparse/sparse execute");
1088
1089 let mut table_sparse = world
1090 .prepare_query2::<D, C>(QuerySpec::new(), QueryPolicy::Prepared)
1091 .expect("table/sparse");
1092 table_sparse
1093 .for_each_mut_read(&mut world, QueryWindow::All, |_, d, c| {
1094 d.0 += c.0;
1095 Ok(())
1096 })
1097 .expect("table/sparse execute");
1098
1099 let mut table_table = world
1100 .prepare_query2::<D, B>(QuerySpec::new(), QueryPolicy::Prepared)
1101 .expect("table/table");
1102 table_table
1103 .for_each_mut_read(&mut world, QueryWindow::All, |_, d, b| {
1104 d.0 += b.0;
1105 Ok(())
1106 })
1107 .expect("table/table execute");
1108
1109 assert_eq!(world.get::<A>(entity).expect("get").expect("A"), &A(4));
1110 assert_eq!(world.get::<B>(entity).expect("get").expect("B"), &B(2));
1111 assert_eq!(world.get::<C>(entity).expect("get").expect("C"), &C(3));
1112 assert_eq!(world.get::<D>(entity).expect("get").expect("D"), &D(9));
1113 }
1114
1115 #[test]
1116 fn cursor_commits_only_after_full_iteration() {
1117 let mut world = world();
1118 let entity = world.spawn().expect("spawn");
1119 world.insert(entity, A(1)).expect("A");
1120 let spec = QuerySpec::new().changed::<A>();
1121 let mut query = world
1122 .prepare_query1::<A>(spec.clone(), QueryPolicy::Prepared)
1123 .expect("prepare");
1124 let mut cursor = QueryCursor::from_spec_start::<A>(&mut world, &spec).expect("cursor");
1125 let before = cursor.since();
1126 {
1127 let mut iter = query
1128 .iter(&mut world, QueryWindow::Cursor(&mut cursor))
1129 .expect("iter");
1130 assert!(iter.next().is_some());
1131 }
1132 assert_eq!(cursor.since(), before);
1133
1134 query
1135 .iter(&mut world, QueryWindow::Cursor(&mut cursor))
1136 .expect("iter")
1137 .for_each(drop);
1138 assert!(cursor.since() > before);
1139 }
1140
1141 #[test]
1142 fn result_policy_rejects_moving_windows() {
1143 let mut world = world();
1144 assert!(matches!(
1145 world.prepare_query1::<A>(QuerySpec::new().changed::<A>(), QueryPolicy::Result,),
1146 Err(QueryError::MovingChangeWindow)
1147 ));
1148 }
1149
1150 #[test]
1151 fn query2_cursor_matches_prepared_fingerprint_and_commits() {
1152 let mut world = world();
1153 let entity = world.spawn().expect("spawn");
1154 world.insert(entity, A(1)).expect("A");
1155 world.insert(entity, B(2)).expect("B");
1156 let spec = QuerySpec::new().changed::<A>();
1157 let mut query = world
1158 .prepare_query2::<A, B>(spec.clone(), QueryPolicy::Prepared)
1159 .expect("prepare");
1160 let mut cursor = QueryCursor::from_spec2_start::<A, B>(&mut world, &spec).expect("cursor");
1161 let before = cursor.since();
1162 assert_eq!(
1163 query
1164 .iter(&mut world, QueryWindow::Cursor(&mut cursor))
1165 .expect("iter")
1166 .count(),
1167 1
1168 );
1169 assert!(cursor.since() > before);
1170
1171 let mut wrong = QueryCursor::from_spec_start::<A>(&mut world, &spec).expect("Q1 cursor");
1172 assert!(matches!(
1173 query.iter(&mut world, QueryWindow::Cursor(&mut wrong)),
1174 Err(QueryError::WrongQuery { .. })
1175 ));
1176 }
1177
1178 #[test]
1179 fn query_window_constructors_resolve_the_requested_window() {
1180 let mut world = world();
1181 let spec = QuerySpec::new();
1182 let plan = world.resolve_query1_plan::<A>(&spec).expect("plan");
1183
1184 let (since, cursor) = QueryWindow::all()
1185 .into_parts(&world, plan.fingerprint)
1186 .expect("all");
1187 assert_eq!(since, ChangeTick::ZERO);
1188 assert!(cursor.is_none());
1189
1190 let requested = ChangeTick::from_raw(17);
1191 let (since, cursor) = QueryWindow::since(requested)
1192 .into_parts(&world, plan.fingerprint)
1193 .expect("since");
1194 assert_eq!(since, requested);
1195 assert!(cursor.is_none());
1196
1197 let mut query_cursor =
1198 QueryCursor::from_spec_start::<A>(&mut world, &spec).expect("cursor");
1199 let expected = query_cursor.since();
1200 let (since, cursor) = QueryWindow::cursor(&mut query_cursor)
1201 .into_parts(&world, plan.fingerprint)
1202 .expect("cursor window");
1203 assert_eq!(since, expected);
1204 assert!(cursor.is_some());
1205 }
1206
1207 #[test]
1208 fn delta_update_keeps_an_existing_matching_entity_in_place() {
1209 let mut world = world();
1210 let entity = world.spawn().expect("spawn");
1211 world.insert(entity, A(1)).expect("A");
1212 let plan = world
1213 .resolve_query1_plan::<A>(&QuerySpec::new())
1214 .expect("plan");
1215 let mut state = delta_state(&mut world, &plan);
1216 let before = state.ids.clone();
1217
1218 update_delta_entity(&mut state, &world, &plan, entity);
1219
1220 assert_eq!(state.ids, before);
1221 assert_eq!(state.reverse[entity.slot() as usize], Some(0));
1222 }
1223
1224 #[test]
1225 fn mutation_executors_commit_query_cursors() {
1226 let mut world = world();
1227 let entity = world.spawn().expect("spawn");
1228 world.insert(entity, A(1)).expect("A");
1229 world.insert(entity, B(2)).expect("B");
1230
1231 let q1_spec = QuerySpec::new().changed::<A>();
1232 let mut query1 = world
1233 .prepare_query1::<A>(q1_spec.clone(), QueryPolicy::Prepared)
1234 .expect("prepare Q1");
1235 let mut cursor1 =
1236 QueryCursor::from_spec_start::<A>(&mut world, &q1_spec).expect("Q1 cursor");
1237 let before1 = cursor1.since();
1238 query1
1239 .for_each_mut(&mut world, QueryWindow::cursor(&mut cursor1), |_, a| {
1240 a.0 += 1;
1241 Ok(())
1242 })
1243 .expect("Q1 mutation");
1244 assert!(cursor1.since() > before1);
1245
1246 let q2_spec = QuerySpec::new().changed::<A>();
1247 let mut query2 = world
1248 .prepare_query2::<A, B>(q2_spec.clone(), QueryPolicy::Prepared)
1249 .expect("prepare Q2");
1250 let mut mut_mut_cursor =
1251 QueryCursor::from_spec2_start::<A, B>(&mut world, &q2_spec).expect("mut/mut cursor");
1252 let mut mut_read_cursor =
1253 QueryCursor::from_spec2_start::<A, B>(&mut world, &q2_spec).expect("mut/read cursor");
1254 let before_mut_mut = mut_mut_cursor.since();
1255 let before_mut_read = mut_read_cursor.since();
1256 query2
1257 .for_each_mut_mut(
1258 &mut world,
1259 QueryWindow::cursor(&mut mut_mut_cursor),
1260 |_, a, b| {
1261 a.0 += 1;
1262 b.0 += 1;
1263 Ok(())
1264 },
1265 )
1266 .expect("Q2 mut/mut");
1267 query2
1268 .for_each_mut_read(
1269 &mut world,
1270 QueryWindow::cursor(&mut mut_read_cursor),
1271 |_, a, b| {
1272 a.0 += b.0;
1273 Ok(())
1274 },
1275 )
1276 .expect("Q2 mut/read");
1277 assert!(mut_mut_cursor.since() > before_mut_mut);
1278 assert!(mut_read_cursor.since() > before_mut_read);
1279 }
1280
1281 #[test]
1282 fn mut_read_propagates_callback_errors_without_committing_cursor() {
1283 let mut world = world();
1284 let entity = world.spawn().expect("spawn");
1285 world.insert(entity, A(1)).expect("A");
1286 world.insert(entity, B(2)).expect("B");
1287 let spec = QuerySpec::new().changed::<A>();
1288 let mut query = world
1289 .prepare_query2::<A, B>(spec.clone(), QueryPolicy::Prepared)
1290 .expect("prepare");
1291 let mut cursor = QueryCursor::from_spec2_start::<A, B>(&mut world, &spec).expect("cursor");
1292 let before = cursor.since();
1293
1294 let error = query
1295 .for_each_mut_read(&mut world, QueryWindow::cursor(&mut cursor), |_, _, _| {
1296 Err(QueryError::WrongOwner)
1297 })
1298 .expect_err("callback error");
1299
1300 assert!(matches!(error, QueryError::WrongOwner));
1301 assert_eq!(cursor.since(), before);
1302 }
1303
1304 #[test]
1305 fn prepared_queries_reject_a_foreign_world() {
1306 let mut owner = world();
1307 let mut query1 = owner
1308 .prepare_query1::<A>(QuerySpec::new(), QueryPolicy::Prepared)
1309 .expect("Q1");
1310 let mut query2 = owner
1311 .prepare_query2::<A, B>(QuerySpec::new(), QueryPolicy::Prepared)
1312 .expect("Q2");
1313 let mut foreign = world();
1314
1315 assert!(matches!(
1316 query1.iter(&mut foreign, QueryWindow::All),
1317 Err(QueryError::WrongOwner)
1318 ));
1319 assert!(matches!(
1320 query2.iter(&mut foreign, QueryWindow::All),
1321 Err(QueryError::WrongOwner)
1322 ));
1323 }
1324
1325 #[test]
1326 fn traversal_equality_is_variant_and_payload_sensitive() {
1327 let first = EntityId::from_parts(1, 1);
1328 let second = EntityId::from_parts(2, 1);
1329
1330 assert!(same_traversal(&TraversalSource::All, &TraversalSource::All));
1331 assert!(same_traversal(
1332 &TraversalSource::Sparse { component_index: 3 },
1333 &TraversalSource::Sparse { component_index: 3 }
1334 ));
1335 assert!(!same_traversal(
1336 &TraversalSource::Sparse { component_index: 3 },
1337 &TraversalSource::Sparse { component_index: 4 }
1338 ));
1339 assert!(same_traversal(
1340 &TraversalSource::Table { component_index: 5 },
1341 &TraversalSource::Table { component_index: 5 }
1342 ));
1343 assert!(!same_traversal(
1344 &TraversalSource::Table { component_index: 5 },
1345 &TraversalSource::Table { component_index: 6 }
1346 ));
1347 assert!(same_traversal(
1348 &TraversalSource::Exact {
1349 ids: alloc::vec![first, second]
1350 },
1351 &TraversalSource::Exact {
1352 ids: alloc::vec![first, second]
1353 }
1354 ));
1355 assert!(!same_traversal(
1356 &TraversalSource::Exact {
1357 ids: alloc::vec![first]
1358 },
1359 &TraversalSource::Exact {
1360 ids: alloc::vec![second]
1361 }
1362 ));
1363 assert!(!same_traversal(
1364 &TraversalSource::All,
1365 &TraversalSource::Sparse { component_index: 0 }
1366 ));
1367 }
1368}