datacortex_core/state/
context_map.rs1use super::state_table::StateTable;
9
10pub struct ContextMap {
15 table: Vec<u8>,
17 mask: usize,
19}
20
21impl ContextMap {
22 pub fn new(size: usize) -> Self {
25 debug_assert!(size.is_power_of_two(), "ContextMap size must be power of 2");
26 ContextMap {
27 table: vec![0u8; size],
28 mask: size - 1,
29 }
30 }
31
32 #[inline(always)]
34 pub fn get(&self, hash: u32) -> u8 {
35 self.table[hash as usize & self.mask]
36 }
37
38 #[inline(always)]
40 pub fn set(&mut self, hash: u32, state: u8) {
41 self.table[hash as usize & self.mask] = state;
42 }
43
44 #[inline]
48 pub fn predict_and_update(&mut self, hash: u32, bit: u8) -> u8 {
49 let idx = hash as usize & self.mask;
50 let state = self.table[idx];
51 self.table[idx] = StateTable::next(state, bit);
52 state
53 }
54}
55
56pub struct ChecksumContextMap {
61 table: Vec<u8>,
63 mask: usize,
65}
66
67impl ChecksumContextMap {
68 pub fn new(byte_size: usize) -> Self {
71 debug_assert!(
72 byte_size.is_power_of_two(),
73 "ChecksumContextMap size must be power of 2"
74 );
75 let entries = byte_size / 2;
76 ChecksumContextMap {
77 table: vec![0u8; byte_size],
78 mask: entries - 1,
79 }
80 }
81
82 #[inline(always)]
84 fn checksum(hash: u32) -> u8 {
85 ((hash >> 16) as u8) | 1 }
87
88 #[inline(always)]
90 pub fn get(&self, hash: u32) -> u8 {
91 let idx = (hash as usize & self.mask) * 2;
92 let stored_cs = self.table[idx];
93 let expected_cs = Self::checksum(hash);
94 if stored_cs == expected_cs {
95 self.table[idx + 1]
96 } else {
97 0
98 }
99 }
100
101 #[inline(always)]
103 pub fn set(&mut self, hash: u32, state: u8) {
104 let idx = (hash as usize & self.mask) * 2;
105 self.table[idx] = Self::checksum(hash);
106 self.table[idx + 1] = state;
107 }
108}
109
110pub struct AssociativeContextMap {
117 table: Vec<u8>,
119 mask: usize,
121}
122
123impl AssociativeContextMap {
124 pub fn new(byte_size: usize) -> Self {
127 debug_assert!(
128 byte_size.is_power_of_two() && byte_size >= 8,
129 "AssociativeContextMap size must be power of 2 and >= 8"
130 );
131 let sets = byte_size / 4;
132 AssociativeContextMap {
133 table: vec![0u8; byte_size],
134 mask: sets - 1,
135 }
136 }
137
138 #[inline(always)]
140 fn checksum(hash: u32) -> u8 {
141 ((hash >> 16) as u8) | 1
142 }
143
144 #[inline(always)]
146 pub fn get(&self, hash: u32) -> u8 {
147 let base = (hash as usize & self.mask) * 4;
148 let cs = Self::checksum(hash);
149
150 if self.table[base] == cs {
152 return self.table[base + 1];
153 }
154 if self.table[base + 2] == cs {
156 return self.table[base + 3];
157 }
158 0
160 }
161
162 #[inline(always)]
164 pub fn set(&mut self, hash: u32, state: u8) {
165 let base = (hash as usize & self.mask) * 4;
166 let cs = Self::checksum(hash);
167
168 if self.table[base] == cs {
170 self.table[base + 1] = state;
171 return;
172 }
173 if self.table[base + 2] == cs {
175 self.table[base + 3] = state;
176 return;
177 }
178 self.table[base + 2] = self.table[base];
180 self.table[base + 3] = self.table[base + 1];
181 self.table[base] = cs;
182 self.table[base + 1] = state;
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn new_entries_are_zero() {
192 let cm = ContextMap::new(1024);
193 assert_eq!(cm.get(0), 0);
194 assert_eq!(cm.get(999), 0);
195 }
196
197 #[test]
198 fn set_and_get() {
199 let mut cm = ContextMap::new(1024);
200 cm.set(42, 128);
201 assert_eq!(cm.get(42), 128);
202 }
203
204 #[test]
205 fn hash_masking() {
206 let mut cm = ContextMap::new(256);
207 cm.set(0, 10);
208 assert_eq!(cm.get(256), 10);
209 }
210
211 #[test]
212 fn predict_and_update_transitions() {
213 let mut cm = ContextMap::new(1024);
214 let state = cm.predict_and_update(42, 1);
215 assert_eq!(state, 0);
216 let new_state = cm.get(42);
217 assert_ne!(new_state, 0);
218 }
219
220 #[test]
221 fn lossy_collision() {
222 let mut cm = ContextMap::new(256);
223 cm.set(5, 100);
224 cm.set(5 + 256, 200);
225 assert_eq!(cm.get(5), 200);
226 }
227
228 #[test]
231 fn checksum_new_entries_return_zero() {
232 let cm = ChecksumContextMap::new(2048);
233 assert_eq!(cm.get(0), 0);
234 assert_eq!(cm.get(12345), 0);
235 }
236
237 #[test]
238 fn checksum_set_and_get() {
239 let mut cm = ChecksumContextMap::new(2048);
240 cm.set(42, 128);
241 assert_eq!(cm.get(42), 128);
242 }
243
244 #[test]
245 fn checksum_overwrites_properly() {
246 let mut cm = ChecksumContextMap::new(2048);
247 cm.set(42, 100);
248 cm.set(42, 200);
249 assert_eq!(cm.get(42), 200);
250 }
251
252 #[test]
255 fn assoc_new_entries_return_zero() {
256 let cm = AssociativeContextMap::new(4096);
257 assert_eq!(cm.get(0), 0);
258 assert_eq!(cm.get(12345), 0);
259 }
260
261 #[test]
262 fn assoc_set_and_get() {
263 let mut cm = AssociativeContextMap::new(4096);
264 cm.set(42, 128);
265 assert_eq!(cm.get(42), 128);
266 }
267
268 #[test]
269 fn assoc_two_entries_same_set() {
270 let mut cm = AssociativeContextMap::new(16); cm.set(0x00010000, 100); cm.set(0x00020000, 200); assert_eq!(cm.get(0x00010000), 100);
276 assert_eq!(cm.get(0x00020000), 200);
277 }
278
279 #[test]
280 fn assoc_overwrites_properly() {
281 let mut cm = AssociativeContextMap::new(4096);
282 cm.set(42, 100);
283 cm.set(42, 200);
284 assert_eq!(cm.get(42), 200);
285 }
286}