1use crate::{
2 db::{
3 index::{
4 entry::RawIndexEntry,
5 fingerprint,
6 key::{IndexId, IndexKey, RawIndexKey},
7 },
8 store::{DataKey, StoreRegistry},
9 },
10 error::{ErrorClass, ErrorOrigin, InternalError},
11 model::index::IndexModel,
12 traits::{EntityKind, Storable},
13 value::Value,
14};
15use canic_cdk::structures::{BTreeMap, DefaultMemoryImpl, memory::VirtualMemory, storable::Bound};
16use canic_utils::hash::Xxh3;
17use derive_more::{Deref, DerefMut};
18use std::borrow::Cow;
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44struct RawIndexFingerprint([u8; 16]);
45
46impl RawIndexFingerprint {
47 const STORED_SIZE: u32 = 16;
48}
49
50impl Storable for RawIndexFingerprint {
51 fn to_bytes(&self) -> Cow<'_, [u8]> {
52 Cow::Borrowed(&self.0)
53 }
54
55 fn from_bytes(bytes: Cow<'_, [u8]>) -> Self {
56 let mut out = [0u8; 16];
57 if bytes.len() == out.len() {
58 out.copy_from_slice(bytes.as_ref());
59 }
60 Self(out)
61 }
62
63 fn into_bytes(self) -> Vec<u8> {
64 self.0.to_vec()
65 }
66
67 const BOUND: Bound = Bound::Bounded {
68 max_size: Self::STORED_SIZE,
69 is_fixed_size: true,
70 };
71}
72
73#[derive(Deref, DerefMut)]
78pub struct IndexStoreRegistry(StoreRegistry<IndexStore>);
79
80impl IndexStoreRegistry {
81 #[must_use]
82 pub fn new() -> Self {
83 Self(StoreRegistry::new())
84 }
85}
86
87impl Default for IndexStoreRegistry {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93pub struct IndexStore {
98 entry: VirtualMemory<DefaultMemoryImpl>,
99 fingerprint: VirtualMemory<DefaultMemoryImpl>,
100}
101
102impl IndexStore {
103 #[must_use]
104 pub const fn init(
105 entry: VirtualMemory<DefaultMemoryImpl>,
106 fingerprint: VirtualMemory<DefaultMemoryImpl>,
107 ) -> Self {
108 Self { entry, fingerprint }
109 }
110
111 pub fn entries(&self) -> Vec<(RawIndexKey, RawIndexEntry)> {
113 self.entry_map()
114 .iter()
115 .map(|e| (*e.key(), e.value()))
116 .collect()
117 }
118
119 pub fn len(&self) -> u64 {
120 self.entry_map().len()
121 }
122
123 pub fn is_empty(&self) -> bool {
124 self.entry_map().is_empty()
125 }
126
127 pub fn get(&self, key: &RawIndexKey) -> Option<RawIndexEntry> {
128 let entry = self.entry_map().get(key);
129
130 #[cfg(debug_assertions)]
133 if let Some(ref value) = entry
134 && let Err(err) = self.verify_entry_fingerprint(None, key, value)
135 {
136 panic!("index fingerprint verification failed: {err:?} (debug-only)");
137 }
138
139 entry
140 }
141
142 pub fn insert(&mut self, key: RawIndexKey, value: RawIndexEntry) -> Option<RawIndexEntry> {
143 let fingerprint = Self::entry_fingerprint(&key, &value);
144 let prev = self.entry_map().insert(key, value);
145
146 let _ = self.fingerprint_map().insert(key, fingerprint);
149
150 prev
151 }
152
153 pub fn remove(&mut self, key: &RawIndexKey) -> Option<RawIndexEntry> {
154 let removed = self.entry_map().remove(key);
155
156 let _ = self.fingerprint_map().remove(key);
158
159 removed
160 }
161
162 pub fn clear(&mut self) {
163 self.entry_map().clear();
164 self.fingerprint_map().clear();
165 }
166
167 pub(crate) fn resolve_data_values<E: EntityKind>(
168 &self,
169 index: &IndexModel,
170 prefix: &[Value],
171 ) -> Result<Vec<DataKey>, InternalError> {
172 if prefix.len() > index.fields.len() {
173 return Err(InternalError::new(
174 ErrorClass::Unsupported,
175 ErrorOrigin::Index,
176 format!(
177 "index prefix length {} exceeds field count {}",
178 prefix.len(),
179 index.fields.len()
180 ),
181 ));
182 }
183
184 let index_id = IndexId::new::<E>(index);
185
186 let mut fps = Vec::with_capacity(prefix.len());
187 for value in prefix {
188 let Some(fp) = fingerprint::to_index_fingerprint(value)? else {
189 return Err(InternalError::new(
190 ErrorClass::Unsupported,
191 ErrorOrigin::Index,
192 "index prefix value is not indexable",
193 ));
194 };
195 fps.push(fp);
196 }
197
198 let (start, end) = IndexKey::bounds_for_prefix(index_id, index.fields.len(), &fps);
199 let (start_raw, end_raw) = (start.to_raw(), end.to_raw());
200
201 let mut out = Vec::new();
202
203 for entry in self.entry_map().range(start_raw..=end_raw) {
204 let raw_key = entry.key();
205 let raw_entry = entry.value();
206
207 #[cfg(debug_assertions)]
208 if let Err(err) = self.verify_entry_fingerprint(Some(index), raw_key, &raw_entry) {
209 panic!("index fingerprint verification failed: {err:?} (debug-only)");
210 }
211
212 IndexKey::try_from_raw(raw_key).map_err(|err| {
214 InternalError::new(
215 ErrorClass::Corruption,
216 ErrorOrigin::Index,
217 format!("index key corrupted during resolve: {err}"),
218 )
219 })?;
220
221 let storage_keys = raw_entry.decode_keys().map_err(|err| {
223 InternalError::new(ErrorClass::Corruption, ErrorOrigin::Index, err.to_string())
224 })?;
225
226 if index.unique && storage_keys.len() != 1 {
227 return Err(InternalError::new(
228 ErrorClass::Corruption,
229 ErrorOrigin::Index,
230 "unique index entry contains an unexpected number of keys",
231 ));
232 }
233
234 out.extend(
236 storage_keys
237 .into_iter()
238 .map(|sk| DataKey::from_storage_key::<E>(sk)),
239 );
240 }
241
242 #[cfg(debug_assertions)]
243 self.debug_verify_no_orphaned_fingerprints(index, &start_raw, &end_raw);
244
245 Ok(out)
246 }
247
248 pub fn memory_bytes(&self) -> u64 {
249 let entry_bytes = self
250 .entry_map()
251 .iter()
252 .map(|e| {
253 let v: RawIndexEntry = e.value();
254 IndexKey::STORED_SIZE_BYTES + v.len() as u64
255 })
256 .sum::<u64>();
257
258 let fingerprint_bytes = self
259 .fingerprint_map()
260 .iter()
261 .map(|_| IndexKey::STORED_SIZE_BYTES + u64::from(RawIndexFingerprint::STORED_SIZE))
262 .sum::<u64>();
263
264 entry_bytes.saturating_add(fingerprint_bytes)
265 }
266
267 fn entry_map(&self) -> BTreeMap<RawIndexKey, RawIndexEntry, VirtualMemory<DefaultMemoryImpl>> {
268 BTreeMap::init(self.entry.clone())
269 }
270
271 fn fingerprint_map(
272 &self,
273 ) -> BTreeMap<RawIndexKey, RawIndexFingerprint, VirtualMemory<DefaultMemoryImpl>> {
274 BTreeMap::init(self.fingerprint.clone())
275 }
276
277 fn entry_fingerprint(key: &RawIndexKey, entry: &RawIndexEntry) -> RawIndexFingerprint {
278 const VERSION: u8 = 1;
279
280 let mut h = Xxh3::with_seed(0);
281 h.update(&[VERSION]);
282 h.update(key.as_bytes());
283 h.update(entry.as_bytes());
284
285 RawIndexFingerprint(h.digest128().to_be_bytes())
286 }
287
288 #[cfg(debug_assertions)]
289 fn verify_entry_fingerprint(
290 &self,
291 index: Option<&IndexModel>,
292 key: &RawIndexKey,
293 entry: &RawIndexEntry,
294 ) -> Result<(), Box<FingerprintVerificationError>> {
295 let expected = Self::entry_fingerprint(key, entry);
296 let stored = self.fingerprint_map().get(key);
297
298 let label = index
299 .map(|idx| format!("index='{}'", idx.name))
300 .or_else(|| {
301 IndexKey::try_from_raw(key)
302 .ok()
303 .map(|decoded| format!("index_key={decoded:?}"))
304 })
305 .unwrap_or_else(|| "index=<unknown>".to_string());
306
307 match stored {
308 None => Err(Box::new(FingerprintVerificationError::Missing {
309 label,
310 key: *key,
311 })),
312 Some(actual) if actual != expected => {
313 Err(Box::new(FingerprintVerificationError::Mismatch {
314 label,
315 key: *key,
316 expected,
317 actual,
318 }))
319 }
320 Some(_) => Ok(()),
321 }
322 }
323
324 #[cfg(test)]
325 #[expect(dead_code)]
326 pub(crate) fn debug_fingerprint_for(&self, key: &RawIndexKey) -> Option<[u8; 16]> {
327 self.fingerprint_map().get(key).map(|fp| fp.0)
328 }
329
330 #[cfg(debug_assertions)]
331 fn debug_verify_no_orphaned_fingerprints(
332 &self,
333 index: &IndexModel,
334 start: &RawIndexKey,
335 end: &RawIndexKey,
336 ) {
337 for fp in self.fingerprint_map().range(*start..=*end) {
338 assert!(
339 self.entry_map().get(fp.key()).is_some(),
340 "index fingerprint orphaned: index='{}' key={:?} (debug-only)",
341 index.name,
342 fp.key()
343 );
344 }
345 }
346}
347
348#[cfg(debug_assertions)]
353#[allow(dead_code)]
354#[derive(Debug)]
355enum FingerprintVerificationError {
356 Missing {
357 label: String,
358 key: RawIndexKey,
359 },
360 Mismatch {
361 label: String,
362 key: RawIndexKey,
363 expected: RawIndexFingerprint,
364 actual: RawIndexFingerprint,
365 },
366}