Skip to main content

icydb_core/db/index/
store.rs

1//! Module: index::store
2//! Responsibility: stable index-entry persistence primitives.
3//! Does not own: range-scan resolution, continuation semantics, or predicate execution.
4//! Boundary: scan/executor layers depend on this storage boundary.
5
6use crate::db::index::{entry::RawIndexEntry, key::RawIndexKey};
7
8use canic_cdk::structures::{BTreeMap, DefaultMemoryImpl, memory::VirtualMemory};
9
10///
11/// IndexStore
12///
13/// Thin persistence wrapper over one stable BTreeMap.
14///
15/// Invariant: callers provide already-validated `RawIndexKey`/`RawIndexEntry`.
16///
17
18pub struct IndexStore {
19    pub(super) map: BTreeMap<RawIndexKey, RawIndexEntry, VirtualMemory<DefaultMemoryImpl>>,
20    generation: u64,
21    secondary_covering_authoritative: bool,
22}
23
24impl IndexStore {
25    #[must_use]
26    pub fn init(memory: VirtualMemory<DefaultMemoryImpl>) -> Self {
27        Self {
28            map: BTreeMap::init(memory),
29            generation: 0,
30            secondary_covering_authoritative: false,
31        }
32    }
33
34    /// Snapshot all index entry pairs (diagnostics only).
35    #[allow(clippy::redundant_closure_for_method_calls)]
36    pub(crate) fn entries(&self) -> Vec<(RawIndexKey, RawIndexEntry)> {
37        self.map.iter().map(|entry| entry.into_pair()).collect()
38    }
39
40    pub(in crate::db) fn get(&self, key: &RawIndexKey) -> Option<RawIndexEntry> {
41        self.map.get(key)
42    }
43
44    pub fn len(&self) -> u64 {
45        self.map.len()
46    }
47
48    pub fn is_empty(&self) -> bool {
49        self.map.is_empty()
50    }
51
52    #[must_use]
53    pub(in crate::db) const fn generation(&self) -> u64 {
54        self.generation
55    }
56
57    pub(crate) fn insert(
58        &mut self,
59        key: RawIndexKey,
60        entry: RawIndexEntry,
61    ) -> Option<RawIndexEntry> {
62        let previous = self.map.insert(key, entry);
63        self.bump_generation();
64        self.invalidate_secondary_covering_authority();
65        previous
66    }
67
68    pub(crate) fn remove(&mut self, key: &RawIndexKey) -> Option<RawIndexEntry> {
69        let previous = self.map.remove(key);
70        self.bump_generation();
71        self.invalidate_secondary_covering_authority();
72        previous
73    }
74
75    pub fn clear(&mut self) {
76        self.map.clear();
77        self.bump_generation();
78        self.invalidate_secondary_covering_authority();
79    }
80
81    /// Return whether this secondary-index store currently participates in a
82    /// synchronized covering-authority witness with its paired row store.
83    #[must_use]
84    pub(in crate::db) const fn secondary_covering_authoritative(&self) -> bool {
85        self.secondary_covering_authoritative
86    }
87
88    /// Mark this secondary-index store as synchronized with its paired row
89    /// store after successful commit or recovery.
90    pub(in crate::db) const fn mark_secondary_covering_authoritative(&mut self) {
91        self.secondary_covering_authoritative = true;
92    }
93
94    /// Sum of bytes used by all stored index entries.
95    pub fn memory_bytes(&self) -> u64 {
96        self.map
97            .iter()
98            .map(|entry| entry.key().as_bytes().len() as u64 + entry.value().len() as u64)
99            .sum()
100    }
101
102    const fn bump_generation(&mut self) {
103        self.generation = self.generation.saturating_add(1);
104    }
105
106    const fn invalidate_secondary_covering_authority(&mut self) {
107        self.secondary_covering_authoritative = false;
108    }
109}