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}