Skip to main content

galeon_engine/
query.rs

1// SPDX-License-Identifier: AGPL-3.0-only OR Commercial
2
3use std::any::TypeId;
4use std::marker::PhantomData;
5use std::ops::{Deref, DerefMut};
6
7use crate::archetype::{Archetype, ArchetypeLayout, ArchetypeStore, Column};
8use crate::component::Component;
9use crate::entity::Entity;
10
11/// A smart pointer providing mutable access to a component that defers
12/// change-tick stamping to `DerefMut`. Holding a `Mut<T>` without writing
13/// through it leaves the component's `changed_tick` untouched, so
14/// `query_changed` and incremental extraction see only entities that were
15/// actually mutated.
16///
17/// # Interior mutability
18///
19/// Components that use interior mutability (`AtomicUsize`, `Mutex<T>`, etc.)
20/// can be mutated through `Deref` without triggering `DerefMut`. In that
21/// case, call [`set_changed()`](Mut::set_changed) explicitly to stamp the
22/// change tick and ensure `query_changed` reports the modification.
23pub struct Mut<'w, T> {
24    value: &'w mut T,
25    changed_tick: *mut u64,
26    tick: u64,
27}
28
29impl<T> Mut<'_, T> {
30    /// Manually stamp this component's `changed_tick` at the current tick.
31    ///
32    /// Use this when mutating through interior mutability (e.g., atomics)
33    /// where `DerefMut` is not triggered. Has no effect if the component
34    /// was already stamped by `DerefMut` in the same tick.
35    pub fn set_changed(&mut self) {
36        // SAFETY: same as DerefMut — pointer is valid for this row.
37        unsafe {
38            *self.changed_tick = self.tick;
39        }
40    }
41}
42
43impl<T> Deref for Mut<'_, T> {
44    type Target = T;
45    fn deref(&self) -> &T {
46        self.value
47    }
48}
49
50impl<T> DerefMut for Mut<'_, T> {
51    fn deref_mut(&mut self) -> &mut T {
52        // SAFETY: `changed_tick` points into the same archetype
53        // `Column<T>::changed_ticks` Vec that `value` came from, and the
54        // iterator guarantees unique row access.
55        unsafe {
56            *self.changed_tick = self.tick;
57        }
58        self.value
59    }
60}
61
62/// Describes an immutable query fetch over matching archetypes.
63pub trait QuerySpec {
64    type Item<'w>;
65
66    type State<'w>;
67
68    fn matches(layout: &ArchetypeLayout) -> bool;
69
70    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>>;
71
72    fn len(state: &Self::State<'_>) -> usize;
73
74    fn entity(state: &Self::State<'_>, row: usize) -> Entity;
75
76    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w>;
77}
78
79/// Describes a mutable query fetch over matching archetypes.
80pub trait QuerySpecMut {
81    type Item<'w>;
82
83    type State<'w>;
84
85    fn matches(layout: &ArchetypeLayout) -> bool;
86
87    fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>>;
88
89    fn len(state: &Self::State<'_>) -> usize;
90
91    fn entity(state: &Self::State<'_>, row: usize) -> Entity;
92
93    /// # Safety
94    ///
95    /// Callers must only request each row at most once per live state. Doing
96    /// otherwise could create aliased mutable references into the same
97    /// archetype column data.
98    unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w>;
99}
100
101/// Restricts which archetypes participate in a query.
102pub trait QueryFilter {
103    fn matches(layout: &ArchetypeLayout) -> bool;
104}
105
106/// No-op filter used by plain `query()` / `query_mut()`.
107pub struct NoFilter;
108
109impl QueryFilter for NoFilter {
110    fn matches(_layout: &ArchetypeLayout) -> bool {
111        true
112    }
113}
114
115/// Matches archetypes that contain component `T`.
116pub struct With<T>(PhantomData<T>);
117
118impl<T: Component> QueryFilter for With<T> {
119    fn matches(layout: &ArchetypeLayout) -> bool {
120        layout.contains(TypeId::of::<T>())
121    }
122}
123
124/// Matches archetypes that do not contain component `T`.
125pub struct Without<T>(PhantomData<T>);
126
127impl<T: Component> QueryFilter for Without<T> {
128    fn matches(layout: &ArchetypeLayout) -> bool {
129        !layout.contains(TypeId::of::<T>())
130    }
131}
132
133impl<A, B> QueryFilter for (A, B)
134where
135    A: QueryFilter,
136    B: QueryFilter,
137{
138    fn matches(layout: &ArchetypeLayout) -> bool {
139        A::matches(layout) && B::matches(layout)
140    }
141}
142
143impl<A, B, C> QueryFilter for (A, B, C)
144where
145    A: QueryFilter,
146    B: QueryFilter,
147    C: QueryFilter,
148{
149    fn matches(layout: &ArchetypeLayout) -> bool {
150        A::matches(layout) && B::matches(layout) && C::matches(layout)
151    }
152}
153
154/// Zero-allocation immutable archetype query iterator.
155pub struct QueryIter<'w, Q, F = NoFilter>
156where
157    Q: QuerySpec,
158    F: QueryFilter,
159{
160    store: &'w ArchetypeStore,
161    archetype_index: usize,
162    row: usize,
163    current: Option<Q::State<'w>>,
164    _filter: PhantomData<F>,
165}
166
167impl<'w, Q, F> QueryIter<'w, Q, F>
168where
169    Q: QuerySpec,
170    F: QueryFilter,
171{
172    pub(crate) fn new(store: &'w ArchetypeStore) -> Self {
173        Self {
174            store,
175            archetype_index: 0,
176            row: 0,
177            current: None,
178            _filter: PhantomData,
179        }
180    }
181
182    fn remaining(&self) -> usize {
183        let current = self
184            .current
185            .as_ref()
186            .map_or(0, |state| Q::len(state) - self.row);
187        let future: usize = self
188            .store
189            .iter()
190            .skip(self.archetype_index)
191            .filter(|archetype| Q::matches(archetype.layout()) && F::matches(archetype.layout()))
192            .map(|archetype| archetype.len())
193            .sum();
194        current + future
195    }
196}
197
198impl<'w, Q, F> Iterator for QueryIter<'w, Q, F>
199where
200    Q: QuerySpec + 'w,
201    F: QueryFilter,
202{
203    type Item = (Entity, Q::Item<'w>);
204
205    fn next(&mut self) -> Option<Self::Item> {
206        loop {
207            if let Some(state) = self.current.as_ref() {
208                if self.row < Q::len(state) {
209                    let row = self.row;
210                    self.row += 1;
211                    return Some((Q::entity(state, row), Q::fetch(state, row)));
212                }
213                self.current = None;
214            }
215
216            let archetype = self.store.get_by_index(self.archetype_index)?;
217            self.archetype_index += 1;
218
219            if !Q::matches(archetype.layout()) || !F::matches(archetype.layout()) {
220                continue;
221            }
222
223            self.current = Q::init_state(archetype);
224            self.row = 0;
225        }
226    }
227
228    fn size_hint(&self) -> (usize, Option<usize>) {
229        let remaining = self.remaining();
230        (remaining, Some(remaining))
231    }
232}
233
234impl<'w, Q, F> ExactSizeIterator for QueryIter<'w, Q, F>
235where
236    Q: QuerySpec + 'w,
237    F: QueryFilter,
238{
239    fn len(&self) -> usize {
240        self.remaining()
241    }
242}
243
244/// Zero-allocation mutable archetype query iterator.
245pub struct QueryIterMut<'w, Q, F = NoFilter>
246where
247    Q: QuerySpecMut,
248    F: QueryFilter,
249{
250    store: *mut ArchetypeStore,
251    archetype_len: usize,
252    archetype_index: usize,
253    row: usize,
254    tick: u64,
255    current: Option<Q::State<'w>>,
256    _filter: PhantomData<F>,
257    _marker: PhantomData<&'w mut ArchetypeStore>,
258}
259
260impl<'w, Q, F> QueryIterMut<'w, Q, F>
261where
262    Q: QuerySpecMut,
263    F: QueryFilter,
264{
265    pub(crate) fn new(store: &'w mut ArchetypeStore, tick: u64) -> Self {
266        Self {
267            archetype_len: store.len(),
268            store,
269            archetype_index: 0,
270            row: 0,
271            tick,
272            current: None,
273            _filter: PhantomData,
274            _marker: PhantomData,
275        }
276    }
277
278    /// Construct from a raw pointer without creating `&mut ArchetypeStore`.
279    ///
280    /// This is the `UnsafeWorldCell` path: avoids the intermediate
281    /// `&mut ArchetypeStore` that `new()` requires, eliminating the
282    /// `&ArchetypeStore` / `&mut ArchetypeStore` overlap when `Query<A>`
283    /// and `QueryMut<B>` are fetched concurrently.
284    ///
285    /// # Safety
286    ///
287    /// - `store` must be a valid, non-null pointer to an `ArchetypeStore`
288    ///   that lives for `'w`.
289    /// - The caller must guarantee exclusive mutable access to the columns
290    ///   that `Q` touches (enforced by conflict detection).
291    pub(crate) unsafe fn new_from_ptr(store: *mut ArchetypeStore, tick: u64) -> Self {
292        Self {
293            archetype_len: unsafe { (*store).len() },
294            store,
295            archetype_index: 0,
296            row: 0,
297            tick,
298            current: None,
299            _filter: PhantomData,
300            _marker: PhantomData,
301        }
302    }
303
304    fn remaining(&self) -> usize {
305        let current = self
306            .current
307            .as_ref()
308            .map_or(0, |state| Q::len(state) - self.row);
309        // SAFETY: The iterator owns the mutable store borrow for its entire
310        // lifetime, and this helper only reads future archetype metadata.
311        let future: usize = unsafe { &*self.store }
312            .iter()
313            .skip(self.archetype_index)
314            .filter(|archetype| Q::matches(archetype.layout()) && F::matches(archetype.layout()))
315            .map(|archetype| archetype.len())
316            .sum();
317        current + future
318    }
319}
320
321impl<'w, Q, F> Iterator for QueryIterMut<'w, Q, F>
322where
323    Q: QuerySpecMut + 'w,
324    F: QueryFilter,
325{
326    type Item = (Entity, Q::Item<'w>);
327
328    fn next(&mut self) -> Option<Self::Item> {
329        loop {
330            if let Some(state) = self.current.as_mut() {
331                if self.row < Q::len(state) {
332                    let row = self.row;
333                    self.row += 1;
334                    let entity = Q::entity(state, row);
335                    // SAFETY: `QueryIterMut` only advances forward through a
336                    // single archetype state and never yields the same row
337                    // twice, so mutable references produced for one row cannot
338                    // alias later yields from the same state.
339                    //
340                    // Change-tick stamping is deferred to `Mut<T>::deref_mut()`
341                    // — no eager stamp here.
342                    let item = unsafe { Q::fetch(state, row) };
343                    return Some((entity, item));
344                }
345                self.current = None;
346            }
347
348            if self.archetype_index >= self.archetype_len {
349                return None;
350            }
351
352            // SAFETY: Uses `get_by_index_mut_ptr` which reaches the
353            // `archetypes` Vec via `addr_of_mut!` — no `&mut ArchetypeStore`
354            // is created, only `&mut Vec<Archetype>` at the field level.
355            // This prevents Stacked Borrows invalidation of any concurrent
356            // `&ArchetypeStore` borrows (e.g., from a `Query<A>` that was
357            // fetched before this `QueryMut<B>`).
358            //
359            // `current` is cleared before borrowing a new archetype, so no
360            // mutable state references the archetype being re-borrowed.
361            // Items yielded from prior archetypes carry `&'w mut T` into
362            // the caller, but those point into distinct per-archetype
363            // `Column<T>` heap allocations — different archetypes own
364            // separate column `Vec`s, so references from archetype A never
365            // alias data in archetype B.
366            let archetype =
367                unsafe { ArchetypeStore::get_by_index_mut_ptr(self.store, self.archetype_index)? };
368            self.archetype_index += 1;
369
370            if !Q::matches(archetype.layout()) || !F::matches(archetype.layout()) {
371                continue;
372            }
373
374            self.current = Q::init_state(archetype, self.tick);
375            self.row = 0;
376        }
377    }
378
379    fn size_hint(&self) -> (usize, Option<usize>) {
380        let remaining = self.remaining();
381        (remaining, Some(remaining))
382    }
383}
384
385impl<'w, Q, F> ExactSizeIterator for QueryIterMut<'w, Q, F>
386where
387    Q: QuerySpecMut + 'w,
388    F: QueryFilter,
389{
390    fn len(&self) -> usize {
391        self.remaining()
392    }
393}
394
395pub type Query2Iter<'w, A, B, F = NoFilter> = QueryIter<'w, (&'w A, &'w B), F>;
396pub type Query2MutIter<'w, A, B, F = NoFilter> = QueryIterMut<'w, (&'w mut A, &'w mut B), F>;
397pub type Query3Iter<'w, A, B, C, F = NoFilter> = QueryIter<'w, (&'w A, &'w B, &'w C), F>;
398pub type Query3MutIter<'w, A, B, C, F = NoFilter> =
399    QueryIterMut<'w, (&'w mut A, &'w mut B, &'w mut C), F>;
400
401#[doc(hidden)]
402pub struct OptionalMutState<'w, T> {
403    entities: &'w [Entity],
404    /// Null when the column is absent from the archetype.
405    data: *mut T,
406    /// Null when the column is absent from the archetype.
407    changed_ticks: *mut u64,
408    tick: u64,
409    _marker: PhantomData<&'w mut T>,
410}
411
412#[doc(hidden)]
413pub struct SingleMutState<'w, T> {
414    entities: &'w [Entity],
415    data: *mut T,
416    changed_ticks: *mut u64,
417    tick: u64,
418    len: usize,
419    _marker: PhantomData<&'w mut T>,
420}
421
422#[doc(hidden)]
423pub struct PairMutState<'w, A, B> {
424    entities: &'w [Entity],
425    col_a: *mut A,
426    col_b: *mut B,
427    changed_ticks_a: *mut u64,
428    changed_ticks_b: *mut u64,
429    tick: u64,
430    len: usize,
431    _marker: PhantomData<&'w mut (A, B)>,
432}
433
434#[doc(hidden)]
435pub struct TripleMutState<'w, A, B, C> {
436    entities: &'w [Entity],
437    col_a: *mut A,
438    col_b: *mut B,
439    col_c: *mut C,
440    changed_ticks_a: *mut u64,
441    changed_ticks_b: *mut u64,
442    changed_ticks_c: *mut u64,
443    tick: u64,
444    len: usize,
445    _marker: PhantomData<&'w mut (A, B, C)>,
446}
447
448impl<T: Component> QuerySpec for &T {
449    type Item<'w> = &'w T;
450
451    type State<'w> = (&'w [Entity], &'w Column<T>);
452
453    fn matches(layout: &ArchetypeLayout) -> bool {
454        layout.contains(TypeId::of::<T>())
455    }
456
457    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
458        Some((archetype.entities(), archetype.column::<T>()?))
459    }
460
461    fn len(state: &Self::State<'_>) -> usize {
462        state.0.len()
463    }
464
465    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
466        state.0[row]
467    }
468
469    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
470        state.1.get(row).unwrap()
471    }
472}
473
474/// Optional immutable query: matches all archetypes (never filters), returns
475/// `Some(&T)` when the column is present and `None` when absent.
476impl<T: Component> QuerySpec for Option<&T> {
477    type Item<'w> = Option<&'w T>;
478
479    type State<'w> = (&'w [Entity], Option<&'w Column<T>>);
480
481    fn matches(_layout: &ArchetypeLayout) -> bool {
482        true
483    }
484
485    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
486        Some((archetype.entities(), archetype.column::<T>()))
487    }
488
489    fn len(state: &Self::State<'_>) -> usize {
490        state.0.len()
491    }
492
493    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
494        state.0[row]
495    }
496
497    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
498        state.1.as_ref().and_then(|col| col.get(row))
499    }
500}
501
502impl<A: Component, B: Component> QuerySpec for (&A, &B) {
503    type Item<'w> = (&'w A, &'w B);
504
505    type State<'w> = (&'w [Entity], &'w Column<A>, &'w Column<B>);
506
507    fn matches(layout: &ArchetypeLayout) -> bool {
508        layout.contains(TypeId::of::<A>()) && layout.contains(TypeId::of::<B>())
509    }
510
511    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
512        Some((
513            archetype.entities(),
514            archetype.column::<A>()?,
515            archetype.column::<B>()?,
516        ))
517    }
518
519    fn len(state: &Self::State<'_>) -> usize {
520        state.0.len()
521    }
522
523    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
524        state.0[row]
525    }
526
527    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
528        (state.1.get(row).unwrap(), state.2.get(row).unwrap())
529    }
530}
531
532impl<A: Component, B: Component> QuerySpec for (&A, Option<&B>) {
533    type Item<'w> = (&'w A, Option<&'w B>);
534
535    type State<'w> = (&'w [Entity], &'w Column<A>, Option<&'w Column<B>>);
536
537    fn matches(layout: &ArchetypeLayout) -> bool {
538        layout.contains(TypeId::of::<A>())
539    }
540
541    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
542        Some((
543            archetype.entities(),
544            archetype.column::<A>()?,
545            archetype.column::<B>(),
546        ))
547    }
548
549    fn len(state: &Self::State<'_>) -> usize {
550        state.0.len()
551    }
552
553    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
554        state.0[row]
555    }
556
557    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
558        (
559            state.1.get(row).unwrap(),
560            state.2.as_ref().and_then(|col| col.get(row)),
561        )
562    }
563}
564
565impl<A: Component, B: Component, C: Component> QuerySpec for (&A, &B, &C) {
566    type Item<'w> = (&'w A, &'w B, &'w C);
567
568    type State<'w> = (&'w [Entity], &'w Column<A>, &'w Column<B>, &'w Column<C>);
569
570    fn matches(layout: &ArchetypeLayout) -> bool {
571        layout.contains(TypeId::of::<A>())
572            && layout.contains(TypeId::of::<B>())
573            && layout.contains(TypeId::of::<C>())
574    }
575
576    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
577        Some((
578            archetype.entities(),
579            archetype.column::<A>()?,
580            archetype.column::<B>()?,
581            archetype.column::<C>()?,
582        ))
583    }
584
585    fn len(state: &Self::State<'_>) -> usize {
586        state.0.len()
587    }
588
589    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
590        state.0[row]
591    }
592
593    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
594        (
595            state.1.get(row).unwrap(),
596            state.2.get(row).unwrap(),
597            state.3.get(row).unwrap(),
598        )
599    }
600}
601
602impl<A: Component, B: Component, C: Component, D: Component> QuerySpec
603    for (&A, Option<&B>, Option<&C>, Option<&D>)
604{
605    type Item<'w> = (&'w A, Option<&'w B>, Option<&'w C>, Option<&'w D>);
606
607    type State<'w> = (
608        &'w [Entity],
609        &'w Column<A>,
610        Option<&'w Column<B>>,
611        Option<&'w Column<C>>,
612        Option<&'w Column<D>>,
613    );
614
615    fn matches(layout: &ArchetypeLayout) -> bool {
616        layout.contains(TypeId::of::<A>())
617    }
618
619    fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
620        Some((
621            archetype.entities(),
622            archetype.column::<A>()?,
623            archetype.column::<B>(),
624            archetype.column::<C>(),
625            archetype.column::<D>(),
626        ))
627    }
628
629    fn len(state: &Self::State<'_>) -> usize {
630        state.0.len()
631    }
632
633    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
634        state.0[row]
635    }
636
637    fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
638        (
639            state.1.get(row).unwrap(),
640            state.2.as_ref().and_then(|col| col.get(row)),
641            state.3.as_ref().and_then(|col| col.get(row)),
642            state.4.as_ref().and_then(|col| col.get(row)),
643        )
644    }
645}
646
647impl<T: Component> QuerySpecMut for &mut T {
648    type Item<'w> = Mut<'w, T>;
649
650    type State<'w> = SingleMutState<'w, T>;
651
652    fn matches(layout: &ArchetypeLayout) -> bool {
653        layout.contains(TypeId::of::<T>())
654    }
655
656    fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
657        let (entities, column) = archetype.entities_and_column_mut::<T>()?;
658        Some(SingleMutState {
659            entities,
660            data: column.as_mut_ptr(),
661            changed_ticks: column.changed_ticks_mut_ptr(),
662            tick,
663            len: column.len(),
664            _marker: PhantomData,
665        })
666    }
667
668    fn len(state: &Self::State<'_>) -> usize {
669        state.len
670    }
671
672    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
673        state.entities[row]
674    }
675
676    unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
677        // SAFETY: `QueryIterMut` guarantees that each row is yielded at most
678        // once, so the mutable reference to this slot cannot alias another
679        // live reference produced by the same iterator. The `changed_ticks`
680        // pointer points into the same column and is valid for this row.
681        unsafe {
682            Mut {
683                value: &mut *state.data.add(row),
684                changed_tick: state.changed_ticks.add(row),
685                tick: state.tick,
686            }
687        }
688    }
689}
690
691/// Optional mutable query: matches all archetypes (never filters), returns
692/// `Some(&mut T)` when the column is present and `None` when absent.
693impl<T: Component> QuerySpecMut for Option<&mut T> {
694    type Item<'w> = Option<Mut<'w, T>>;
695
696    type State<'w> = OptionalMutState<'w, T>;
697
698    fn matches(_layout: &ArchetypeLayout) -> bool {
699        true
700    }
701
702    fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
703        let entities = archetype.entities() as *const [Entity];
704        let (data, changed_ticks) = match archetype.column_mut::<T>() {
705            Some(col) => (col.as_mut_ptr(), col.changed_ticks_mut_ptr()),
706            None => (std::ptr::null_mut(), std::ptr::null_mut()),
707        };
708        // SAFETY: `entities` pointer is valid for the lifetime of the archetype
709        // borrow, which is `'w`.
710        Some(OptionalMutState {
711            entities: unsafe { &*entities },
712            data,
713            changed_ticks,
714            tick,
715            _marker: PhantomData,
716        })
717    }
718
719    fn len(state: &Self::State<'_>) -> usize {
720        state.entities.len()
721    }
722
723    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
724        state.entities[row]
725    }
726
727    unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
728        if state.data.is_null() {
729            None
730        } else {
731            // SAFETY: `QueryIterMut` guarantees each row is yielded at most once.
732            unsafe {
733                Some(Mut {
734                    value: &mut *state.data.add(row),
735                    changed_tick: state.changed_ticks.add(row),
736                    tick: state.tick,
737                })
738            }
739        }
740    }
741}
742
743impl<A: Component, B: Component> QuerySpecMut for (&mut A, &mut B) {
744    type Item<'w> = (Mut<'w, A>, Mut<'w, B>);
745
746    type State<'w> = PairMutState<'w, A, B>;
747
748    fn matches(layout: &ArchetypeLayout) -> bool {
749        layout.contains(TypeId::of::<A>()) && layout.contains(TypeId::of::<B>())
750    }
751
752    fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
753        let (entities, col_a, col_b) = archetype.entities_and_two_columns_mut::<A, B>()?;
754        Some(PairMutState {
755            entities,
756            col_a: col_a.as_mut_ptr(),
757            col_b: col_b.as_mut_ptr(),
758            changed_ticks_a: col_a.changed_ticks_mut_ptr(),
759            changed_ticks_b: col_b.changed_ticks_mut_ptr(),
760            tick,
761            len: entities.len(),
762            _marker: PhantomData,
763        })
764    }
765
766    fn len(state: &Self::State<'_>) -> usize {
767        state.len
768    }
769
770    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
771        state.entities[row]
772    }
773
774    unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
775        // SAFETY: `entities_and_two_columns_mut` guarantees distinct columns
776        // and `QueryIterMut` guarantees each row is yielded once.
777        unsafe {
778            (
779                Mut {
780                    value: &mut *state.col_a.add(row),
781                    changed_tick: state.changed_ticks_a.add(row),
782                    tick: state.tick,
783                },
784                Mut {
785                    value: &mut *state.col_b.add(row),
786                    changed_tick: state.changed_ticks_b.add(row),
787                    tick: state.tick,
788                },
789            )
790        }
791    }
792}
793
794impl<A: Component, B: Component, C: Component> QuerySpecMut for (&mut A, &mut B, &mut C) {
795    type Item<'w> = (Mut<'w, A>, Mut<'w, B>, Mut<'w, C>);
796
797    type State<'w> = TripleMutState<'w, A, B, C>;
798
799    fn matches(layout: &ArchetypeLayout) -> bool {
800        layout.contains(TypeId::of::<A>())
801            && layout.contains(TypeId::of::<B>())
802            && layout.contains(TypeId::of::<C>())
803    }
804
805    fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
806        let (entities, col_a, col_b, col_c) =
807            archetype.entities_and_three_columns_mut::<A, B, C>()?;
808        Some(TripleMutState {
809            entities,
810            col_a: col_a.as_mut_ptr(),
811            col_b: col_b.as_mut_ptr(),
812            col_c: col_c.as_mut_ptr(),
813            changed_ticks_a: col_a.changed_ticks_mut_ptr(),
814            changed_ticks_b: col_b.changed_ticks_mut_ptr(),
815            changed_ticks_c: col_c.changed_ticks_mut_ptr(),
816            tick,
817            len: entities.len(),
818            _marker: PhantomData,
819        })
820    }
821
822    fn len(state: &Self::State<'_>) -> usize {
823        state.len
824    }
825
826    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
827        state.entities[row]
828    }
829
830    unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
831        // SAFETY: `entities_and_three_columns_mut` guarantees three distinct
832        // columns and `QueryIterMut` yields each row at most once.
833        unsafe {
834            (
835                Mut {
836                    value: &mut *state.col_a.add(row),
837                    changed_tick: state.changed_ticks_a.add(row),
838                    tick: state.tick,
839                },
840                Mut {
841                    value: &mut *state.col_b.add(row),
842                    changed_tick: state.changed_ticks_b.add(row),
843                    tick: state.tick,
844                },
845                Mut {
846                    value: &mut *state.col_c.add(row),
847                    changed_tick: state.changed_ticks_c.add(row),
848                    tick: state.tick,
849                },
850            )
851        }
852    }
853}
854
855// =============================================================================
856// Change-detection iterators
857// =============================================================================
858
859/// Iterator yielding entities whose component `T` has a `changed_tick > since_tick`.
860pub struct ChangedIter<'w, T: Component> {
861    store: &'w ArchetypeStore,
862    archetype_index: usize,
863    row: usize,
864    since_tick: u64,
865    current: Option<(&'w [Entity], &'w Column<T>)>,
866}
867
868impl<'w, T: Component> ChangedIter<'w, T> {
869    pub(crate) fn new(store: &'w ArchetypeStore, since_tick: u64) -> Self {
870        Self {
871            store,
872            archetype_index: 0,
873            row: 0,
874            since_tick,
875            current: None,
876        }
877    }
878}
879
880impl<'w, T: Component> Iterator for ChangedIter<'w, T> {
881    type Item = (Entity, &'w T);
882
883    fn next(&mut self) -> Option<Self::Item> {
884        loop {
885            if let Some((entities, col)) = &self.current {
886                while self.row < entities.len() {
887                    let row = self.row;
888                    self.row += 1;
889                    if col.changed_tick(row) > self.since_tick {
890                        return Some((entities[row], col.get(row).unwrap()));
891                    }
892                }
893                self.current = None;
894            }
895
896            let archetype = self.store.get_by_index(self.archetype_index)?;
897            self.archetype_index += 1;
898
899            if !archetype.layout().contains(TypeId::of::<T>()) {
900                continue;
901            }
902
903            if let (Some(col), entities) = (archetype.column::<T>(), archetype.entities()) {
904                self.current = Some((entities, col));
905                self.row = 0;
906            }
907        }
908    }
909}
910
911/// Iterator yielding entities whose component `T` has an `added_tick > since_tick`.
912pub struct AddedIter<'w, T: Component> {
913    store: &'w ArchetypeStore,
914    archetype_index: usize,
915    row: usize,
916    since_tick: u64,
917    current: Option<(&'w [Entity], &'w Column<T>)>,
918}
919
920impl<'w, T: Component> AddedIter<'w, T> {
921    pub(crate) fn new(store: &'w ArchetypeStore, since_tick: u64) -> Self {
922        Self {
923            store,
924            archetype_index: 0,
925            row: 0,
926            since_tick,
927            current: None,
928        }
929    }
930}
931
932impl<'w, T: Component> Iterator for AddedIter<'w, T> {
933    type Item = (Entity, &'w T);
934
935    fn next(&mut self) -> Option<Self::Item> {
936        loop {
937            if let Some((entities, col)) = &self.current {
938                while self.row < entities.len() {
939                    let row = self.row;
940                    self.row += 1;
941                    if col.added_tick(row) > self.since_tick {
942                        return Some((entities[row], col.get(row).unwrap()));
943                    }
944                }
945                self.current = None;
946            }
947
948            let archetype = self.store.get_by_index(self.archetype_index)?;
949            self.archetype_index += 1;
950
951            if !archetype.layout().contains(TypeId::of::<T>()) {
952                continue;
953            }
954
955            if let (Some(col), entities) = (archetype.column::<T>(), archetype.entities()) {
956                self.current = Some((entities, col));
957                self.row = 0;
958            }
959        }
960    }
961}
962
963#[doc(hidden)]
964pub struct PairMutOptionalState<'w, A, B> {
965    entities: &'w [Entity],
966    col_a: *mut A,
967    col_b: *mut B,
968    changed_ticks_a: *mut u64,
969    changed_ticks_b: *mut u64,
970    tick: u64,
971    len: usize,
972    _marker: PhantomData<&'w mut (A, B)>,
973}
974
975impl<A: Component, B: Component> QuerySpecMut for (&mut A, Option<&mut B>) {
976    type Item<'w> = (Mut<'w, A>, Option<Mut<'w, B>>);
977
978    type State<'w> = PairMutOptionalState<'w, A, B>;
979
980    fn matches(layout: &ArchetypeLayout) -> bool {
981        layout.contains(TypeId::of::<A>())
982    }
983
984    fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
985        assert_ne!(
986            TypeId::of::<A>(),
987            TypeId::of::<B>(),
988            "cannot borrow the same column mutably twice"
989        );
990        let (entities, col_a, col_b) =
991            archetype.entities_and_required_optional_columns_mut::<A, B>()?;
992        let col_a_ptr = col_a.as_mut_ptr();
993        let changed_ticks_a = col_a.changed_ticks_mut_ptr();
994        let len = col_a.len();
995        let (col_b_ptr, changed_ticks_b) = match col_b {
996            Some(col) => (col.as_mut_ptr(), col.changed_ticks_mut_ptr()),
997            None => (std::ptr::null_mut(), std::ptr::null_mut()),
998        };
999        Some(PairMutOptionalState {
1000            entities,
1001            col_a: col_a_ptr,
1002            col_b: col_b_ptr,
1003            changed_ticks_a,
1004            changed_ticks_b,
1005            tick,
1006            len,
1007            _marker: PhantomData,
1008        })
1009    }
1010
1011    fn len(state: &Self::State<'_>) -> usize {
1012        state.len
1013    }
1014
1015    fn entity(state: &Self::State<'_>, row: usize) -> Entity {
1016        state.entities[row]
1017    }
1018
1019    unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
1020        // SAFETY: `QueryIterMut` guarantees each row is yielded at most once.
1021        // A and B are distinct types, so column pointers cannot alias.
1022        unsafe {
1023            let a = Mut {
1024                value: &mut *state.col_a.add(row),
1025                changed_tick: state.changed_ticks_a.add(row),
1026                tick: state.tick,
1027            };
1028            let b = if state.col_b.is_null() {
1029                None
1030            } else {
1031                Some(Mut {
1032                    value: &mut *state.col_b.add(row),
1033                    changed_tick: state.changed_ticks_b.add(row),
1034                    tick: state.tick,
1035                })
1036            };
1037            (a, b)
1038        }
1039    }
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044    use crate::World;
1045
1046    #[derive(Debug, Clone, PartialEq)]
1047    struct Pos {
1048        x: f32,
1049        y: f32,
1050    }
1051    impl crate::Component for Pos {}
1052
1053    #[derive(Debug, Clone, PartialEq)]
1054    struct Vel {
1055        x: f32,
1056        y: f32,
1057    }
1058    impl crate::Component for Vel {}
1059
1060    #[derive(Debug, Clone, PartialEq)]
1061    struct Name(String);
1062    impl crate::Component for Name {}
1063
1064    #[derive(Debug, Clone, PartialEq)]
1065    struct Health(i32);
1066    impl crate::Component for Health {}
1067
1068    // -- Option<&T> standalone --
1069
1070    #[test]
1071    fn optional_query_returns_some_when_present() {
1072        let mut world = World::new();
1073        world.spawn((Pos { x: 1.0, y: 2.0 },));
1074
1075        let results: Vec<_> = world.query::<Option<&Pos>>().collect();
1076        assert_eq!(results.len(), 1);
1077        assert_eq!(results[0].1, Some(&Pos { x: 1.0, y: 2.0 }));
1078    }
1079
1080    #[test]
1081    fn optional_query_returns_none_when_absent() {
1082        let mut world = World::new();
1083        world.spawn((Vel { x: 1.0, y: 0.0 },));
1084
1085        let results: Vec<_> = world.query::<Option<&Pos>>().collect();
1086        assert_eq!(results.len(), 1);
1087        assert_eq!(results[0].1, None);
1088    }
1089
1090    // -- (&A, Option<&B>) tuple --
1091
1092    #[test]
1093    fn required_plus_optional_both_present() {
1094        let mut world = World::new();
1095        world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 2.0, y: 0.0 }));
1096
1097        let results: Vec<_> = world.query::<(&Pos, Option<&Vel>)>().collect();
1098        assert_eq!(results.len(), 1);
1099        assert_eq!(results[0].1.0, &Pos { x: 1.0, y: 0.0 });
1100        assert_eq!(results[0].1.1, Some(&Vel { x: 2.0, y: 0.0 }));
1101    }
1102
1103    #[test]
1104    fn required_plus_optional_absent() {
1105        let mut world = World::new();
1106        world.spawn((Pos { x: 1.0, y: 0.0 },));
1107
1108        let results: Vec<_> = world.query::<(&Pos, Option<&Vel>)>().collect();
1109        assert_eq!(results.len(), 1);
1110        assert_eq!(results[0].1.1, None);
1111    }
1112
1113    #[test]
1114    fn required_plus_optional_filters_by_required() {
1115        let mut world = World::new();
1116        world.spawn((Pos { x: 1.0, y: 0.0 },));
1117        world.spawn((Vel { x: 2.0, y: 0.0 },)); // no Pos — should NOT appear
1118
1119        let results: Vec<_> = world.query::<(&Pos, Option<&Vel>)>().collect();
1120        assert_eq!(results.len(), 1);
1121    }
1122
1123    // -- (&A, Option<&B>, Option<&C>, Option<&D>) 4-tuple --
1124
1125    #[test]
1126    fn four_tuple_all_present() {
1127        let mut world = World::new();
1128        world.spawn((
1129            Pos { x: 1.0, y: 0.0 },
1130            Vel { x: 2.0, y: 0.0 },
1131            Name("a".into()),
1132            Health(100),
1133        ));
1134
1135        let results: Vec<_> = world
1136            .query::<(&Pos, Option<&Vel>, Option<&Name>, Option<&Health>)>()
1137            .collect();
1138        assert_eq!(results.len(), 1);
1139        let (pos, vel, name, hp) = &results[0].1;
1140        assert_eq!(*pos, &Pos { x: 1.0, y: 0.0 });
1141        assert!(vel.is_some());
1142        assert!(name.is_some());
1143        assert!(hp.is_some());
1144    }
1145
1146    #[test]
1147    fn four_tuple_mixed_presence() {
1148        let mut world = World::new();
1149        // Entity with Pos + Health only
1150        world.spawn((Pos { x: 1.0, y: 0.0 }, Health(50)));
1151        // Entity with Pos + Vel + Name only
1152        world.spawn((
1153            Pos { x: 2.0, y: 0.0 },
1154            Vel { x: 1.0, y: 0.0 },
1155            Name("b".into()),
1156        ));
1157
1158        let mut results: Vec<_> = world
1159            .query::<(&Pos, Option<&Vel>, Option<&Name>, Option<&Health>)>()
1160            .map(|(_, (pos, vel, name, hp))| (pos.x, vel.is_some(), name.is_some(), hp.is_some()))
1161            .collect();
1162        results.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
1163
1164        assert_eq!(results.len(), 2);
1165        assert_eq!(results[0], (1.0, false, false, true));
1166        assert_eq!(results[1], (2.0, true, true, false));
1167    }
1168
1169    // -- Option<&mut T> --
1170
1171    #[test]
1172    fn optional_mut_modifies_when_present() {
1173        let mut world = World::new();
1174        world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 0.0, y: 0.0 }));
1175
1176        for (_, vel) in world.query_mut::<Option<&mut Vel>>() {
1177            if let Some(mut v) = vel {
1178                v.x += 10.0;
1179            }
1180        }
1181
1182        let vel = world.query::<&Vel>().next().unwrap().1;
1183        assert_eq!(vel.x, 10.0);
1184    }
1185
1186    #[test]
1187    fn optional_mut_skips_when_absent() {
1188        let mut world = World::new();
1189        world.spawn((Pos { x: 1.0, y: 0.0 },));
1190
1191        let mut count = 0;
1192        for (_, vel) in world.query_mut::<Option<&mut Vel>>() {
1193            count += 1;
1194            assert!(vel.is_none());
1195        }
1196        assert_eq!(count, 1);
1197    }
1198
1199    // -- (&mut A, Option<&mut B>) --
1200
1201    #[test]
1202    fn required_mut_plus_optional_mut() {
1203        let mut world = World::new();
1204        world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 0.0, y: 0.0 }));
1205        world.spawn((Pos { x: 2.0, y: 0.0 },)); // no Vel
1206
1207        for (_, (mut pos, vel)) in world.query_mut::<(&mut Pos, Option<&mut Vel>)>() {
1208            pos.x += 100.0;
1209            if let Some(mut v) = vel {
1210                v.x += 50.0;
1211            }
1212        }
1213
1214        let mut results: Vec<_> = world.query::<&Pos>().map(|(_, p)| p.x).collect();
1215        results.sort_by(|a, b| a.partial_cmp(b).unwrap());
1216        assert_eq!(results, vec![101.0, 102.0]);
1217
1218        let vel = world.query::<&Vel>().next().unwrap().1;
1219        assert_eq!(vel.x, 50.0);
1220    }
1221
1222    // -- Cross-archetype iteration --
1223
1224    #[test]
1225    fn optional_query_spans_multiple_archetypes() {
1226        let mut world = World::new();
1227        // Archetype 1: Pos only
1228        world.spawn((Pos { x: 1.0, y: 0.0 },));
1229        // Archetype 2: Pos + Vel
1230        world.spawn((Pos { x: 2.0, y: 0.0 }, Vel { x: 5.0, y: 0.0 }));
1231        // Archetype 3: Pos + Name
1232        world.spawn((Pos { x: 3.0, y: 0.0 }, Name("c".into())));
1233
1234        let mut results: Vec<_> = world
1235            .query::<(&Pos, Option<&Vel>)>()
1236            .map(|(_, (pos, vel))| (pos.x, vel.map(|v| v.x)))
1237            .collect();
1238        results.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
1239
1240        assert_eq!(results.len(), 3);
1241        assert_eq!(results[0], (1.0, None));
1242        assert_eq!(results[1], (2.0, Some(5.0)));
1243        assert_eq!(results[2], (3.0, None));
1244    }
1245}