ixa/entity/query/
query_impls.rs

1use std::any::TypeId;
2
3use seq_macro::seq;
4
5use crate::entity::entity_set::{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_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
32        let population_iterator = context.get_entity_iterator::<E>();
33        EntitySetIterator::from_population_iterator(population_iterator)
34    }
35
36    fn match_entity(&self, _entity_id: EntityId<E>, _context: &Context) -> bool {
37        // Every entity matches the empty query.
38        true
39    }
40
41    fn filter_entities(&self, _entities: &mut Vec<EntityId<E>>, _context: &Context) {
42        // Nothing to do.
43    }
44}
45
46// Implement the query version with one parameter.
47impl<E: Entity, P1: Property<E>> Query<E> for (P1,) {
48    fn get_query(&self) -> Vec<(usize, HashValueType)> {
49        let value = P1::make_canonical(self.0);
50        vec![(P1::id(), P1::hash_property_value(&value))]
51    }
52
53    fn get_type_ids(&self) -> Vec<TypeId> {
54        vec![P1::type_id()]
55    }
56
57    fn multi_property_id(&self) -> Option<usize> {
58        // While not a "true" multi-property, it is convenient to have this method return the
59        // `TypeId` of the singleton property.
60        Some(P1::index_id())
61    }
62
63    fn multi_property_value_hash(&self) -> HashValueType {
64        P1::hash_property_value(&P1::make_canonical(self.0))
65    }
66
67    fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
68        let property_store = context.entity_store.get_property_store::<E>();
69
70        // The case of an indexed multi-property.
71        // This mirrors the indexed case in `SourceSet<'a, E>::new()`. The difference is, if the
72        // multi-property is unindexed, we fall through to create `SourceSet`s for the components
73        // rather than wrapping a `DerivedPropertySource`.
74        if let Some(multi_property_id) = self.multi_property_id() {
75            match property_store.get_index_set_with_hash_for_property_id(
76                context,
77                multi_property_id,
78                self.multi_property_value_hash(),
79            ) {
80                IndexSetResult::Set(people_set) => {
81                    return EntitySetIterator::from_index_set(people_set);
82                }
83                IndexSetResult::Empty => {
84                    return EntitySetIterator::empty();
85                }
86                IndexSetResult::Unsupported => {}
87            }
88            // If the property is not indexed, we fall through.
89        }
90
91        // We create a source set for each property.
92        let mut sources: Vec<SourceSet<E>> = Vec::new();
93
94        if let Some(source_set) = SourceSet::new::<P1>(self.0, context) {
95            sources.push(source_set);
96        } else {
97            // If a single source set is empty, the intersection of all sources is empty.
98            return EntitySetIterator::empty();
99        }
100
101        EntitySetIterator::from_sources(sources)
102    }
103
104    fn match_entity(&self, entity_id: EntityId<E>, context: &Context) -> bool {
105        let found_value: P1 = context.get_property(entity_id);
106        found_value == self.0
107    }
108
109    fn filter_entities(&self, entities: &mut Vec<EntityId<E>>, context: &Context) {
110        let property_value_store = context.get_property_value_store::<E, P1>();
111        entities.retain(|entity| self.0 == property_value_store.get(*entity));
112    }
113}
114
115macro_rules! impl_query {
116    ($ct:expr) => {
117        seq!(N in 0..$ct {
118            impl<
119                E: Entity,
120                #(
121                    T~N : Property<E>,
122                )*
123            > Query<E> for (
124                #(
125                    T~N,
126                )*
127            )
128            {
129                fn get_query(&self) -> Vec<(usize, HashValueType)> {
130                    let mut ordered_items = vec![
131                    #(
132                        (T~N::id(), T~N::hash_property_value(&T~N::make_canonical(self.N))),
133                    )*
134                    ];
135                    ordered_items.sort_unstable_by(|a, b| a.0.cmp(&b.0));
136                    ordered_items
137                }
138
139                fn get_type_ids(&self) -> Vec<TypeId> {
140                    vec![
141                        #(
142                            T~N::type_id(),
143                        )*
144                    ]
145                }
146
147                fn multi_property_value_hash(&self) -> HashValueType {
148                    // This needs to be kept in sync with how multi-properties compute their hash. We are
149                    // exploiting the fact that `bincode` encodes tuples as the concatenation of their
150                    // elements. Unfortunately, `bincode` allocates, but we avoid more allocations by
151                    // using stack allocated arrays.
152
153                    // Multi-properties order their values by lexicographic order of the component
154                    // properties, not `TypeId` order.
155                    // let type_ids: [TypeId; $ct] = [
156                    //     #(
157                    //         T~N::type_id(),
158                    //     )*
159                    // ];
160                    let keys: [&str; $ct] = [
161                        #(
162                            T~N::name(),
163                        )*
164                    ];
165                    // It is convenient to have the elements of the array to be `Copy` in the `static_apply_reordering`
166                    // function. Since references are trivially copyable, we construct `values` below to be an array
167                    // of _references_ to the `Vec`s returned from `encode_to_vec`. (The compiler is smart enough to
168                    // keep the referenced value in scope.)
169                    let mut values: [&Vec<u8>; $ct] = [
170                        #(
171                            &$crate::bincode::serde::encode_to_vec(self.N, bincode::config::standard()).unwrap(),
172                        )*
173                    ];
174                    static_reorder_by_keys(&keys, &mut values);
175
176                    let data = values.into_iter().flatten().copied().collect::<Vec<u8>>();
177                    one_shot_128(&data.as_slice())
178                }
179
180                fn new_query_result_iterator<'c>(&self, context: &'c Context) -> EntitySetIterator<'c, E> {
181                    // The case of an indexed multi-property.
182                    // This mirrors the indexed case in `SourceSet<'a, E>::new()`. The difference is, if the
183                    // multi-property is unindexed, we fall through to create `SourceSet`s for the components
184                    // rather than wrapping a `DerivedPropertySource`.
185                    if let Some(multi_property_id) = self.multi_property_id() {
186                        let property_store = context.entity_store.get_property_store::<E>();
187                        match property_store.get_index_set_with_hash_for_property_id(
188                            context,
189                            multi_property_id,
190                            self.multi_property_value_hash(),
191                        ) {
192                            $crate::entity::index::IndexSetResult::Set(entity_set) => {
193                                return EntitySetIterator::from_index_set(entity_set);
194                            }
195                            $crate::entity::index::IndexSetResult::Empty => {
196                                return EntitySetIterator::empty();
197                            }
198                            $crate::entity::index::IndexSetResult::Unsupported => {}
199                        }
200                        // If the property is not indexed, we fall through.
201                    }
202
203                    // We create a source set for each property.
204                    let mut sources: Vec<SourceSet<E>> = Vec::new();
205
206                    #(
207                        if let Some(source_set) = SourceSet::new::<T~N>(self.N, context) {
208                            sources.push(source_set);
209                        } else {
210                            // If a single source set is empty, the intersection of all sources is empty.
211                            return EntitySetIterator::empty();
212                        }
213                    )*
214
215                    EntitySetIterator::from_sources(sources)
216                }
217
218                fn match_entity(&self, entity_id: EntityId<E>, context: &Context) -> bool {
219                    #(
220                        {
221                            let found_value: T~N = context.get_property(entity_id);
222                            if found_value != self.N {
223                                return false
224                            }
225                        }
226                    )*
227                    true
228                }
229
230                fn filter_entities(&self, entities: &mut Vec<EntityId<E>>, context: &Context) {
231                    // The fast path: If this query is indexed, we only have to do one pass over the entities.
232                    if let Some(multi_property_id) = self.multi_property_id() {
233                        let property_store = context.entity_store.get_property_store::<E>();
234                        match property_store.get_index_set_with_hash_for_property_id(
235                            context,
236                            multi_property_id,
237                            self.multi_property_value_hash(),
238                        ) {
239                            $crate::entity::index::IndexSetResult::Set(entity_set) => {
240                                entities.retain(|entity_id| entity_set.contains(entity_id));
241                                return;
242                            }
243                            $crate::entity::index::IndexSetResult::Empty => {
244                                entities.clear();
245                                return;
246                            }
247                            $crate::entity::index::IndexSetResult::Unsupported => {}
248                        }
249                        // If the property is not indexed, we fall through.
250                    }
251
252                    // The slow path: Check each property of the query separately.
253                    #(
254                        {
255                            let property_value_store = context.get_property_value_store::<E, T~N>();
256                            entities.retain(
257                                |entity|{
258                                    self.N == property_value_store.get(*entity)
259                                }
260                            );
261                        }
262                    )*
263                }
264            }
265        });
266    }
267}
268
269// Implement the versions with 2..10 parameters. (The 1 case is implemented above.)
270seq!(Z in 2..10 {
271    impl_query!(Z);
272});