/rust/registry/src/index.crates.io-6f17d22bba15001f/fst-0.4.7/src/raw/registry.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::raw::build::BuilderNode; |
2 | | use crate::raw::{CompiledAddr, NONE_ADDRESS}; |
3 | | |
4 | | #[derive(Debug)] |
5 | | pub struct Registry { |
6 | | table: Vec<RegistryCell>, |
7 | | table_size: usize, // number of rows |
8 | | mru_size: usize, // number of columns |
9 | | } |
10 | | |
11 | | #[derive(Debug)] |
12 | | struct RegistryCache<'a> { |
13 | | cells: &'a mut [RegistryCell], |
14 | | } |
15 | | |
16 | | #[derive(Clone, Debug)] |
17 | | pub struct RegistryCell { |
18 | | addr: CompiledAddr, |
19 | | node: BuilderNode, |
20 | | } |
21 | | |
22 | | #[derive(Debug)] |
23 | | pub enum RegistryEntry<'a> { |
24 | | Found(CompiledAddr), |
25 | | NotFound(&'a mut RegistryCell), |
26 | | Rejected, |
27 | | } |
28 | | |
29 | | impl Registry { |
30 | 0 | pub fn new(table_size: usize, mru_size: usize) -> Registry { |
31 | 0 | let empty_cell = RegistryCell::none(); |
32 | 0 | let ncells = table_size.checked_mul(mru_size).unwrap(); |
33 | 0 | Registry { table: vec![empty_cell; ncells], table_size, mru_size } |
34 | 0 | } |
35 | | |
36 | 0 | pub fn entry<'a>(&'a mut self, node: &BuilderNode) -> RegistryEntry<'a> { |
37 | 0 | if self.table.is_empty() { |
38 | 0 | return RegistryEntry::Rejected; |
39 | 0 | } |
40 | 0 | let bucket = self.hash(node); |
41 | 0 | let start = self.mru_size * bucket; |
42 | 0 | let end = start + self.mru_size; |
43 | 0 | RegistryCache { cells: &mut self.table[start..end] }.entry(node) |
44 | 0 | } |
45 | | |
46 | 0 | fn hash(&self, node: &BuilderNode) -> usize { |
47 | | // Basic FNV-1a hash as described: |
48 | | // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function |
49 | | // |
50 | | // In unscientific experiments, this provides the same compression |
51 | | // as `std::hash::SipHasher` but is much much faster. |
52 | | const FNV_PRIME: u64 = 1099511628211; |
53 | 0 | let mut h = 14695981039346656037; |
54 | 0 | h = (h ^ (node.is_final as u64)).wrapping_mul(FNV_PRIME); |
55 | 0 | h = (h ^ node.final_output.value()).wrapping_mul(FNV_PRIME); |
56 | 0 | for t in &node.trans { |
57 | 0 | h = (h ^ (t.inp as u64)).wrapping_mul(FNV_PRIME); |
58 | 0 | h = (h ^ t.out.value()).wrapping_mul(FNV_PRIME); |
59 | 0 | h = (h ^ (t.addr as u64)).wrapping_mul(FNV_PRIME); |
60 | 0 | } |
61 | 0 | (h as usize) % self.table_size |
62 | 0 | } |
63 | | } |
64 | | |
65 | | impl<'a> RegistryCache<'a> { |
66 | 0 | fn entry(mut self, node: &BuilderNode) -> RegistryEntry<'a> { |
67 | 0 | if self.cells.len() == 1 { |
68 | 0 | let cell = &mut self.cells[0]; |
69 | 0 | if !cell.is_none() && &cell.node == node { |
70 | 0 | RegistryEntry::Found(cell.addr) |
71 | | } else { |
72 | 0 | cell.node.clone_from(node); |
73 | 0 | RegistryEntry::NotFound(cell) |
74 | | } |
75 | 0 | } else if self.cells.len() == 2 { |
76 | 0 | let cell1 = &mut self.cells[0]; |
77 | 0 | if !cell1.is_none() && &cell1.node == node { |
78 | 0 | return RegistryEntry::Found(cell1.addr); |
79 | 0 | } |
80 | 0 |
|
81 | 0 | let cell2 = &mut self.cells[1]; |
82 | 0 | if !cell2.is_none() && &cell2.node == node { |
83 | 0 | let addr = cell2.addr; |
84 | 0 | self.cells.swap(0, 1); |
85 | 0 | return RegistryEntry::Found(addr); |
86 | 0 | } |
87 | 0 |
|
88 | 0 | self.cells[1].node.clone_from(node); |
89 | 0 | self.cells.swap(0, 1); |
90 | 0 | RegistryEntry::NotFound(&mut self.cells[0]) |
91 | | } else { |
92 | 0 | let find = |c: &RegistryCell| !c.is_none() && &c.node == node; |
93 | 0 | if let Some(i) = self.cells.iter().position(find) { |
94 | 0 | let addr = self.cells[i].addr; |
95 | 0 | self.promote(i); // most recently used |
96 | 0 | RegistryEntry::Found(addr) |
97 | | } else { |
98 | 0 | let last = self.cells.len() - 1; |
99 | 0 | self.cells[last].node.clone_from(node); // discard LRU |
100 | 0 | self.promote(last); |
101 | 0 | RegistryEntry::NotFound(&mut self.cells[0]) |
102 | | } |
103 | | } |
104 | 0 | } |
105 | | |
106 | 0 | fn promote(&mut self, mut i: usize) { |
107 | 0 | assert!(i < self.cells.len()); |
108 | 0 | while i > 0 { |
109 | 0 | self.cells.swap(i - 1, i); |
110 | 0 | i -= 1; |
111 | 0 | } |
112 | 0 | } |
113 | | } |
114 | | |
115 | | impl RegistryCell { |
116 | 0 | fn none() -> RegistryCell { |
117 | 0 | RegistryCell { addr: NONE_ADDRESS, node: BuilderNode::default() } |
118 | 0 | } |
119 | | |
120 | 0 | fn is_none(&self) -> bool { |
121 | 0 | self.addr == NONE_ADDRESS |
122 | 0 | } |
123 | | |
124 | 0 | pub fn insert(&mut self, addr: CompiledAddr) { |
125 | 0 | self.addr = addr; |
126 | 0 | } |
127 | | } |
128 | | |
129 | | #[cfg(test)] |
130 | | mod tests { |
131 | | use super::{Registry, RegistryCache, RegistryCell, RegistryEntry}; |
132 | | use crate::raw::build::BuilderNode; |
133 | | use crate::raw::{Output, Transition}; |
134 | | |
135 | | fn assert_rejected(entry: RegistryEntry) { |
136 | | match entry { |
137 | | RegistryEntry::Rejected => {} |
138 | | entry => panic!("expected rejected entry, got: {:?}", entry), |
139 | | } |
140 | | } |
141 | | |
142 | | fn assert_not_found(entry: RegistryEntry) { |
143 | | match entry { |
144 | | RegistryEntry::NotFound(_) => {} |
145 | | entry => panic!("expected nout found entry, got: {:?}", entry), |
146 | | } |
147 | | } |
148 | | |
149 | | fn assert_insert_and_found(reg: &mut Registry, bnode: &BuilderNode) { |
150 | | match reg.entry(&bnode) { |
151 | | RegistryEntry::NotFound(cell) => cell.insert(1234), |
152 | | entry => panic!("unexpected not found entry, got: {:?}", entry), |
153 | | } |
154 | | match reg.entry(&bnode) { |
155 | | RegistryEntry::Found(addr) => assert_eq!(addr, 1234), |
156 | | entry => panic!("unexpected found entry, got: {:?}", entry), |
157 | | } |
158 | | } |
159 | | |
160 | | #[test] |
161 | | fn empty_is_ok() { |
162 | | let mut reg = Registry::new(0, 0); |
163 | | let bnode = BuilderNode { |
164 | | is_final: false, |
165 | | final_output: Output::zero(), |
166 | | trans: vec![], |
167 | | }; |
168 | | assert_rejected(reg.entry(&bnode)); |
169 | | } |
170 | | |
171 | | #[test] |
172 | | fn one_final_is_ok() { |
173 | | let mut reg = Registry::new(1, 1); |
174 | | let bnode = BuilderNode { |
175 | | is_final: true, |
176 | | final_output: Output::zero(), |
177 | | trans: vec![], |
178 | | }; |
179 | | assert_insert_and_found(&mut reg, &bnode); |
180 | | } |
181 | | |
182 | | #[test] |
183 | | fn one_with_trans_is_ok() { |
184 | | let mut reg = Registry::new(1, 1); |
185 | | let bnode = BuilderNode { |
186 | | is_final: false, |
187 | | final_output: Output::zero(), |
188 | | trans: vec![Transition { |
189 | | addr: 0, |
190 | | inp: b'a', |
191 | | out: Output::zero(), |
192 | | }], |
193 | | }; |
194 | | assert_insert_and_found(&mut reg, &bnode); |
195 | | assert_not_found( |
196 | | reg.entry(&BuilderNode { is_final: true, ..bnode.clone() }), |
197 | | ); |
198 | | assert_not_found(reg.entry(&BuilderNode { |
199 | | trans: vec![Transition { |
200 | | addr: 0, |
201 | | inp: b'b', |
202 | | out: Output::zero(), |
203 | | }], |
204 | | ..bnode.clone() |
205 | | })); |
206 | | assert_not_found(reg.entry(&BuilderNode { |
207 | | trans: vec![Transition { |
208 | | addr: 0, |
209 | | inp: b'a', |
210 | | out: Output::new(1), |
211 | | }], |
212 | | ..bnode.clone() |
213 | | })); |
214 | | } |
215 | | |
216 | | #[test] |
217 | | fn cache_works() { |
218 | | let mut reg = Registry::new(1, 1); |
219 | | |
220 | | let bnode1 = BuilderNode { is_final: true, ..BuilderNode::default() }; |
221 | | assert_insert_and_found(&mut reg, &bnode1); |
222 | | |
223 | | let bnode2 = |
224 | | BuilderNode { final_output: Output::new(1), ..bnode1.clone() }; |
225 | | assert_insert_and_found(&mut reg, &bnode2); |
226 | | assert_not_found(reg.entry(&bnode1)); |
227 | | } |
228 | | |
229 | | #[test] |
230 | | fn promote() { |
231 | | let bn = BuilderNode::default(); |
232 | | let mut bnodes = vec![ |
233 | | RegistryCell { addr: 1, node: bn.clone() }, |
234 | | RegistryCell { addr: 2, node: bn.clone() }, |
235 | | RegistryCell { addr: 3, node: bn.clone() }, |
236 | | RegistryCell { addr: 4, node: bn.clone() }, |
237 | | ]; |
238 | | let mut cache = RegistryCache { cells: &mut bnodes }; |
239 | | |
240 | | cache.promote(0); |
241 | | assert_eq!(cache.cells[0].addr, 1); |
242 | | assert_eq!(cache.cells[1].addr, 2); |
243 | | assert_eq!(cache.cells[2].addr, 3); |
244 | | assert_eq!(cache.cells[3].addr, 4); |
245 | | |
246 | | cache.promote(1); |
247 | | assert_eq!(cache.cells[0].addr, 2); |
248 | | assert_eq!(cache.cells[1].addr, 1); |
249 | | assert_eq!(cache.cells[2].addr, 3); |
250 | | assert_eq!(cache.cells[3].addr, 4); |
251 | | |
252 | | cache.promote(3); |
253 | | assert_eq!(cache.cells[0].addr, 4); |
254 | | assert_eq!(cache.cells[1].addr, 2); |
255 | | assert_eq!(cache.cells[2].addr, 1); |
256 | | assert_eq!(cache.cells[3].addr, 3); |
257 | | |
258 | | cache.promote(2); |
259 | | assert_eq!(cache.cells[0].addr, 1); |
260 | | assert_eq!(cache.cells[1].addr, 4); |
261 | | assert_eq!(cache.cells[2].addr, 2); |
262 | | assert_eq!(cache.cells[3].addr, 3); |
263 | | } |
264 | | } |