edict/world/
edges.rs

1use alloc::vec::Vec;
2use core::{
3    any::TypeId,
4    hash::{BuildHasher, Hash, Hasher},
5};
6
7use hashbrown::hash_map::{Entry, HashMap, RawEntryMut};
8
9use crate::{
10    archetype::Archetype,
11    bundle::{Bundle, BundleDesc},
12    cold,
13    component::{ComponentInfo, ComponentRegistry},
14    hash::MulHasherBuilder,
15};
16
17use super::ArchetypeSet;
18
19pub(super) struct Edges {
20    /// Maps archetype index + additional component id to the archetype index.
21    add_one: HashMap<(u32, TypeId), u32, MulHasherBuilder>,
22
23    /// Maps archetype index + additional static bundle key to the archetype index.
24    add_key: HashMap<(u32, TypeId), u32, MulHasherBuilder>,
25
26    /// Maps archetype index + additional component ids list to archetype.
27    add_ids: HashMap<(u32, Vec<TypeId>), u32, MulHasherBuilder>,
28
29    /// Maps archetype index - removed component id to the archetype index.
30    sub_one: HashMap<(u32, TypeId), u32, MulHasherBuilder>,
31
32    /// Maps archetype index + removed static bundle key to the archetype index.
33    sub_key: HashMap<(u32, TypeId), u32, MulHasherBuilder>,
34
35    /// Maps archetype index + removed component ids list to archetype.
36    sub_ids: HashMap<(u32, Vec<TypeId>), u32, MulHasherBuilder>,
37}
38
39impl Edges {
40    #[must_use]
41    pub fn new() -> Edges {
42        Edges {
43            add_one: HashMap::with_hasher(MulHasherBuilder),
44            add_key: HashMap::with_hasher(MulHasherBuilder),
45            add_ids: HashMap::with_hasher(MulHasherBuilder),
46            sub_one: HashMap::with_hasher(MulHasherBuilder),
47            sub_key: HashMap::with_hasher(MulHasherBuilder),
48            sub_ids: HashMap::with_hasher(MulHasherBuilder),
49        }
50    }
51}
52
53impl Edges {
54    #[must_use]
55    pub fn insert<F>(
56        &mut self,
57        registry: &mut ComponentRegistry,
58        archetypes: &mut ArchetypeSet,
59        src: u32,
60        ty: TypeId,
61        register_component: F,
62    ) -> u32
63    where
64        F: FnOnce(&mut ComponentRegistry) -> &ComponentInfo,
65    {
66        let slow = || {
67            cold();
68            match archetypes.iter().position(|a| {
69                let ids = archetypes[src as usize].ids().chain(Some(ty));
70                a.matches(ids)
71            }) {
72                None => {
73                    cold();
74                    archetypes.add_with(|archetypes| {
75                        let info = register_component(registry);
76                        Archetype::new(archetypes[src as usize].infos().chain(Some(info)))
77                    })
78                }
79                Some(idx) => idx as u32,
80            }
81        };
82
83        match self.add_one.entry((src, ty)) {
84            Entry::Occupied(entry) => *entry.get(),
85            Entry::Vacant(entry) => {
86                let idx = slow();
87                entry.insert(idx);
88                idx
89            }
90        }
91    }
92
93    #[must_use]
94    pub fn insert_bundle<B, F>(
95        &mut self,
96        registry: &mut ComponentRegistry,
97        archetypes: &mut ArchetypeSet,
98        src: u32,
99        bundle: &B,
100        register_components: F,
101    ) -> u32
102    where
103        B: BundleDesc,
104        F: FnOnce(&mut ComponentRegistry),
105    {
106        let very_slow = || {
107            cold();
108            match archetypes.iter().position(|a| {
109                bundle.with_ids(|ids| {
110                    let ids = archetypes[src as usize].ids().chain(ids.iter().copied());
111                    a.matches(ids)
112                })
113            }) {
114                None => {
115                    cold();
116                    archetypes.add_with(|archetypes| {
117                        register_components(registry);
118
119                        bundle.with_ids(|ids| {
120                            Archetype::new(
121                                archetypes[src as usize]
122                                    .ids()
123                                    .filter(|aid| ids.iter().all(|id| *id != *aid))
124                                    .chain(ids.iter().copied())
125                                    .map(|id| match registry.get_info(id) {
126                                        None => panic!("Component {:?} is not registered", id),
127                                        Some(info) => info,
128                                    }),
129                            )
130                        })
131                    })
132                }
133                Some(idx) => idx as u32,
134            }
135        };
136
137        let add_ids = &mut self.add_ids;
138        let slow = || {
139            cold();
140            let raw_entry = bundle.with_ids(move |ids| {
141                let mut hasher = add_ids.hasher().build_hasher();
142                (src, ids).hash(&mut hasher);
143                let hash = hasher.finish();
144
145                add_ids
146                    .raw_entry_mut()
147                    .from_hash(hash, |(key_src, key_ids)| *key_src == src && key_ids == ids)
148            });
149
150            match raw_entry {
151                RawEntryMut::Occupied(entry) => *entry.get(),
152                RawEntryMut::Vacant(entry) => {
153                    let idx = very_slow();
154                    entry.insert((src, bundle.with_ids(|ids| Vec::from(ids))), idx);
155                    idx
156                }
157            }
158        };
159
160        match B::key() {
161            None => slow(),
162            Some(key) => match self.add_key.entry((src, key)) {
163                Entry::Occupied(entry) => *entry.get(),
164                Entry::Vacant(entry) => {
165                    let idx = slow();
166                    entry.insert(idx);
167                    idx
168                }
169            },
170        }
171    }
172
173    #[must_use]
174    pub fn remove(&mut self, archetypes: &mut ArchetypeSet, src: u32, ty: TypeId) -> u32 {
175        let mut slow = || {
176            cold();
177            match archetypes.iter().position(|a| {
178                let ids = archetypes[src as usize].ids().filter(|id| *id != ty);
179                a.matches(ids)
180            }) {
181                None => {
182                    cold();
183                    archetypes.add_with(|archetypes| {
184                        Archetype::new(
185                            archetypes[src as usize]
186                                .infos()
187                                .filter(|info| info.id() != ty),
188                        )
189                    })
190                }
191                Some(idx) => idx as u32,
192            }
193        };
194
195        match self.sub_one.entry((src, ty)) {
196            Entry::Occupied(entry) => *entry.get(),
197            Entry::Vacant(entry) => {
198                let idx = slow();
199                entry.insert(idx);
200                idx
201            }
202        }
203    }
204
205    #[must_use]
206    pub fn remove_bundle<B>(&mut self, archetypes: &mut ArchetypeSet, src: u32) -> u32
207    where
208        B: Bundle,
209    {
210        let mut very_slow = || {
211            cold();
212            let ids = archetypes[src as usize]
213                .ids()
214                .filter(|id| !B::static_contains_id(*id));
215
216            match archetypes.iter().position(|a| a.matches(ids.clone())) {
217                None => {
218                    cold();
219                    drop(ids);
220
221                    archetypes.add_with(|archetypes| {
222                        Archetype::new(
223                            archetypes[src as usize]
224                                .infos()
225                                .filter(|info| !B::static_contains_id(info.id())),
226                        )
227                    })
228                }
229                Some(idx) => idx as u32,
230            }
231        };
232
233        let sub_ids = &mut self.sub_ids;
234        let slow = || {
235            cold();
236            let raw_entry = B::static_with_ids(move |ids| {
237                let mut hasher = sub_ids.hasher().build_hasher();
238                (src, ids).hash(&mut hasher);
239                let hash = hasher.finish();
240
241                sub_ids
242                    .raw_entry_mut()
243                    .from_hash(hash, |(key_src, key_ids)| *key_src == src && key_ids == ids)
244            });
245
246            match raw_entry {
247                RawEntryMut::Occupied(entry) => *entry.get(),
248                RawEntryMut::Vacant(entry) => {
249                    let idx = very_slow();
250                    entry.insert((src, B::static_with_ids(|ids| ids.into())), idx);
251                    idx
252                }
253            }
254        };
255
256        match B::key() {
257            None => slow(),
258            Some(key) => match self.sub_key.entry((src, key)) {
259                Entry::Occupied(entry) => *entry.get(),
260                Entry::Vacant(entry) => {
261                    let idx = slow();
262                    entry.insert(idx);
263                    idx
264                }
265            },
266        }
267    }
268}