Skip to main content

khora_data/ecs/
query.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use khora_core::ecs::entity::EntityId;
16
17use crate::ecs::{
18    page::{AnyVec, ComponentPage},
19    Component, DomainBitset, FieldSoaColumn, QueryMode, QueryPlan, SemanticDomain, SoaLayout,
20    World,
21};
22use std::{any::TypeId, marker::PhantomData};
23
24/// Returns `true` if `row` in `page_id` is the entity's **live** location for
25/// `domain` — i.e. not a stale orphan row left behind by a migration.
26///
27/// `add_component` / `remove_component` move an entity's domain row to a new
28/// page but leave the old row in place (reclaimed later by the maintenance GC),
29/// and the old page's signature is unchanged, so `find_matching_pages` keeps
30/// matching it. Only the row the entity's metadata points to is real; any other
31/// row bearing the same entity is a stale orphan (holding outdated component
32/// values) that must not be yielded. The Native iterators call this per row; the
33/// Transversal path performs the equivalent check inside `Without::fetch_from_world`.
34fn is_live_row(
35    world: &World,
36    page_id: u32,
37    row: usize,
38    entity: EntityId,
39    domain: SemanticDomain,
40) -> bool {
41    world
42        .entities
43        .get(entity.index as usize)
44        .filter(|(slot, _)| slot.generation == entity.generation)
45        .and_then(|(_, meta)| meta.as_ref())
46        .and_then(|meta| meta.locations.get(&domain))
47        .is_some_and(|loc| loc.page_id == page_id && loc.row_index as usize == row)
48}
49
50// ------------------------- //
51// ---- WorldQuery Part ---- //
52// ------------------------- //
53
54/// A trait implemented by types that can be used to query data from the `World`.
55///
56/// This "sealed" trait provides the necessary information for the query engine to
57/// find the correct pages and safely access component data. It is implemented for
58/// component references (`&T`, `&mut T`), `EntityId`, filters like `Without<T>`,
59/// and tuples of other `WorldQuery` types.
60pub trait WorldQuery {
61    /// The type of item that the query iterator will yield (e.g., `(&'a Position, &'a mut Velocity)`).
62    type Item<'a>;
63
64    /// Returns the sorted list of `TypeId`s for the components to be INCLUDED in the query.
65    /// This signature is used to find `ComponentPage`s that contain all these components.
66    fn type_ids() -> Vec<TypeId>;
67
68    /// Returns the sorted list of `TypeId`s for components to be EXCLUDED from the query.
69    /// Used to filter out pages that contain these components.
70    fn without_type_ids() -> Vec<TypeId> {
71        Vec::new()
72    }
73
74    /// Returns the `TypeId`s of the components this query accesses **mutably**
75    /// (`&mut T` / `Option<&mut T>` terms). [`World::query_mut`] uses this to
76    /// mark the matching domains changed once per query construction.
77    /// Read-only terms and filters return nothing (the default).
78    fn mutable_type_ids() -> Vec<TypeId> {
79        Vec::new()
80    }
81
82    /// Fetches the query's item from a specific row in a `ComponentPage`.
83    ///
84    /// # Safety
85    ///
86    /// This function is unsafe because the caller (the `Query` iterator) must guarantee
87    /// several invariants:
88    /// 1. The page pointed to by `page_ptr` is valid and matches the query's signature.
89    /// 2. `row_index` is a valid index within the page's columns.
90    /// 3. Aliasing rules are not violated (e.g., no two `&mut T` to the same data).
91    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a>;
92
93    /// Fetches the query's item directly from the world for a specific entity.
94    ///
95    /// # Safety
96    /// Caller must ensure `world` is valid and no aliasing rules are violated.
97    unsafe fn fetch_from_world<'a>(
98        world: *const World,
99        entity_id: EntityId,
100    ) -> Option<Self::Item<'a>>;
101}
102
103// Implementation for a query of a single, immutable component reference.
104impl<T: Component> WorldQuery for &T {
105    type Item<'a> = &'a T;
106
107    fn type_ids() -> Vec<TypeId> {
108        vec![TypeId::of::<T>()]
109    }
110
111    /// Fetches a reference to the component `T` from the specified row.
112    ///
113    /// # Safety
114    /// The caller MUST guarantee that the page contains a column for component `T`
115    /// and that `row_index` is in bounds.
116    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
117        // 1. Get a reference to the `ComponentPage`.
118        let page = &*page_ptr;
119
120        // 2. Get the type-erased column for the component `T`.
121        // We can unwrap because the caller guarantees the column exists.
122        let column: &dyn AnyVec = &**page.columns.get(&TypeId::of::<T>()).unwrap();
123
124        // 3. Downcast the column to its concrete `Vec<T>` type.
125        // First, cast to `&dyn Any`, then `downcast_ref`.
126        let vec: &Vec<T> = column.as_any().downcast_ref::<Vec<T>>().unwrap();
127
128        // 4. Get the component from the vector at the specified row.
129        // We use `get_unchecked` for performance, as the caller guarantees the
130        // index is in bounds. This avoids a bounds check.
131        vec.get_unchecked(row_index)
132    }
133
134    unsafe fn fetch_from_world<'a>(
135        world: *const World,
136        entity_id: EntityId,
137    ) -> Option<Self::Item<'a>> {
138        let world = &*world;
139
140        // Get the entity's metadata.
141        let metadata = world.entities.get(entity_id.index as usize)?.1.as_ref()?;
142
143        // Get the domain for the component type.
144        let domain = world.storage.registry.get_domain(TypeId::of::<T>())?;
145
146        // Get the location of the entity in the domain.
147        let location = metadata.locations.get(&domain)?;
148
149        // Get the page for the entity.
150        let page = &world.storage.pages[location.page_id as usize];
151        let column = page.columns.get(&TypeId::of::<T>())?;
152        let vec = column.as_any().downcast_ref::<Vec<T>>()?;
153        vec.get(location.row_index as usize)
154    }
155}
156
157// Implementation for a query of a single, mutable component reference.
158impl<T: Component> WorldQuery for &mut T {
159    type Item<'a> = &'a mut T;
160
161    fn type_ids() -> Vec<TypeId> {
162        vec![TypeId::of::<T>()]
163    }
164
165    fn mutable_type_ids() -> Vec<TypeId> {
166        vec![TypeId::of::<T>()]
167    }
168
169    /// Fetches a mutable reference to the component `T` from the specified row.
170    ///
171    /// # Safety
172    /// The caller MUST guarantee that:
173    /// 1. The page contains a column for component `T`.
174    /// 2. `row_index` is in bounds.
175    /// 3. No other mutable reference to this specific component exists at the same time.
176    ///    The query engine must enforce this.
177    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
178        // UNSAFE: We cast the const pointer to a mutable one.
179        // This is safe ONLY if the query engine guarantees no other access.
180        let page = &mut *(page_ptr as *mut ComponentPage);
181        let column = page.columns.get_mut(&TypeId::of::<T>()).unwrap();
182        let vec = column.as_any_mut().downcast_mut::<Vec<T>>().unwrap();
183        vec.get_unchecked_mut(row_index)
184    }
185
186    unsafe fn fetch_from_world<'a>(
187        world: *const World,
188        entity_id: EntityId,
189    ) -> Option<Self::Item<'a>> {
190        let world_mut = &mut *(world as *mut World);
191        let metadata = world_mut
192            .entities
193            .get_mut(entity_id.index as usize)?
194            .1
195            .as_mut()?;
196        let _domain = world_mut.storage.registry.get_domain(TypeId::of::<T>())?;
197        let location = metadata.locations.get(&_domain)?;
198
199        let page = &mut world_mut.storage.pages[location.page_id as usize];
200        let column = page.columns.get_mut(&TypeId::of::<T>())?;
201        let vec = column.as_any_mut().downcast_mut::<Vec<T>>()?;
202        vec.get_mut(location.row_index as usize)
203    }
204}
205
206// Implementation for an optional immutable component reference.
207impl<T: Component> WorldQuery for Option<&T> {
208    type Item<'a> = Option<&'a T>;
209
210    fn type_ids() -> Vec<TypeId> {
211        // Optional components do NOT drive the query signature.
212        Vec::new()
213    }
214
215    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
216        let page = &*page_ptr;
217        let column = page.columns.get(&TypeId::of::<T>())?;
218        let vec = column.as_any().downcast_ref::<Vec<T>>()?;
219        vec.get(row_index)
220    }
221
222    unsafe fn fetch_from_world<'a>(
223        world: *const World,
224        entity_id: EntityId,
225    ) -> Option<Self::Item<'a>> {
226        let world = &*world;
227        let metadata = world.entities.get(entity_id.index as usize)?.1.as_ref()?;
228        let domain = world.storage.registry.get_domain(TypeId::of::<T>())?;
229        let location = metadata.locations.get(&domain)?;
230
231        let page = &world.storage.pages[location.page_id as usize];
232        let column = page.columns.get(&TypeId::of::<T>())?;
233        let vec = column.as_any().downcast_ref::<Vec<T>>()?;
234        // For optional in world, we return Some(Option).
235        // If the component is missing, we return Some(None).
236        Some(vec.get(location.row_index as usize))
237    }
238}
239
240// Implementation for an optional mutable component reference.
241impl<T: Component> WorldQuery for Option<&mut T> {
242    type Item<'a> = Option<&'a mut T>;
243
244    fn type_ids() -> Vec<TypeId> {
245        Vec::new()
246    }
247
248    fn mutable_type_ids() -> Vec<TypeId> {
249        vec![TypeId::of::<T>()]
250    }
251
252    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
253        let page = &mut *(page_ptr as *mut ComponentPage);
254        let column = page.columns.get_mut(&TypeId::of::<T>())?;
255        let vec = column.as_any_mut().downcast_mut::<Vec<T>>()?;
256        vec.get_mut(row_index)
257    }
258
259    unsafe fn fetch_from_world<'a>(
260        world: *const World,
261        entity_id: EntityId,
262    ) -> Option<Self::Item<'a>> {
263        let world_mut = &mut *(world as *mut World);
264        let metadata = world_mut
265            .entities
266            .get_mut(entity_id.index as usize)?
267            .1
268            .as_mut()?;
269        let domain = world_mut.storage.registry.get_domain(TypeId::of::<T>())?;
270        let location = metadata.locations.get(&domain)?;
271
272        let page = &mut world_mut.storage.pages[location.page_id as usize];
273        let column = page.columns.get_mut(&TypeId::of::<T>())?;
274        let vec = column.as_any_mut().downcast_mut::<Vec<T>>()?;
275        // Return Some(Option)
276        Some(vec.get_mut(location.row_index as usize))
277    }
278}
279
280// Implementation for tuples of WorldQuery types.
281// We use a macro to avoid "infinity" of manual implementations while maintaining
282// the same rigorous safety standards as the single-component cases.
283macro_rules! impl_query_tuple {
284    ($($Q:ident),*) => {
285        impl<$($Q: WorldQuery),*> WorldQuery for ($($Q,)*) {
286            type Item<'a> = ($($Q::Item<'a>,)*);
287
288            fn type_ids() -> Vec<TypeId> {
289                let mut ids = Vec::new();
290                $(ids.extend($Q::type_ids());)*
291                ids.sort();
292                ids.dedup(); // Ensure unique TypeIds for canonical signature
293                ids
294            }
295
296            fn without_type_ids() -> Vec<TypeId> {
297                let mut ids = Vec::new();
298                $(ids.extend($Q::without_type_ids());)*
299                ids.sort();
300                ids.dedup(); // Ensure unique TypeIds for canonical signature
301                ids
302            }
303
304            fn mutable_type_ids() -> Vec<TypeId> {
305                let mut ids = Vec::new();
306                $(ids.extend($Q::mutable_type_ids());)*
307                ids.sort();
308                ids.dedup();
309                ids
310            }
311
312            unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
313                ($($Q::fetch(page_ptr, row_index),)*)
314            }
315
316            unsafe fn fetch_from_world<'a>(
317                world: *const World,
318                entity_id: EntityId,
319            ) -> Option<Self::Item<'a>> {
320                Some(($($Q::fetch_from_world(world, entity_id)?,)*))
321            }
322        }
323    };
324}
325
326impl_query_tuple!(Q1);
327impl_query_tuple!(Q1, Q2);
328impl_query_tuple!(Q1, Q2, Q3);
329impl_query_tuple!(Q1, Q2, Q3, Q4);
330impl_query_tuple!(Q1, Q2, Q3, Q4, Q5);
331impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6);
332impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6, Q7);
333impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8);
334impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9);
335impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10);
336impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11);
337impl_query_tuple!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12);
338
339// To fetch an entity's ID, we need to access the page's own entity list.
340// We also need to query for the entity ID itself.
341impl WorldQuery for EntityId {
342    type Item<'a> = EntityId;
343
344    // EntityId is not a component, it doesn't have a TypeId in the page signature.
345    fn type_ids() -> Vec<TypeId> {
346        Vec::new()
347    }
348
349    // It doesn't have a `without` filter either.
350    // The default implementation is sufficient.
351
352    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
353        let page = &*page_ptr;
354        // The entity ID is fetched from the page's own list of entities.
355        *page.entities.get_unchecked(row_index)
356    }
357
358    unsafe fn fetch_from_world<'a>(
359        _world: *const World,
360        entity_id: EntityId,
361    ) -> Option<Self::Item<'a>> {
362        // We already have the entity ID, just return it.
363        Some(entity_id)
364    }
365}
366
367/// A query that reads a field-SoA component **by value**.
368///
369/// A component stored field-SoA (`#[component(layout = "soa")]`) cannot be
370/// borrowed as `&T` — its bytes are split across per-field lanes, not laid out
371/// as a contiguous `T` — so it is queried as `Soa<T>`, which yields an owned
372/// `T` reconstructed (gathered) from the lanes. For the bulk SIMD path that is
373/// the *point* of the layout, use [`World::for_each_soa_column_mut`] instead;
374/// `Soa<T>` is the per-row, mix-with-other-components access.
375///
376/// [`World::for_each_soa_column_mut`]: crate::ecs::World::for_each_soa_column_mut
377pub struct Soa<T: SoaLayout>(PhantomData<T>);
378
379impl<T: SoaLayout> WorldQuery for Soa<T> {
380    type Item<'a> = T;
381
382    fn type_ids() -> Vec<TypeId> {
383        vec![TypeId::of::<T>()]
384    }
385
386    unsafe fn fetch<'a>(page_ptr: *const ComponentPage, row_index: usize) -> Self::Item<'a> {
387        let page = &*page_ptr;
388        let column: &dyn AnyVec = &**page.columns.get(&TypeId::of::<T>()).unwrap();
389        let soa = column
390            .as_any()
391            .downcast_ref::<FieldSoaColumn<T>>()
392            .expect("Soa<T> queried on a component that is not field-SoA stored");
393        soa.get(row_index)
394    }
395
396    unsafe fn fetch_from_world<'a>(
397        world: *const World,
398        entity_id: EntityId,
399    ) -> Option<Self::Item<'a>> {
400        let world = &*world;
401        world.clone_component::<T>(entity_id)
402    }
403}
404
405// -------------------- //
406// ---- Query Part ---- //
407// -------------------- //
408
409/// An iterator that yields the results of a `WorldQuery`.
410///
411/// This struct is created by the [`World::query()`] method. It holds a reference
412/// to the world and iterates through all `ComponentPage`s that match the query's
413/// signature, fetching the requested data for each entity in those pages.
414pub struct Query<'a, Q: WorldQuery> {
415    /// A raw pointer to the world. Using a pointer avoids lifetime variance issues
416    /// and gives us the flexibility to provide either mutable or immutable access.
417    world_ptr: *const World,
418
419    /// The list of page indices that match this query (used in Native mode).
420    matching_page_indices: Vec<u32>,
421
422    /// The execution plan for this query.
423    plan: QueryPlan,
424
425    /// The index of the current page we are iterating through.
426    current_page_index: usize,
427
428    /// The index of the next row to fetch within the current page.
429    current_row_index: usize,
430
431    /// A marker to associate this iterator with the lifetime `'a` and the query type `Q`.
432    /// It tells Rust that our iterator behaves as if it's borrowing `&'a Q` from the world.
433    _phantom: PhantomData<(&'a (), Q)>,
434
435    /// Pre-computed bitset intersection for fast-failing transversal lookups.
436    combined_bitset: Option<DomainBitset>,
437}
438
439impl<'a, Q: WorldQuery> Query<'a, Q> {
440    /// (Internal) Creates a new `Query` iterator.
441    ///
442    /// This is intended to be called only by `World::query()`.
443    /// It takes the world, the plan (strategy), and the pre-calculated list
444    /// of matching pages as arguments.
445    pub(crate) fn new(world: &'a World, plan: QueryPlan, matching_page_indices: Vec<u32>) -> Self {
446        let combined_bitset = world.compute_query_bitset(&plan);
447        Self {
448            world_ptr: world as *const _,
449            matching_page_indices,
450            plan,
451            current_page_index: 0,
452            current_row_index: 0,
453            _phantom: PhantomData,
454            combined_bitset,
455        }
456    }
457}
458
459impl<'a, Q: WorldQuery> Iterator for Query<'a, Q> {
460    /// The type of item yielded by this iterator, as defined by the `WorldQuery` trait.
461    type Item = Q::Item<'a>;
462
463    /// Advances the iterator and returns the next item.
464    fn next(&mut self) -> Option<Self::Item> {
465        // Delegate to the appropriate execution path based on the pre-calculated plan.
466        match self.plan.mode {
467            QueryMode::Native => self.next_native(),
468            QueryMode::Transversal => self.next_transversal(),
469        }
470    }
471}
472
473impl<'a, Q: WorldQuery> Query<'a, Q> {
474    /// (Internal) Performs a "Native" iteration, fetching data from a single domain.
475    /// This is the most efficient execution path.
476    fn next_native(&mut self) -> Option<Q::Item<'a>> {
477        loop {
478            // 1. Check if there are any pages left to iterate through.
479            if self.current_page_index >= self.matching_page_indices.len() {
480                return None; // No more pages, iteration is finished.
481            }
482
483            // SAFETY: `world_ptr` was obtained from the `&'a World` passed to
484            // `Query::new`, and the borrow checker holds that shared borrow alive
485            // for `'a` via `_phantom`. No `&mut World` can exist concurrently, so
486            // reborrowing it as `&World` here is sound.
487            let world = unsafe { &*self.world_ptr };
488
489            // 2. Get the current page.
490            let page_id = self.matching_page_indices[self.current_page_index];
491            let page = &world.storage.pages[page_id as usize];
492
493            // 3. Check if there are rows left in the current page.
494            if self.current_row_index < page.row_count() {
495                let row = self.current_row_index;
496                self.current_row_index += 1;
497
498                // Skip stale orphan rows (see `is_live_row`): `find_matching_pages`
499                // filters by page signature, which a migration leaves unchanged on
500                // the abandoned old page, so an entity can appear in a matched page
501                // it no longer lives in.
502                if let Some(domain) = self.plan.driver_domain {
503                    if !is_live_row(world, page_id, row, page.entities[row], domain) {
504                        continue;
505                    }
506                }
507
508                // SAFETY: `page` lives in `world`, which is borrowed for `'a`, and
509                // `matching_page_indices` only contains pages whose signature
510                // satisfies `Q`, so every column `Q::fetch` reads is present.
511                // `row < page.row_count()` keeps the row in bounds.
512                let item = unsafe { Q::fetch(page as *const _, row) };
513                return Some(item);
514            } else {
515                self.current_page_index += 1;
516                self.current_row_index = 0; // Reset the row index for the new page.
517                                            // The `loop` will then re-evaluate with the new page index.
518            }
519        }
520    }
521
522    /// (Internal) Performs a "Transversal" iteration, joining data across domains.
523    /// This uses the driver domain to find entities and then pulls peer data from other domains.
524    fn next_transversal(&mut self) -> Option<Q::Item<'a>> {
525        loop {
526            if self.current_page_index >= self.matching_page_indices.len() {
527                return None;
528            }
529
530            // SAFETY: same invariant as `next_native` — `world_ptr` came from the
531            // `&'a World` given to `Query::new` and that shared borrow is kept
532            // alive for `'a`, so no aliasing `&mut World` exists.
533            let world = unsafe { &*self.world_ptr };
534            let page_id = self.matching_page_indices[self.current_page_index];
535            let page = &world.storage.pages[page_id as usize];
536
537            if self.current_row_index < page.row_count() {
538                // In transversal mode, we use the EntityId from the driver page to look up
539                // counterpart components in the peer domains.
540                let entity_id = page.entities[self.current_row_index];
541                self.current_row_index += 1;
542
543                // Optimization: Skip metadata lookup if the entity is not in the combined bitset.
544                if let Some(bitset) = &self.combined_bitset {
545                    if !bitset.is_set(entity_id.index) {
546                        continue;
547                    }
548                }
549
550                // SAFETY: `world` is a valid `&World` borrowed for `'a` (see the
551                // deref above); `fetch_from_world` only reads peer columns through
552                // it for an entity that exists in the driver page.
553                if let Some(item) = unsafe { Q::fetch_from_world(world as *const _, entity_id) } {
554                    return Some(item);
555                }
556                // If the join failed for this entity, we continue to the next one.
557            } else {
558                self.current_page_index += 1;
559                self.current_row_index = 0;
560            }
561        }
562    }
563}
564
565/// A `WorldQuery` filter that matches entities that do NOT have component `T`.
566///
567/// This is used as a marker in a query tuple to exclude entities. For example,
568/// `Query<(&Position, Without<Velocity>)>` will iterate over all entities
569/// that have a `Position` but no `Velocity`.
570pub struct Without<T: Component>(PhantomData<T>);
571
572// `Without<T>` itself doesn't fetch any data, so its `WorldQuery` implementation
573// is mostly empty. It acts as a signal to the query engine.
574impl<T: Component> WorldQuery for Without<T> {
575    /// This query item is a zero-sized unit type, as it fetches no data.
576    type Item<'a> = ();
577
578    /// A `Without` filter does not add any component types to the query's main
579    /// signature for page matching. The filtering is handled separately.
580    fn type_ids() -> Vec<TypeId> {
581        Vec::new() // Returns an empty Vec.
582    }
583
584    /// This is the key part of the filter: it returns the TypeId of the component to filter out.
585    fn without_type_ids() -> Vec<TypeId> {
586        // This is the key change: it returns the TypeId of the component to filter out.
587        vec![TypeId::of::<T>()]
588    }
589
590    /// Fetches nothing. Returns a unit type `()`.
591    unsafe fn fetch<'a>(_page_ptr: *const ComponentPage, _row_index: usize) -> Self::Item<'a> {
592        // This function will be called but its result is ignored.
593    }
594
595    unsafe fn fetch_from_world<'a>(
596        world: *const World,
597        entity_id: EntityId,
598    ) -> Option<Self::Item<'a>> {
599        // SAFETY: `fetch_from_world` is an `unsafe fn` whose contract requires
600        // `world` to point to a `World` valid for `'a`; the `Query`/`QueryMut`
601        // callers always pass a pointer derived from their live borrow.
602        let world = unsafe { &*world };
603        let metadata = world.entities.get(entity_id.index as usize)?.1.as_ref()?;
604
605        // Check ALL pages associated with this entity.
606        // If ANY page contains the forbidden component, filtering failed.
607        for location in metadata.locations.values() {
608            let page = &world.storage.pages[location.page_id as usize];
609            if page.type_ids.binary_search(&TypeId::of::<T>()).is_ok() {
610                return None;
611            }
612        }
613        Some(())
614    }
615}
616
617// ------------------------- //
618// ---- QueryMut Part ---- //
619// ------------------------- //
620
621/// An iterator that yields the results of a mutable `WorldQuery`.
622///
623/// This struct is created by the [`World::query_mut()`] method.
624pub struct QueryMut<'a, Q: WorldQuery> {
625    world_ptr: *mut World,
626    matching_page_indices: Vec<u32>,
627    plan: QueryPlan,
628    current_page_index: usize,
629    current_row_index: usize,
630    _phantom: PhantomData<(&'a (), Q)>,
631    /// Pre-computed bitset intersection for fast-failing transversal lookups.
632    combined_bitset: Option<DomainBitset>,
633}
634
635impl<'a, Q: WorldQuery> QueryMut<'a, Q> {
636    /// Creates a new `QueryMut` iterator.
637    ///
638    /// This is intended to be called only by `World::query_mut()`.
639    pub(crate) fn new(
640        world: &'a mut World,
641        plan: QueryPlan,
642        matching_page_indices: Vec<u32>,
643    ) -> Self {
644        let combined_bitset = world.compute_query_bitset(&plan);
645        Self {
646            world_ptr: world as *mut _,
647            matching_page_indices,
648            plan,
649            current_page_index: 0,
650            current_row_index: 0,
651            _phantom: PhantomData,
652            combined_bitset,
653        }
654    }
655}
656
657impl<'a, Q: WorldQuery> Iterator for QueryMut<'a, Q> {
658    type Item = Q::Item<'a>;
659
660    fn next(&mut self) -> Option<Self::Item> {
661        match self.plan.mode {
662            QueryMode::Native => self.next_native(),
663            QueryMode::Transversal => self.next_transversal(),
664        }
665    }
666}
667
668impl<'a, Q: WorldQuery> QueryMut<'a, Q> {
669    fn next_native(&mut self) -> Option<Q::Item<'a>> {
670        loop {
671            if self.current_page_index >= self.matching_page_indices.len() {
672                return None;
673            }
674
675            // SAFETY: `world_ptr` came from the `&'a mut World` passed to
676            // `QueryMut::new`; that exclusive borrow is held for `'a` (via
677            // `_phantom`), so no other reference to the `World` can be observed
678            // while this reborrow lives. Each `next` call drops its `&mut World`
679            // before returning, so reborrows never overlap.
680            let world = unsafe { &mut *self.world_ptr };
681            let page_id = self.matching_page_indices[self.current_page_index] as usize;
682
683            if self.current_row_index < world.storage.pages[page_id].row_count() {
684                let row = self.current_row_index;
685                self.current_row_index += 1;
686
687                // Skip stale orphan rows (see `is_live_row`), through a shared
688                // borrow taken before the `&mut` page below.
689                if let Some(domain) = self.plan.driver_domain {
690                    let entity = world.storage.pages[page_id].entities[row];
691                    if !is_live_row(world, page_id as u32, row, entity, domain) {
692                        continue;
693                    }
694                }
695
696                // We get a mutable reference to the page, which is a safe operation
697                // because `world` is a mutable reference.
698                // SAFETY: `page` is borrowed from the exclusively-held `world`;
699                // `matching_page_indices` only lists pages matching `Q`, so the
700                // columns `Q::fetch` reads (and mutably aliases for `&mut`
701                // queries) are present, and `row` is in bounds.
702                let page = &mut world.storage.pages[page_id];
703                let item = unsafe { Q::fetch(page as *mut _ as *const _, row) };
704                return Some(item);
705            } else {
706                self.current_page_index += 1;
707                self.current_row_index = 0;
708            }
709        }
710    }
711
712    fn next_transversal(&mut self) -> Option<Q::Item<'a>> {
713        loop {
714            if self.current_page_index >= self.matching_page_indices.len() {
715                return None;
716            }
717
718            // SAFETY: same invariant as `next_native` — `world_ptr` came from the
719            // `&'a mut World` given to `QueryMut::new`, that exclusive borrow is
720            // held for `'a`, and each `next` call drops its reborrow before
721            // returning, so no two `&mut World` reborrows overlap.
722            let world = unsafe { &mut *self.world_ptr };
723            let page_id = self.matching_page_indices[self.current_page_index] as usize;
724            let page = &mut world.storage.pages[page_id];
725
726            // Check if there are rows left in the current page.
727            if self.current_row_index < page.row_count() {
728                let entity_id = page.entities[self.current_row_index];
729                self.current_row_index += 1;
730
731                // Skip entities that are not in the combined bitset.
732                if let Some(combined) = &self.combined_bitset {
733                    if !combined.is_set(entity_id.index) {
734                        continue;
735                    }
736                }
737
738                // SAFETY: `world` is the exclusively-borrowed `&mut World` reborrowed
739                // above; passing it as `*const World` to `fetch_from_world` only reads
740                // peer columns for an entity that exists in the driver page.
741                if let Some(item) = unsafe { Q::fetch_from_world(world as *const _, entity_id) } {
742                    return Some(item);
743                }
744            } else {
745                self.current_page_index += 1;
746                self.current_row_index = 0;
747            }
748        }
749    }
750}