icydb_core/db/index/
store.rs1use crate::db::index::{IndexEntryValue, key::RawIndexStoreKey};
7
8use candid::CandidType;
9use ic_memory::stable_structures::{
10 BTreeMap as StableBTreeMap, DefaultMemoryImpl, memory_manager::VirtualMemory,
11};
12use serde::Deserialize;
13use std::collections::BTreeMap as HeapBTreeMap;
14
15#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
23pub enum IndexState {
24 Building,
25 #[default]
26 Ready,
27 Dropping,
28}
29
30impl IndexState {
31 #[must_use]
33 pub const fn as_str(self) -> &'static str {
34 match self {
35 Self::Building => "building",
36 Self::Ready => "ready",
37 Self::Dropping => "dropping",
38 }
39 }
40}
41
42pub struct IndexStore {
51 pub(super) backend: IndexStoreBackend,
52 generation: u64,
53 state: IndexState,
54}
55
56pub(super) enum IndexStoreBackend {
57 Stable(StableBTreeMap<RawIndexStoreKey, IndexEntryValue, VirtualMemory<DefaultMemoryImpl>>),
58 Heap(HeapBTreeMap<RawIndexStoreKey, IndexEntryValue>),
59}
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub(in crate::db) enum IndexStoreVisit {
64 Continue,
65 #[allow(
66 dead_code,
67 reason = "index traversal exposes early-stop semantics for bounded future callers; focused tests cover it before live call sites need it"
68 )]
69 Stop,
70}
71
72impl IndexStoreVisit {
73 const fn should_stop(self) -> bool {
74 matches!(self, Self::Stop)
75 }
76}
77
78impl IndexStore {
79 #[must_use]
80 pub fn init(memory: VirtualMemory<DefaultMemoryImpl>) -> Self {
81 Self {
82 backend: IndexStoreBackend::Stable(StableBTreeMap::init(memory)),
83 generation: 0,
84 state: IndexState::Ready,
87 }
88 }
89
90 #[must_use]
92 pub const fn init_heap() -> Self {
93 Self {
94 backend: IndexStoreBackend::Heap(HeapBTreeMap::new()),
95 generation: 0,
96 state: IndexState::Ready,
97 }
98 }
99
100 #[must_use]
102 pub(in crate::db) const fn is_heap_storage(&self) -> bool {
103 matches!(self.backend, IndexStoreBackend::Heap(_))
104 }
105
106 pub(in crate::db) fn visit_entries<E>(
109 &self,
110 mut visitor: impl FnMut(&RawIndexStoreKey, &IndexEntryValue) -> Result<IndexStoreVisit, E>,
111 ) -> Result<(), E> {
112 match &self.backend {
113 IndexStoreBackend::Stable(map) => {
114 for entry in map.iter() {
115 if visitor(entry.key(), &entry.value())?.should_stop() {
116 return Ok(());
117 }
118 }
119 }
120 IndexStoreBackend::Heap(map) => {
121 for (key, value) in map {
122 if visitor(key, value)?.should_stop() {
123 return Ok(());
124 }
125 }
126 }
127 }
128
129 Ok(())
130 }
131
132 pub(in crate::db) fn get(&self, key: &RawIndexStoreKey) -> Option<IndexEntryValue> {
133 match &self.backend {
134 IndexStoreBackend::Stable(map) => map.get(key),
135 IndexStoreBackend::Heap(map) => map.get(key).cloned(),
136 }
137 }
138
139 pub fn len(&self) -> u64 {
140 match &self.backend {
141 IndexStoreBackend::Stable(map) => map.len(),
142 IndexStoreBackend::Heap(map) => u64::try_from(map.len()).unwrap_or(u64::MAX),
143 }
144 }
145
146 pub fn is_empty(&self) -> bool {
147 match &self.backend {
148 IndexStoreBackend::Stable(map) => map.is_empty(),
149 IndexStoreBackend::Heap(map) => map.is_empty(),
150 }
151 }
152
153 #[must_use]
154 pub(in crate::db) const fn generation(&self) -> u64 {
155 self.generation
156 }
157
158 #[must_use]
160 pub(in crate::db) const fn state(&self) -> IndexState {
161 self.state
162 }
163
164 pub(in crate::db) const fn mark_building(&mut self) {
167 self.state = IndexState::Building;
168 }
169
170 pub(in crate::db) const fn mark_ready(&mut self) {
172 self.state = IndexState::Ready;
173 }
174
175 pub(in crate::db) const fn mark_dropping(&mut self) {
177 self.state = IndexState::Dropping;
178 }
179
180 pub(crate) fn insert(
181 &mut self,
182 key: RawIndexStoreKey,
183 entry: IndexEntryValue,
184 ) -> Option<IndexEntryValue> {
185 let previous = match &mut self.backend {
186 IndexStoreBackend::Stable(map) => map.insert(key, entry),
187 IndexStoreBackend::Heap(map) => map.insert(key, entry),
188 };
189 self.bump_generation();
190 previous
191 }
192
193 pub(crate) fn remove(&mut self, key: &RawIndexStoreKey) -> Option<IndexEntryValue> {
194 let previous = match &mut self.backend {
195 IndexStoreBackend::Stable(map) => map.remove(key),
196 IndexStoreBackend::Heap(map) => map.remove(key),
197 };
198 self.bump_generation();
199 previous
200 }
201
202 pub fn clear(&mut self) {
203 match &mut self.backend {
204 IndexStoreBackend::Stable(map) => map.clear_new(),
205 IndexStoreBackend::Heap(map) => map.clear(),
206 }
207 self.bump_generation();
208 }
209
210 pub fn memory_bytes(&self) -> u64 {
212 let mut bytes = 0u64;
213 let _: Result<(), std::convert::Infallible> = self.visit_entries(|key, value| {
214 bytes = bytes.saturating_add(key.as_bytes().len() as u64 + value.len() as u64);
215 Ok(IndexStoreVisit::Continue)
216 });
217 bytes
218 }
219
220 const fn bump_generation(&mut self) {
221 self.generation = self.generation.saturating_add(1);
222 }
223}