cuckoo_clock/
associated_data.rs1use std::{borrow::Borrow, fmt::Display};
2
3use crate::{config::CuckooConfiguration, data_block::DataBlock};
4
5#[derive(Debug)]
7pub enum AccessError {
8 FeatureNotEnabled(String),
11}
12
13impl Display for AccessError {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 match self {
16 AccessError::FeatureNotEnabled(feature) => {
17 f.write_str(&format!("Feature ({feature}) not enabled."))
18 }
19 }
20 }
21}
22
23impl std::error::Error for AccessError {
24 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
25 None
26 }
27}
28
29pub struct AssociatedData {
62 data: Box<[u8]>,
63 configuration: CuckooConfiguration,
64}
65
66impl AssociatedData {
67 pub(crate) fn new<T: Borrow<[u8]>>(
68 data: DataBlock<T>,
69 configuration: CuckooConfiguration,
70 ) -> Self {
71 Self {
72 data: data.inner().into(),
73 configuration,
74 }
75 }
76
77 #[must_use]
82 pub fn get_fingerprint(&self) -> u32 {
83 DataBlock::from(&self.data[..])
84 .get_fingerprint(&self.configuration)
85 .data()
86 }
87
88 pub fn get_lru_counter(&self) -> Result<u32, AccessError> {
90 Ok(DataBlock::from(&self.data[..]).get_lru_counter(
91 self.configuration
92 .lru_field_config
93 .as_ref()
94 .ok_or(AccessError::FeatureNotEnabled("LRU".to_string()))?,
95 ))
96 }
97
98 pub fn get_counter(&self) -> Result<u32, AccessError> {
100 Ok(DataBlock::from(&self.data[..]).get_counter(
101 self.configuration
102 .counter_field_config
103 .as_ref()
104 .ok_or(AccessError::FeatureNotEnabled("Counter".to_string()))?,
105 ))
106 }
107
108 pub fn get_stored_ttl_value(&self) -> Result<u32, AccessError> {
113 Ok(DataBlock::from(&self.data[..]).get_ttl(
114 self.configuration
115 .ttl_field_config
116 .as_ref()
117 .ok_or(AccessError::FeatureNotEnabled("TTL".to_string()))?,
118 ))
119 }
120}
121
122#[cfg(test)]
123#[expect(clippy::unwrap_used)]
124mod tests {
125 use crate::{
126 Fingerprint,
127 config::{LruConfig, TtlConfig},
128 };
129
130 use super::*;
131
132 #[test]
133 #[allow(unused_assignments)]
134 fn basic_fingerprint_access() {
135 for bit_count in 1..=32 {
136 let config = CuckooConfiguration::builder(1000)
137 .fingerprint_bits(bit_count.try_into().unwrap())
138 .build()
139 .unwrap();
140
141 let mut data = [0u8; 4];
142 let mut data_block = DataBlock::from(&mut data[..]);
143 data_block.store_fingerprint(&Fingerprint::new(1, 1), &config);
144
145 let associated_data = AssociatedData::new(data_block, config);
146 assert_eq!(associated_data.get_fingerprint(), 1);
147
148 data[0] = 100;
151 assert_eq!(associated_data.get_fingerprint(), 1);
152 }
153 }
154
155 #[test]
156 fn disabled_features() {
157 let config = CuckooConfiguration::builder(1000)
159 .fingerprint_bits(8.try_into().unwrap())
160 .build()
161 .unwrap();
162
163 let data = [0u8; 4];
164 let data_block = DataBlock::from(&data[..]);
165 let associated_data = AssociatedData::new(data_block, config);
166
167 let Err(AccessError::FeatureNotEnabled(feature_name)) =
168 associated_data.get_stored_ttl_value()
169 else {
170 panic!("TTL not enabled error should be returned!");
171 };
172 assert_eq!(feature_name, "TTL");
173
174 let Err(AccessError::FeatureNotEnabled(feature_name)) = associated_data.get_lru_counter()
175 else {
176 panic!("LRU not enabled error should be returned!");
177 };
178 assert_eq!(feature_name, "LRU");
179
180 let Err(AccessError::FeatureNotEnabled(feature_name)) = associated_data.get_counter()
181 else {
182 panic!("Counter not enabled error should be returned!");
183 };
184 assert_eq!(feature_name, "Counter");
185 }
186
187 #[test]
188 #[allow(unused_assignments)]
189 fn ttl_access() {
190 for bit_count in 1..=32 {
191 let config = CuckooConfiguration::builder(1000)
193 .fingerprint_bits(5.try_into().unwrap())
194 .with_ttl(TtlConfig {
195 ttl: 10.try_into().unwrap(),
196 ttl_bits: bit_count.try_into().unwrap(),
197 })
198 .build()
199 .unwrap();
200
201 let mut data = [0u8; 5];
202 let mut data_block = DataBlock::from(&mut data[..]);
203 data_block.set_ttl(config.ttl_field_config.as_ref().unwrap(), 1);
204 let associated_data = AssociatedData::new(data_block, config);
205
206 assert_eq!(associated_data.get_stored_ttl_value().unwrap(), 1);
207
208 data[0] = 7;
211 assert_eq!(associated_data.get_stored_ttl_value().unwrap(), 1);
212 }
213 }
214
215 #[test]
216 #[allow(unused_assignments)]
217 fn lru_counter_access() {
218 for bit_count in 1..=32 {
219 let config = CuckooConfiguration::builder(1000)
221 .fingerprint_bits(5.try_into().unwrap())
222 .with_lru(LruConfig {
223 counter_bits: bit_count.try_into().unwrap(),
224 ..Default::default()
225 })
226 .build()
227 .unwrap();
228
229 let mut data = [0u8; 5];
230 let mut data_block = DataBlock::from(&mut data[..]);
231 data_block.inc_lru_counter(config.lru_field_config.as_ref().unwrap());
232 let associated_data = AssociatedData::new(data_block, config);
233
234 assert_eq!(associated_data.get_lru_counter().unwrap(), 1);
235
236 data[0] = 7;
239 assert_eq!(associated_data.get_lru_counter().unwrap(), 1);
240 }
241 }
242}