ixa/entity/query/
query_impls.rs

1use std::any::TypeId;
2
3use seq_macro::seq;
4
5use crate::entity::entity_set::{EntitySet, EntitySetIterator, SourceSet};
6use crate::entity::index::IndexSetResult;
7use crate::entity::multi_property::static_reorder_by_keys;
8use crate::entity::property::Property;
9use crate::entity::{ContextEntitiesExt, Entity, EntityId, HashValueType, Query};
10use crate::hashing::one_shot_128;
11use crate::Context;
12
13impl<E: Entity> Query<E> for () {
14    fn get_query(&self) -> Vec<(usize, HashValueType)> {
15        Vec::new()
16    }
17
18    fn get_type_ids(&self) -> Vec<TypeId> {
19        Vec::new()
20    }
21
22    fn multi_property_id(&self) -> Option<usize> {
23        None
24    }
25
26    fn multi_property_value_hash(&self) -> HashValueType {
27        let empty: &[u128] = &[];
28        one_shot_128(&empty)
29    }
30
31    fn new_query_result<'c>(&self, context: &'c Context) -> EntitySet<'c, E> {
32        EntitySet::from_source(SourceSet::Population(context.get_entity_count::<E>()))
33    }
34
35    fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
36        EntitySetIterator::from_population_iterator(context.get_entity_iterator::<E>())
37    }
38
39    fn match_entity(&self, _entity_id: EntityId<E>, _context: &Context) -> bool {
40        // Every entity matches the empty query.
41        true
42    }
43
44    fn filter_entities(&self, _entities: &mut Vec<EntityId<E>>, _context: &Context) {
45        // Nothing to do.
46    }
47}
48
49// An Entity ZST itself is an empty query matching all entities of that type.
50// This allows `context.sample_entity(Rng, Person)` instead of `context.sample_entity(Rng, ())`.
51impl<E: Entity + Copy> Query<E> for E {
52    fn get_query(&self) -> Vec<(usize, HashValueType)> {
53        Vec::new()
54    }
55
56    fn get_type_ids(&self) -> Vec<TypeId> {
57        Vec::new()
58    }
59
60    fn multi_property_id(&self) -> Option<usize> {
61        None
62    }
63
64    fn multi_property_value_hash(&self) -> HashValueType {
65        let empty: &[u128] = &[];
66        one_shot_128(&empty)
67    }
68
69    fn new_query_result<'c>(&self, context: &'c Context) -> EntitySet<'c, E> {
70        EntitySet::from_source(SourceSet::Population(context.get_entity_count::<E>()))
71    }
72
73    fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
74        let population_iterator = context.get_entity_iterator::<E>();
75        EntitySetIterator::from_population_iterator(population_iterator)
76    }
77
78    fn match_entity(&self, _entity_id: EntityId<E>, _context: &Context) -> bool {
79        true
80    }
81
82    fn filter_entities(&self, _entities: &mut Vec<EntityId<E>>, _context: &Context) {
83        // Nothing to do.
84    }
85}
86
87// Implement the query version with one parameter.
88impl<E: Entity, P1: Property<E>> Query<E> for (P1,) {
89    fn get_query(&self) -> Vec<(usize, HashValueType)> {
90        let value = P1::make_canonical(self.0);
91        vec![(P1::id(), P1::hash_property_value(&value))]
92    }
93
94    fn get_type_ids(&self) -> Vec<TypeId> {
95        vec![P1::type_id()]
96    }
97
98    fn multi_property_id(&self) -> Option<usize> {
99        // While not a "true" multi-property, it is convenient to have this method return the
100        // `TypeId` of the singleton property.
101        Some(P1::index_id())
102    }
103
104    fn multi_property_value_hash(&self) -> HashValueType {
105        P1::hash_property_value(&P1::make_canonical(self.0))
106    }
107
108    fn new_query_result<'c>(&self, context: &'c Context) -> EntitySet<'c, E> {
109        let property_store = context.entity_store.get_property_store::<E>();
110
111        // The case of an indexed multi-property.
112        // This mirrors the indexed case in `SourceSet<'a, E>::new()`. The difference is, if the
113        // multi-property is unindexed, we fall through to create `SourceSet`s for the components
114        // rather than wrapping a `DerivedPropertySource`.
115        if let Some(multi_property_id) = self.multi_property_id() {
116            match property_store.get_index_set_with_hash_for_property_id(
117                multi_property_id,
118                self.multi_property_value_hash(),
119            ) {
120                IndexSetResult::Set(people_set) => {
121                    return EntitySet::from_source(SourceSet::IndexSet(people_set));
122                }
123                IndexSetResult::Empty => {
124                    return EntitySet::empty();
125                }
126                IndexSetResult::Unsupported => {}
127            }
128            // If the property is not indexed, we fall through.
129        }
130
131        // We create a source set for each property.
132        let mut sources: Vec<SourceSet<E>> = Vec::new();
133
134        if let Some(source_set) = SourceSet::new::<P1>(self.0, context) {
135            sources.push(source_set);
136        } else {
137            // If a single source set is empty, the intersection of all sources is empty.
138            return EntitySet::empty();
139        }
140
141        EntitySet::from_intersection_sources(sources)
142    }
143
144    fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
145        // Constructing the `EntitySetIterator` directly instead of constructing an `EntitySet`
146        // first is a micro-optimization improving tight-loop benchmark performance.
147        let property_store = context.entity_store.get_property_store::<E>();
148
149        if let Some(multi_property_id) = self.multi_property_id() {
150            match property_store.get_index_set_with_hash_for_property_id(
151                multi_property_id,
152                self.multi_property_value_hash(),
153            ) {
154                IndexSetResult::Set(people_set) => {
155                    return EntitySetIterator::from_index_set(people_set);
156                }
157                IndexSetResult::Empty => {
158                    return EntitySetIterator::empty();
159                }
160                IndexSetResult::Unsupported => {}
161            }
162        }
163
164        let mut sources: Vec<SourceSet<E>> = Vec::new();
165
166        if let Some(source_set) = SourceSet::new::<P1>(self.0, context) {
167            sources.push(source_set);
168        } else {
169            return EntitySetIterator::empty();
170        }
171
172        EntitySetIterator::from_sources(sources)
173    }
174
175    fn match_entity(&self, entity_id: EntityId<E>, context: &Context) -> bool {
176        let found_value: P1 = context.get_property(entity_id);
177        found_value == self.0
178    }
179
180    fn filter_entities(&self, entities: &mut Vec<EntityId<E>>, context: &Context) {
181        let property_value_store = context.get_property_value_store::<E, P1>();
182        entities.retain(|entity| self.0 == property_value_store.get(*entity));
183    }
184}
185
186macro_rules! impl_query {
187    ($ct:expr) => {
188        seq!(N in 0..$ct {
189            impl<
190                E: Entity,
191                #(
192                    T~N : Property<E>,
193                )*
194            > Query<E> for (
195                #(
196                    T~N,
197                )*
198            )
199            {
200                fn get_query(&self) -> Vec<(usize, HashValueType)> {
201                    let mut ordered_items = vec![
202                    #(
203                        (T~N::id(), T~N::hash_property_value(&T~N::make_canonical(self.N))),
204                    )*
205                    ];
206                    ordered_items.sort_unstable_by(|a, b| a.0.cmp(&b.0));
207                    ordered_items
208                }
209
210                fn get_type_ids(&self) -> Vec<TypeId> {
211                    vec![
212                        #(
213                            T~N::type_id(),
214                        )*
215                    ]
216                }
217
218                fn multi_property_value_hash(&self) -> HashValueType {
219                    // This needs to be kept in sync with how multi-properties compute their hash. We are
220                    // exploiting the fact that `bincode` encodes tuples as the concatenation of their
221                    // elements. Unfortunately, `bincode` allocates, but we avoid more allocations by
222                    // using stack allocated arrays.
223
224                    // Multi-properties order their values by lexicographic order of the component
225                    // properties, not `TypeId` order.
226                    // let type_ids: [TypeId; $ct] = [
227                    //     #(
228                    //         T~N::type_id(),
229                    //     )*
230                    // ];
231                    let keys: [&str; $ct] = [
232                        #(
233                            T~N::name(),
234                        )*
235                    ];
236                    // It is convenient to have the elements of the array to be `Copy` in the `static_apply_reordering`
237                    // function. Since references are trivially copyable, we construct `values` below to be an array
238                    // of _references_ to the `Vec`s returned from `encode_to_vec`. (The compiler is smart enough to
239                    // keep the referenced value in scope.)
240                    let mut values: [&Vec<u8>; $ct] = [
241                        #(
242                            &$crate::bincode::serde::encode_to_vec(self.N, bincode::config::standard()).unwrap(),
243                        )*
244                    ];
245                    static_reorder_by_keys(&keys, &mut values);
246
247                    let data = values.into_iter().flatten().copied().collect::<Vec<u8>>();
248                    one_shot_128(&data.as_slice())
249                }
250
251                fn new_query_result<'c>(&self, context: &'c Context) -> EntitySet<'c, E> {
252                    // The case of an indexed multi-property.
253                    // This mirrors the indexed case in `SourceSet<'a, E>::new()`. The difference is, if the
254                    // multi-property is unindexed, we fall through to create `SourceSet`s for the components
255                    // rather than wrapping a `DerivedPropertySource`.
256                    if let Some(multi_property_id) = self.multi_property_id() {
257                        let property_store = context.entity_store.get_property_store::<E>();
258                        match property_store.get_index_set_with_hash_for_property_id(
259                            multi_property_id,
260                            self.multi_property_value_hash(),
261                        ) {
262                            $crate::entity::index::IndexSetResult::Set(entity_set) => {
263                                return EntitySet::from_source(SourceSet::IndexSet(entity_set));
264                            }
265                            $crate::entity::index::IndexSetResult::Empty => {
266                                return EntitySet::empty();
267                            }
268                            $crate::entity::index::IndexSetResult::Unsupported => {}
269                        }
270                        // If the property is not indexed, we fall through.
271                    }
272
273                    // We create a source set for each property.
274                    let mut sources: Vec<SourceSet<E>> = Vec::new();
275
276                    #(
277                        if let Some(source_set) = SourceSet::new::<T~N>(self.N, context) {
278                            sources.push(source_set);
279                        } else {
280                            // If a single source set is empty, the intersection of all sources is empty.
281                            return EntitySet::empty();
282                        }
283                    )*
284
285                    EntitySet::from_intersection_sources(sources)
286                }
287
288                fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
289                    // Constructing the `EntitySetIterator` directly instead of constructing an `EntitySet`
290                    // first is a micro-optimization improving tight-loop benchmark performance.
291                    if let Some(multi_property_id) = self.multi_property_id() {
292                        let property_store = context.entity_store.get_property_store::<E>();
293                        match property_store.get_index_set_with_hash_for_property_id(
294                            multi_property_id,
295                            self.multi_property_value_hash(),
296                        ) {
297                            $crate::entity::index::IndexSetResult::Set(entity_set) => {
298                                return EntitySetIterator::from_index_set(entity_set);
299                            }
300                            $crate::entity::index::IndexSetResult::Empty => {
301                                return EntitySetIterator::empty();
302                            }
303                            $crate::entity::index::IndexSetResult::Unsupported => {}
304                        }
305                    }
306
307                    let mut sources: Vec<SourceSet<E>> = Vec::new();
308
309                    #(
310                        if let Some(source_set) = SourceSet::new::<T~N>(self.N, context) {
311                            sources.push(source_set);
312                        } else {
313                            return EntitySetIterator::empty();
314                        }
315                    )*
316
317                    EntitySetIterator::from_sources(sources)
318                }
319
320                fn match_entity(&self, entity_id: EntityId<E>, context: &Context) -> bool {
321                    #(
322                        {
323                            let found_value: T~N = context.get_property(entity_id);
324                            if found_value != self.N {
325                                return false
326                            }
327                        }
328                    )*
329                    true
330                }
331
332                fn filter_entities(&self, entities: &mut Vec<EntityId<E>>, context: &Context) {
333                    // The fast path: If this query is indexed, we only have to do one pass over the entities.
334                    if let Some(multi_property_id) = self.multi_property_id() {
335                        let property_store = context.entity_store.get_property_store::<E>();
336                        match property_store.get_index_set_with_hash_for_property_id(
337                            multi_property_id,
338                            self.multi_property_value_hash(),
339                        ) {
340                            $crate::entity::index::IndexSetResult::Set(entity_set) => {
341                                entities.retain(|entity_id| entity_set.contains(entity_id));
342                                return;
343                            }
344                            $crate::entity::index::IndexSetResult::Empty => {
345                                entities.clear();
346                                return;
347                            }
348                            $crate::entity::index::IndexSetResult::Unsupported => {}
349                        }
350                        // If the property is not indexed, we fall through.
351                    }
352
353                    // The slow path: Check each property of the query separately.
354                    #(
355                        {
356                            let property_value_store = context.get_property_value_store::<E, T~N>();
357                            entities.retain(
358                                |entity|{
359                                    self.N == property_value_store.get(*entity)
360                                }
361                            );
362                        }
363                    )*
364                }
365            }
366        });
367    }
368}
369
370// Implement the versions with 2..20 parameters. (The 0 and 1 case are implemented above.)
371seq!(Z in 2..20 {
372    impl_query!(Z);
373});