xvc_ecs/ecs/
hstore.rs

1//! A component store for ephemeral operations based on [HashMap].
2//! Unlike [XvcStore], it doesn't require `T` to be serializable.
3//! It's supposed to be used operations that don't require final result to be recorded to disk.
4use super::*;
5use crate::error::{Error, Result};
6use crate::{Storable, XvcStore};
7use log::debug;
8use rayon::iter::{FromParallelIterator, ParallelIterator};
9
10use std::collections::hash_map::IterMut;
11use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
12use std::convert::From;
13use std::fmt::Debug;
14use std::hash::Hash;
15use std::iter::{FromIterator, Iterator};
16use std::sync::{Arc, RwLock};
17
18use super::vstore::VStore;
19
20use std::ops::{Deref, DerefMut};
21
22/// This is a HashMap for more random access and less restrictions, no support for serialization
23#[derive(Debug, Clone)]
24pub struct HStore<T> {
25    /// The wrapped map for the store
26    pub map: HashMap<XvcEntity, T>,
27}
28
29/// A shared version of [HStore] for use in multithreaded environments.
30pub type SharedHStore<T> = Arc<RwLock<HStore<T>>>;
31
32impl<T> Deref for HStore<T> {
33    type Target = HashMap<XvcEntity, T>;
34
35    fn deref(&self) -> &Self::Target {
36        &self.map
37    }
38}
39
40impl<T> DerefMut for HStore<T> {
41    fn deref_mut(&mut self) -> &mut Self::Target {
42        &mut self.map
43    }
44}
45
46impl<T> From<HashMap<XvcEntity, T>> for HStore<T> {
47    fn from(map: HashMap<XvcEntity, T>) -> Self {
48        Self { map }
49    }
50}
51
52impl<T> FromIterator<(XvcEntity, T)> for HStore<T> {
53    fn from_iter<I: IntoIterator<Item = (XvcEntity, T)>>(iter: I) -> Self {
54        Self {
55            map: HashMap::<XvcEntity, T>::from_iter(iter),
56        }
57    }
58}
59
60impl<T> FromParallelIterator<(XvcEntity, T)> for HStore<T>
61where
62    T: Send,
63{
64    fn from_par_iter<I>(par_iter: I) -> Self
65    where
66        I: rayon::iter::IntoParallelIterator<Item = (XvcEntity, T)>,
67    {
68        let par_iter = par_iter.into_par_iter();
69        let map: HashMap<XvcEntity, T> = par_iter.collect();
70        Self { map }
71    }
72}
73
74impl<T> HStore<T>
75where
76    T: Storable,
77{
78    /// Convert to [VStore]
79    pub fn to_vstore(&self) -> Result<VStore<T>> {
80        let mut store = VStore::new();
81        for (k, v) in self.map.iter() {
82            store.insert(*k, v.clone());
83        }
84        Ok(store)
85    }
86
87    /// Returns the inner map's iter_mut
88    pub fn iter_mut(&mut self) -> IterMut<'_, XvcEntity, T> {
89        self.map.iter_mut()
90    }
91
92    /// Return a mutable value for `entity`
93    pub fn get_mut(&mut self, entity: &XvcEntity) -> Option<&mut T> {
94        self.map.get_mut(entity)
95    }
96
97    /// This is used to create a store from actual values where the entity may
98    /// or may not already be in the store.
99    pub fn from_storable<I>(
100        values: I,
101        store: &XvcStore<T>,
102        generator: &XvcEntityGenerator,
103    ) -> HStore<T>
104    where
105        I: IntoIterator<Item = T>,
106    {
107        let mut hstore = HStore::<T>::new();
108        for value in values {
109            let key = match store.entity_by_value(&value) {
110                Some(e) => e,
111                None => generator.next_element(),
112            };
113            hstore.map.insert(key, value.clone());
114        }
115        hstore
116    }
117}
118
119impl<T> Default for HStore<T> {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125impl<T: Storable> From<&XvcStore<T>> for HStore<T> {
126    fn from(store: &XvcStore<T>) -> Self {
127        let map = HashMap::from_iter(store.iter().map(|(k, v)| (*k, v.clone())));
128        Self { map }
129    }
130}
131
132impl<T> HStore<T> {
133    /// Create an empty HStore.
134    ///
135    /// Calls inner map's [HashMap::new].
136    pub fn new() -> HStore<T> {
137        HStore {
138            map: HashMap::<XvcEntity, T>::new(),
139        }
140    }
141
142    /// Creates an empty HStore.
143    ///
144    /// Creates an inner map with the given `capacity`.
145    pub fn with_capacity(capacity: usize) -> HStore<T> {
146        HStore {
147            map: HashMap::<XvcEntity, T>::with_capacity(capacity),
148        }
149    }
150
151    /// Creates values from `func` and gets new entities from `generator` to create new records.
152    pub fn from_func<F>(generator: &XvcEntityGenerator, func: F) -> Result<HStore<T>>
153    where
154        F: Fn() -> Result<Option<T>>,
155    {
156        let mut hstore = HStore::<T>::new();
157        loop {
158            let value = match func() {
159                Ok(Some(v)) => v,
160                Ok(None) => break,
161                Err(err) => return Err(err),
162            };
163            let key = generator.next_element();
164            hstore.map.insert(key, value);
165        }
166        Ok(hstore)
167    }
168
169    /// Return the number of elements in this HStore
170    pub fn len(&self) -> usize {
171        self.map.len()
172    }
173
174    /// Return true if there are no elements in this HStore
175    pub fn is_empty(&self) -> bool {
176        self.map.is_empty()
177    }
178
179    /// Calls the inner map's insert
180    pub fn insert(&mut self, entity: XvcEntity, value: T) -> Option<T> {
181        self.map.insert(entity, value)
182    }
183
184    /// Returns a [HashSet] of values.
185    pub fn to_hset(&self) -> HashSet<T>
186    where
187        T: std::fmt::Debug + Eq + Hash + Clone,
188    {
189        let mut set = HashSet::<T>::with_capacity(self.len());
190        for (e, v) in self.iter() {
191            if !set.insert(v.clone()) {
192                debug!("Duplicate value in store: ({:?}, {:?})", e, v);
193            }
194        }
195        set
196    }
197
198    /// Returns a [BTreeSet] of values.
199    pub fn to_bset(&self) -> BTreeSet<T>
200    where
201        T: std::fmt::Debug + Ord + Clone,
202    {
203        let mut set = BTreeSet::<T>::new();
204        for (e, v) in self.iter() {
205            if !set.insert(v.clone()) {
206                debug!("Duplicate value in store: ({:?}, {:?})", e, &v);
207            }
208        }
209        set
210    }
211
212    /// Returns a map from values to entities.
213    ///
214    /// Skips if there are duplicate values in the map.
215    pub fn inverted_hmap(&self) -> HashMap<T, XvcEntity>
216    where
217        T: std::fmt::Debug + Eq + Hash + Clone,
218    {
219        let mut imap = HashMap::<T, XvcEntity>::with_capacity(self.len());
220        for (e, v) in self.iter() {
221            let ires = imap.insert(v.clone(), *e);
222            if ires.is_some() {
223                debug!("Duplicate value in store: ({:?}, {:?})", e, v);
224            }
225        }
226        imap
227    }
228
229    /// Returns a map from values to entities.
230    ///
231    /// Skips if there are duplicate values in the map.
232    pub fn inverted_bmap(&self) -> BTreeMap<T, XvcEntity>
233    where
234        T: std::fmt::Debug + Ord + Clone,
235    {
236        let mut imap = BTreeMap::<T, XvcEntity>::new();
237        for (e, v) in self.map.iter() {
238            let ires = imap.insert(v.clone(), *e);
239            if ires.is_some() {
240                debug!("Duplicate value in store: ({:?}, {:?})", e, v);
241            }
242        }
243        imap
244    }
245
246    /// Performs a left join with [XvcEntity] keys.
247    ///
248    /// The returned store contains `(T, Option<U>)` values that correspond to the identical
249    /// `XvcEntity` values.
250    ///
251    /// ## Example
252    ///
253    /// If this store has
254    ///
255    /// `{10: "John Doe", 12: "George Mason", 19: "Ali Canfield"}`,
256    /// and the `other` store contains
257    /// `{10: "Carpenter", 17: "Developer", 19: "Artist" }`
258    ///
259    /// `left_join` will return
260    ///
261    /// `{10: ("John Doe", Some("Carpenter")), 12: ("George Mason", None), 19: ("Ali Canfield",
262    /// Some("Artist")}`
263    ///
264    /// Note that, it may be more convenient to keep this relationship in a [crate::R11Store]
265    /// if your stores don't use filtering
266    pub fn left_join<U>(&self, other: HStore<U>) -> HStore<(T, Option<U>)>
267    where
268        T: Storable,
269        U: Storable,
270    {
271        let mut joined = HStore::<(T, Option<U>)>::new();
272        for (entity, value) in self.map.iter() {
273            joined.insert(*entity, (value.clone(), other.get(entity).cloned()));
274        }
275
276        joined
277    }
278
279    /// Performs a full join with [XvcEntity] keys.
280    ///
281    /// The returned store contains `(Option<T>, Option<U>)` values that correspond to the
282    /// identical `XvcEntity` values.
283    ///
284    /// Note that, it may be more convenient to keep this relationship in a [crate::R11Store]
285    /// if your stores don't use filtering
286    ///
287    /// ```rust
288    ///
289    /// use xvc_ecs::{XvcEntity, HStore};
290    ///
291    /// let mut store1 = HStore::<String>::new();
292    /// store1.insert(10u128.into(), "John Doe".into());
293    /// store1.insert(12u128.into(), "George Mason".into());
294    /// store1.insert(19u128.into(), "Ali Canfield".into());
295    ///
296    /// let mut store2 = HStore::<String>::new();
297    /// store2.insert(10u128.into(), "Carpenter".into());
298    /// store2.insert(17u128.into(), "Developer".into());
299    /// store2.insert(15u128.into(), "Plumber".into());
300    /// store2.insert(19u128.into(), "Artist".into());
301    ///
302    /// let result = store1.full_join(store2);
303    ///
304    /// assert_eq!(result.len(), 5);
305    /// assert_eq!(result[&10u128.into()], (Some("John Doe".into()), Some("Carpenter".into())));
306    /// assert_eq!(result[&12u128.into()], (Some("George Mason".into()), None));
307    /// assert_eq!(result[&15u128.into()], (None, Some("Plumber".into())));
308    /// assert_eq!(result[&17u128.into()], (None, Some("Developer".into())));
309    /// assert_eq!(result[&19u128.into()], (Some("Ali Canfield".into()), Some("Artist".into())));
310    /// ```
311    pub fn full_join<U>(&self, other: HStore<U>) -> HStore<(Option<T>, Option<U>)>
312    where
313        T: Storable,
314        U: Storable,
315    {
316        let all_keys = self.keys().chain(other.keys()).collect::<HashSet<_>>();
317        let mut joined = HStore::<(Option<T>, Option<U>)>::new();
318        for entity in all_keys.into_iter() {
319            joined.insert(
320                *entity,
321                (self.get(entity).cloned(), other.get(entity).cloned()),
322            );
323        }
324
325        joined
326    }
327
328    /// Performs a join with [XvcEntity] keys.
329    ///
330    /// The returned store contains `(T, U)` values that correspond to the
331    /// identical `XvcEntity` values for values that exist in both stores.
332    ///
333    /// ## Example
334    ///
335    /// If this store has
336    ///
337    /// `{10: "John Doe", 12: "George Mason", 19: "Ali Canfield"}`,
338    /// and the `other` store contains
339    /// `{10: "Carpenter", 17: "Developer", 15: "Plumber",  19: "Artist" }`
340    ///
341    /// `join` will return
342    ///
343    /// `{10: ("John Doe", "Carpenter"),
344    ///   19: ("Ali Canfield", "Artist")}`
345    ///
346    /// Note that, it may be more convenient to keep this relationship in a [crate::R11Store]
347    /// if your stores don't use filtering
348    /// ```rust
349    ///
350    /// use xvc_ecs::{XvcEntity, HStore};
351    ///
352    /// let mut store1 = HStore::<String>::new();
353    /// store1.insert(10u128.into(), "John Doe".into());
354    /// store1.insert(12u128.into(), "George Mason".into());
355    /// store1.insert(19u128.into(), "Ali Canfield".into());
356    ///
357    /// let mut store2 = HStore::<String>::new();
358    /// store2.insert(10u128.into(), "Carpenter".into());
359    /// store2.insert(17u128.into(), "Developer".into());
360    /// store2.insert(15u128.into(), "Plumber".into());
361    /// store2.insert(19u128.into(), "Artist".into());
362    ///
363    /// let result = store1.join(store2);
364    ///
365    /// assert_eq!(result.len(), 2);
366    /// assert_eq!(result[&10u128.into()], ("John Doe".into(), "Carpenter".into()));
367    /// assert_eq!(result[&19u128.into()], ("Ali Canfield".into(), "Artist".into()));
368    pub fn join<U>(&self, other: HStore<U>) -> HStore<(T, U)>
369    where
370        T: Storable,
371        U: Storable,
372    {
373        let mut joined = HStore::<(T, U)>::new();
374        self.map.iter().for_each(|(entity, value)| {
375            if let Some(other_value) = other.get(entity) {
376                joined.insert(*entity, (value.clone(), other_value.clone()));
377            }
378        });
379
380        joined
381    }
382
383    /// returns a subset of the store defined by iterator of XvcEntity
384    pub fn subset<I>(&self, keys: I) -> Result<HStore<T>>
385    where
386        I: Iterator<Item = XvcEntity>,
387        T: Clone,
388    {
389        let mut map = HashMap::<XvcEntity, T>::with_capacity(self.len());
390        for e in keys {
391            if let Some(v) = self.get(&e) {
392                map.insert(e, v.clone());
393            } else {
394                Error::CannotFindKeyInStore { key: e.to_string() }.warn();
395            }
396        }
397        Ok(Self { map })
398    }
399    /// Creates a new map by calling the `predicate` with each value.
400    ///
401    /// `predicate` must be a function or closure that returns `bool`.
402    ///
403    /// It doesn't clone the values.
404    pub fn filter<F>(&self, predicate: F) -> HStore<&T>
405    where
406        F: Fn(&XvcEntity, &T) -> bool,
407    {
408        let mut m = HashMap::<XvcEntity, &T>::new();
409        for (e, v) in self.map.iter() {
410            if predicate(e, v) {
411                m.insert(*e, v);
412            }
413        }
414
415        HStore::from(m)
416    }
417
418    /// Returns the first element of the map.
419    pub fn first(&self) -> Option<(&XvcEntity, &T)> {
420        self.map.iter().next()
421    }
422}
423
424impl<T: Clone> HStore<&T> {
425    /// Returns a new map by cloning the values.
426    pub fn cloned(&self) -> HStore<T> {
427        let mut map = HashMap::<XvcEntity, T>::with_capacity(self.len());
428        for (e, v) in self.iter() {
429            map.insert(*e, (*v).clone());
430        }
431        HStore::from(map)
432    }
433}
434
435impl<T> IntoIterator for HStore<T> {
436    type Item = (XvcEntity, T);
437    type IntoIter = std::collections::hash_map::IntoIter<XvcEntity, T>;
438
439    fn into_iter(self) -> Self::IntoIter {
440        self.map.into_iter()
441    }
442}
443
444impl<T: PartialEq> HStore<T> {
445    /// Returns the entities for a `value`.
446    ///
447    /// There may be more than one entity for a given value, hence it returns a `Vec`.
448    /// It uses internal reverse index for fast lookup.
449    pub fn entities_for(&self, value: &T) -> Option<Vec<XvcEntity>>
450    where
451        T: PartialEq,
452    {
453        let entity_vec: Vec<XvcEntity> = self
454            .map
455            .iter()
456            .filter_map(|(k, v)| if *v == *value { Some(*k) } else { None })
457            .collect();
458        if entity_vec.is_empty() {
459            None
460        } else {
461            Some(entity_vec)
462        }
463    }
464
465    /// Returns the first entity matched with [Self::entities_for]
466    pub fn entity_by_value(&self, value: &T) -> Option<XvcEntity> {
467        match self.entities_for(value) {
468            Some(vec_e) => vec_e.first().copied(),
469            None => None,
470        }
471    }
472}
473
474impl<T> From<(XvcEntity, T)> for HStore<T> {
475    fn from((e, v): (XvcEntity, T)) -> Self {
476        let mut store = HStore::<T>::new();
477        store.insert(e, v);
478        store
479    }
480}