1 | use fnv::FnvHashMap; |
2 | |
3 | use crate::config::{Entity, Position}; |
4 | |
5 | /// A structure to keep information for [`Entity`] as a key. |
6 | #[derive (Debug, Default, Clone, PartialEq, Eq)] |
7 | pub struct EntityMap<T> { |
8 | // we have a global type to allocate in on stack. |
9 | // because most of the time no changes are made to the [`EntityMap`]. |
10 | global: T, |
11 | columns: FnvHashMap<usize, T>, |
12 | rows: FnvHashMap<usize, T>, |
13 | cells: FnvHashMap<Position, T>, |
14 | } |
15 | |
16 | impl<T> EntityMap<T> { |
17 | /// Creates an empty [`EntityMap`]. |
18 | pub fn new(global: T) -> Self { |
19 | Self { |
20 | global, |
21 | rows: FnvHashMap::default(), |
22 | columns: FnvHashMap::default(), |
23 | cells: FnvHashMap::default(), |
24 | } |
25 | } |
26 | |
27 | /// Get a value for an [`Entity`]. |
28 | pub fn get(&self, entity: Entity) -> &T { |
29 | if self.rows.is_empty() && self.columns.is_empty() && self.cells.is_empty() { |
30 | return &self.global; |
31 | } |
32 | |
33 | match entity { |
34 | Entity::Column(col) => self.columns.get(&col).unwrap_or(&self.global), |
35 | Entity::Row(row) => self.rows.get(&row).unwrap_or(&self.global), |
36 | Entity::Cell(row, col) => { |
37 | // todo: optimize; |
38 | // |
39 | // Cause we can change rows/columns/cells separately we need to check them separately. |
40 | // But we often doing this checks in `Grid::fmt` and I believe if we could optimize it it could be beneficial. |
41 | // |
42 | // Haven't found a solution for that yet. |
43 | // |
44 | // I was wondering if there is a hash function like. |
45 | // Apparently it doesn't make sense cause we will reset columns/rows on cell insert which is not what we want. |
46 | // |
47 | // ``` |
48 | // hash(column, row) == hash(column) == hash(row) |
49 | // ``` |
50 | // |
51 | // ref: https://opendsa-server.cs.vt.edu/ODSA/Books/Everything/html/Sparse.html |
52 | // ref: https://users.rust-lang.org/t/make-hash-return-same-value-whather-the-order-of-element-of-a-tuple/69932/13 |
53 | |
54 | self.cells |
55 | .get(&(row, col)) |
56 | .or_else(|| self.columns.get(&col)) |
57 | .or_else(|| self.rows.get(&row)) |
58 | .unwrap_or(&self.global) |
59 | } |
60 | Entity::Global => &self.global, |
61 | } |
62 | } |
63 | |
64 | /// Removes a value for an [`Entity`]. |
65 | pub fn remove(&mut self, entity: Entity) { |
66 | match entity { |
67 | Entity::Global => { |
68 | self.cells.clear(); |
69 | self.rows.clear(); |
70 | self.columns.clear(); |
71 | } |
72 | Entity::Column(col) => self.cells.retain(|&(_, c), _| c != col), |
73 | Entity::Row(row) => self.cells.retain(|&(r, _), _| r != row), |
74 | Entity::Cell(row, col) => { |
75 | self.cells.remove(&(row, col)); |
76 | } |
77 | } |
78 | } |
79 | } |
80 | |
81 | impl<T: Clone> EntityMap<T> { |
82 | /// Set a value for an [`Entity`]. |
83 | pub fn insert(&mut self, entity: Entity, value: T) { |
84 | match entity { |
85 | Entity::Column(col) => { |
86 | for &row in self.rows.keys() { |
87 | self.cells.insert((row, col), value.clone()); |
88 | } |
89 | |
90 | self.columns.insert(col, value); |
91 | } |
92 | Entity::Row(row) => { |
93 | for &col in self.columns.keys() { |
94 | self.cells.insert((row, col), value.clone()); |
95 | } |
96 | |
97 | self.rows.insert(row, value); |
98 | } |
99 | Entity::Cell(row, col) => { |
100 | self.cells.insert((row, col), value); |
101 | } |
102 | Entity::Global => { |
103 | self.remove(Entity::Global); |
104 | self.global = value |
105 | } |
106 | } |
107 | } |
108 | } |
109 | |