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 pub(in crate::db) fn visit_entries<E>(
103 &self,
104 mut visitor: impl FnMut(&RawIndexStoreKey, &IndexEntryValue) -> Result<IndexStoreVisit, E>,
105 ) -> Result<(), E> {
106 match &self.backend {
107 IndexStoreBackend::Stable(map) => {
108 for entry in map.iter() {
109 if visitor(entry.key(), &entry.value())?.should_stop() {
110 return Ok(());
111 }
112 }
113 }
114 IndexStoreBackend::Heap(map) => {
115 for (key, value) in map {
116 if visitor(key, value)?.should_stop() {
117 return Ok(());
118 }
119 }
120 }
121 }
122
123 Ok(())
124 }
125
126 pub(in crate::db) fn get(&self, key: &RawIndexStoreKey) -> Option<IndexEntryValue> {
127 match &self.backend {
128 IndexStoreBackend::Stable(map) => map.get(key),
129 IndexStoreBackend::Heap(map) => map.get(key).cloned(),
130 }
131 }
132
133 pub fn len(&self) -> u64 {
134 match &self.backend {
135 IndexStoreBackend::Stable(map) => map.len(),
136 IndexStoreBackend::Heap(map) => u64::try_from(map.len()).unwrap_or(u64::MAX),
137 }
138 }
139
140 pub fn is_empty(&self) -> bool {
141 match &self.backend {
142 IndexStoreBackend::Stable(map) => map.is_empty(),
143 IndexStoreBackend::Heap(map) => map.is_empty(),
144 }
145 }
146
147 #[must_use]
148 pub(in crate::db) const fn generation(&self) -> u64 {
149 self.generation
150 }
151
152 #[must_use]
154 pub(in crate::db) const fn state(&self) -> IndexState {
155 self.state
156 }
157
158 pub(in crate::db) const fn mark_building(&mut self) {
161 self.state = IndexState::Building;
162 }
163
164 pub(in crate::db) const fn mark_ready(&mut self) {
166 self.state = IndexState::Ready;
167 }
168
169 pub(in crate::db) const fn mark_dropping(&mut self) {
171 self.state = IndexState::Dropping;
172 }
173
174 pub(crate) fn insert(
175 &mut self,
176 key: RawIndexStoreKey,
177 entry: IndexEntryValue,
178 ) -> Option<IndexEntryValue> {
179 let previous = match &mut self.backend {
180 IndexStoreBackend::Stable(map) => map.insert(key, entry),
181 IndexStoreBackend::Heap(map) => map.insert(key, entry),
182 };
183 self.bump_generation();
184 previous
185 }
186
187 pub(crate) fn remove(&mut self, key: &RawIndexStoreKey) -> Option<IndexEntryValue> {
188 let previous = match &mut self.backend {
189 IndexStoreBackend::Stable(map) => map.remove(key),
190 IndexStoreBackend::Heap(map) => map.remove(key),
191 };
192 self.bump_generation();
193 previous
194 }
195
196 pub fn clear(&mut self) {
197 match &mut self.backend {
198 IndexStoreBackend::Stable(map) => map.clear_new(),
199 IndexStoreBackend::Heap(map) => map.clear(),
200 }
201 self.bump_generation();
202 }
203
204 pub fn memory_bytes(&self) -> u64 {
206 let mut bytes = 0u64;
207 let _: Result<(), std::convert::Infallible> = self.visit_entries(|key, value| {
208 bytes = bytes.saturating_add(key.as_bytes().len() as u64 + value.len() as u64);
209 Ok(IndexStoreVisit::Continue)
210 });
211 bytes
212 }
213
214 const fn bump_generation(&mut self) {
215 self.generation = self.generation.saturating_add(1);
216 }
217}