ic_storage/adapter.rs
1//! Supported upstream collections with governed memory and an explicit API.
2//!
3//! Constructors preserve upstream initialization, panic and persisted-format
4//! contracts. These wrappers expose neither raw memory nor unrestricted
5//! upstream collections.
6//! Use distinct admitted allocations for owning collections. Region views retain
7//! a shared lease for disjoint application frames and reload roots per operation.
8//! Drop collections and all shared lease owners before reopening.
9//! IC Memory enforces one live lease per allocation within each runtime;
10//! hosts retain exclusive custody of the backing across runtimes.
11use crate::substrate::{self, DefaultMemoryImpl};
12use ic_memory::RuntimeMemory;
13use std::{borrow::Cow, ops::RangeBounds};
14/// Exact substrate interfaces for backing bounds and record codecs.
15/// Implementing `Memory` grants no allocation authority; constructors require
16/// committed `RuntimeMemory` leases.
17pub use substrate::{Memory, Storable, storable, storable::Bound};
18
19#[cfg(all(test, not(target_arch = "wasm32")))]
20mod tests;
21
22mod region;
23pub use region::{BTreeMapRegion, RegionGrowError};
24
25/// Governed upstream `BTreeMap`; initialization and failures follow upstream contracts.
26pub struct StableBTreeMap<K: Storable + Ord + Clone, V: Storable, M: Memory = DefaultMemoryImpl> {
27 inner: substrate::BTreeMap<K, V, RuntimeMemory<M>>,
28}
29impl<K: Storable + Ord + Clone, V: Storable, M: Memory> StableBTreeMap<K, V, M> {
30 /// Create a new collection, replacing any prior collection in the selected memory.
31 /// This is destructive even when a valid collection already exists.
32 ///
33 /// # Panics
34 /// Panics on insufficient memory or codec/backing failure.
35 pub fn new(memory: RuntimeMemory<M>) -> Self {
36 Self {
37 inner: substrate::BTreeMap::new(memory),
38 }
39 }
40 /// Open a recognized retained collection; initialize a new one when memory
41 /// is empty or has foreign magic. Foreign contents may be overwritten.
42 /// This is not a corruption-recovery or schema-admission operation.
43 ///
44 /// # Panics
45 /// Panics for unsupported recognized layouts, invalid retained contents,
46 /// insufficient memory or codec/backing failure.
47 pub fn init(memory: RuntimeMemory<M>) -> Self {
48 Self {
49 inner: substrate::BTreeMap::init(memory),
50 }
51 }
52 /// Load an existing tree without initializing a replacement.
53 ///
54 /// # Panics
55 /// Panics for missing/foreign headers, unsupported layouts or backing failure.
56 /// Record decoding can also panic when records are subsequently accessed.
57 pub fn load(memory: RuntimeMemory<M>) -> Self {
58 Self {
59 inner: substrate::BTreeMap::load(memory),
60 }
61 }
62 /// Clear entries while retaining this allocation's exclusive lease.
63 pub fn clear(&mut self) {
64 self.inner.clear_new();
65 }
66 /// Reserve a minimum virtual extent without exposing or duplicating the lease.
67 ///
68 /// Returns the map on both success and ordinary growth refusal. Entries are
69 /// preserved; reloading the upstream header refreshes its cached allocator.
70 /// Backing traps retain the upstream panic contract.
71 ///
72 /// `minimum` is an absolute virtual extent in 65,536-byte Wasm pages, not an
73 /// entry count or additional pages. Existing extents never shrink. A request
74 /// already covered performs no growth or writes. This reserves backing only:
75 /// it does not promise that any particular number or encoding of entries fits.
76 /// Callers own their byte/page budget and must qualify it for their codecs;
77 /// upstream node-layout constants are not a supported capacity contract.
78 pub fn reserve_pages(self, minimum: u64) -> (Self, Result<(), ic_memory::RuntimeGrowError>) {
79 let memory = self.inner.into_memory();
80 let additional = minimum.saturating_sub(memory.size());
81 let result = memory.grow(additional).map(|_| ());
82 (Self::load(memory), result)
83 }
84 /// Observe the virtual extent in 65,536-byte pages while retaining the lease.
85 ///
86 /// Consumes and returns the map because upstream exposes memory only through
87 /// ownership transfer. Reloading reads the header without writing or growing.
88 /// This is allocated extent, not free space or guaranteed entry capacity.
89 #[must_use]
90 pub fn allocated_pages(self) -> (Self, u64) {
91 let memory = self.inner.into_memory();
92 let pages = memory.size();
93 (Self::load(memory), pages)
94 }
95 /// Delegates to upstream `BTreeMap::insert` with the governed allocation.
96 pub fn insert(&mut self, key: K, value: V) -> Option<V> {
97 self.inner.insert(key, value)
98 }
99 /// Delegates to upstream `BTreeMap::get` with the governed allocation.
100 pub fn get(&self, key: &K) -> Option<V> {
101 self.inner.get(key)
102 }
103 /// Delegates to upstream `BTreeMap::contains_key` with the governed allocation.
104 pub fn contains_key(&self, key: &K) -> bool {
105 self.inner.contains_key(key)
106 }
107 /// Delegates to upstream `BTreeMap::is_empty` with the governed allocation.
108 pub fn is_empty(&self) -> bool {
109 self.inner.is_empty()
110 }
111 /// Delegates to upstream `BTreeMap::len` with the governed allocation.
112 pub fn len(&self) -> u64 {
113 self.inner.len()
114 }
115 /// Delegates to upstream `BTreeMap::remove` with the governed allocation.
116 pub fn remove(&mut self, key: &K) -> Option<V> {
117 self.inner.remove(key)
118 }
119 /// Delegates to upstream `BTreeMap::first_key_value` with the governed allocation.
120 pub fn first_key_value(&self) -> Option<(K, V)> {
121 self.inner.first_key_value()
122 }
123 /// Delegates to upstream `BTreeMap::last_key_value` with the governed allocation.
124 pub fn last_key_value(&self) -> Option<(K, V)> {
125 self.inner.last_key_value()
126 }
127 /// Delegates to upstream `BTreeMap::pop_first` with the governed allocation.
128 pub fn pop_first(&mut self) -> Option<(K, V)> {
129 self.inner.pop_first()
130 }
131 /// Delegates to upstream `BTreeMap::pop_last` with the governed allocation.
132 pub fn pop_last(&mut self) -> Option<(K, V)> {
133 self.inner.pop_last()
134 }
135 /// Delegates to upstream `BTreeMap::iter` with the governed allocation.
136 pub fn iter(&self) -> MapIter<'_, K, V, M> {
137 self.inner.iter()
138 }
139 /// Delegates to upstream `BTreeMap::range` with the governed allocation.
140 pub fn range(&self, key_range: impl RangeBounds<K>) -> MapIter<'_, K, V, M> {
141 self.inner.range(key_range)
142 }
143 /// Delegates to upstream `BTreeMap::keys` with the governed allocation.
144 pub fn keys(&self) -> impl DoubleEndedIterator<Item = K> + '_ {
145 self.inner.keys()
146 }
147 /// Delegates to upstream `BTreeMap::values` with the governed allocation.
148 pub fn values(&self) -> impl DoubleEndedIterator<Item = V> + '_ {
149 self.inner.values()
150 }
151}
152
153/// Governed upstream `BTreeSet`; initialization and failures follow upstream contracts.
154pub struct StableBTreeSet<K: Storable + Ord + Clone, M: Memory = DefaultMemoryImpl> {
155 inner: substrate::BTreeSet<K, RuntimeMemory<M>>,
156}
157impl<K: Storable + Ord + Clone, M: Memory> StableBTreeSet<K, M> {
158 /// Create a new collection, replacing any prior collection in the selected memory.
159 /// This is destructive even when a valid collection already exists.
160 ///
161 /// # Panics
162 /// Panics on insufficient memory or codec/backing failure.
163 pub fn new(memory: RuntimeMemory<M>) -> Self {
164 Self {
165 inner: substrate::BTreeSet::new(memory),
166 }
167 }
168 /// Open a recognized retained collection; initialize a new one when memory
169 /// is empty or has foreign magic. Foreign contents may be overwritten.
170 /// This is not a corruption-recovery or schema-admission operation.
171 ///
172 /// # Panics
173 /// Panics for unsupported recognized layouts, invalid retained contents,
174 /// insufficient memory or codec/backing failure.
175 pub fn init(memory: RuntimeMemory<M>) -> Self {
176 Self {
177 inner: substrate::BTreeSet::init(memory),
178 }
179 }
180 /// Load an existing tree without initializing a replacement.
181 ///
182 /// # Panics
183 /// Panics for missing/foreign headers, unsupported layouts or backing failure.
184 /// Record decoding can also panic when records are subsequently accessed.
185 pub fn load(memory: RuntimeMemory<M>) -> Self {
186 Self {
187 inner: substrate::BTreeSet::load(memory),
188 }
189 }
190 /// Delegates to upstream `BTreeSet::insert` with the governed allocation.
191 pub fn insert(&mut self, key: K) -> bool {
192 self.inner.insert(key)
193 }
194 /// Delegates to upstream `BTreeSet::contains` with the governed allocation.
195 pub fn contains(&self, key: &K) -> bool {
196 self.inner.contains(key)
197 }
198 /// Delegates to upstream `BTreeSet::is_empty` with the governed allocation.
199 pub fn is_empty(&self) -> bool {
200 self.inner.is_empty()
201 }
202 /// Delegates to upstream `BTreeSet::len` with the governed allocation.
203 pub fn len(&self) -> u64 {
204 self.inner.len()
205 }
206 /// Delegates to upstream `BTreeSet::remove` with the governed allocation.
207 pub fn remove(&mut self, key: &K) -> bool {
208 self.inner.remove(key)
209 }
210 /// Delegates to upstream `BTreeSet::first` with the governed allocation.
211 pub fn first(&self) -> Option<K> {
212 self.inner.first()
213 }
214 /// Delegates to upstream `BTreeSet::last` with the governed allocation.
215 pub fn last(&self) -> Option<K> {
216 self.inner.last()
217 }
218 /// Delegates to upstream `BTreeSet::pop_first` with the governed allocation.
219 pub fn pop_first(&mut self) -> Option<K> {
220 self.inner.pop_first()
221 }
222 /// Delegates to upstream `BTreeSet::pop_last` with the governed allocation.
223 pub fn pop_last(&mut self) -> Option<K> {
224 self.inner.pop_last()
225 }
226 /// Delegates to upstream `BTreeSet::iter` with the governed allocation.
227 pub fn iter(&self) -> SetIter<'_, K, M> {
228 self.inner.iter()
229 }
230 /// Delegates to upstream `BTreeSet::range` with the governed allocation.
231 pub fn range(&self, key_range: impl RangeBounds<K>) -> SetIter<'_, K, M> {
232 self.inner.range(key_range)
233 }
234 /// Delegates to upstream `BTreeSet::clear` with the governed allocation.
235 pub fn clear(&mut self) {
236 self.inner.clear();
237 }
238}
239
240/// Governed upstream `Cell`; initialization and failures follow upstream contracts.
241pub struct StableCell<T: Storable, M: Memory = DefaultMemoryImpl> {
242 inner: substrate::Cell<T, RuntimeMemory<M>>,
243}
244impl<T: Storable, M: Memory> StableCell<T, M> {
245 /// Create a new collection, replacing any prior collection in the selected memory.
246 /// This is destructive even when a valid collection already exists.
247 ///
248 /// # Panics
249 /// Panics on insufficient memory or codec/backing failure.
250 pub fn new(memory: RuntimeMemory<M>, value: T) -> Self {
251 Self {
252 inner: substrate::Cell::new(memory, value),
253 }
254 }
255 /// Open a recognized retained collection; initialize a new one when memory
256 /// is empty or has foreign magic. Foreign contents may be overwritten.
257 /// This is not a corruption-recovery or schema-admission operation.
258 ///
259 /// # Panics
260 /// Panics for unsupported recognized layouts, invalid retained contents,
261 /// insufficient memory or codec/backing failure.
262 pub fn init(memory: RuntimeMemory<M>, default_value: T) -> Self {
263 Self {
264 inner: substrate::Cell::init(memory, default_value),
265 }
266 }
267 /// Delegates to upstream `Cell::get` with the governed allocation.
268 pub fn get(&self) -> &T {
269 self.inner.get()
270 }
271 /// Delegates to upstream `Cell::set` with the governed allocation.
272 pub fn set(&mut self, value: T) -> T {
273 self.inner.set(value)
274 }
275}
276
277/// Governed upstream `Vec`; initialization and failures follow upstream contracts.
278pub struct StableVec<T: Storable, M: Memory = DefaultMemoryImpl> {
279 inner: substrate::Vec<T, RuntimeMemory<M>>,
280}
281impl<T: Storable, M: Memory> StableVec<T, M> {
282 /// Create a new collection, replacing any prior collection in the selected memory.
283 /// This is destructive even when a valid collection already exists.
284 ///
285 /// # Panics
286 /// Panics on insufficient memory or codec/backing failure.
287 pub fn new(memory: RuntimeMemory<M>) -> Self {
288 Self {
289 inner: substrate::Vec::new(memory),
290 }
291 }
292 /// Open a valid retained collection, or create one in zero-page memory.
293 ///
294 /// # Panics
295 /// Panics for foreign magic, unsupported layout, incompatible element bounds,
296 /// insufficient memory or codec/backing failure. Foreign memory is not reset.
297 pub fn init(memory: RuntimeMemory<M>) -> Self {
298 Self {
299 inner: substrate::Vec::init(memory),
300 }
301 }
302 /// Delegates to upstream `Vec::get` with the governed allocation.
303 pub fn get(&self, index: u64) -> Option<T> {
304 self.inner.get(index)
305 }
306 /// Delegates to upstream `Vec::set` with the governed allocation.
307 pub fn set(&self, index: u64, item: &T) {
308 self.inner.set(index, item);
309 }
310 /// Delegates to upstream `Vec::push` with the governed allocation.
311 pub fn push(&self, item: &T) {
312 self.inner.push(item);
313 }
314 /// Delegates to upstream `Vec::pop` with the governed allocation.
315 pub fn pop(&self) -> Option<T> {
316 self.inner.pop()
317 }
318 /// Reset to an empty vector while retaining this allocation's exclusive lease.
319 /// Existing payload bytes follow upstream `Vec::new` semantics.
320 #[must_use]
321 pub fn reset(self) -> Self {
322 Self::new(self.inner.into_memory())
323 }
324 /// Delegates to upstream `Vec::len` with the governed allocation.
325 pub fn len(&self) -> u64 {
326 self.inner.len()
327 }
328 /// Delegates to upstream `Vec::is_empty` with the governed allocation.
329 pub fn is_empty(&self) -> bool {
330 self.inner.is_empty()
331 }
332 /// Delegates to upstream `Vec::iter` with the governed allocation.
333 pub fn iter(&self) -> impl DoubleEndedIterator<Item = T> + '_ {
334 self.inner.iter()
335 }
336}
337
338/// Governed upstream `MinHeap`; initialization and failures follow upstream contracts.
339pub struct StableMinHeap<T: Storable + Ord, M: Memory = DefaultMemoryImpl> {
340 inner: substrate::MinHeap<T, RuntimeMemory<M>>,
341}
342impl<T: Storable + Ord, M: Memory> StableMinHeap<T, M> {
343 /// Create a new collection, replacing any prior collection in the selected memory.
344 /// This is destructive even when a valid collection already exists.
345 ///
346 /// # Panics
347 /// Panics on insufficient memory or codec/backing failure.
348 pub fn new(memory: RuntimeMemory<M>) -> Self {
349 Self {
350 inner: substrate::MinHeap::new(memory),
351 }
352 }
353 /// Open a valid retained collection, or create one in zero-page memory.
354 ///
355 /// # Panics
356 /// Panics for foreign magic, unsupported layout, incompatible element bounds,
357 /// insufficient memory or codec/backing failure. Foreign memory is not reset.
358 pub fn init(memory: RuntimeMemory<M>) -> Self {
359 Self {
360 inner: substrate::MinHeap::init(memory),
361 }
362 }
363 /// Delegates to upstream `MinHeap::push` with the governed allocation.
364 pub fn push(&mut self, item: &T) {
365 self.inner.push(item);
366 }
367 /// Delegates to upstream `MinHeap::pop` with the governed allocation.
368 pub fn pop(&mut self) -> Option<T> {
369 self.inner.pop()
370 }
371 /// Delegates to upstream `MinHeap::peek` with the governed allocation.
372 pub fn peek(&self) -> Option<T> {
373 self.inner.peek()
374 }
375 /// Delegates to upstream `MinHeap::len` with the governed allocation.
376 pub fn len(&self) -> u64 {
377 self.inner.len()
378 }
379 /// Delegates to upstream `MinHeap::is_empty` with the governed allocation.
380 pub fn is_empty(&self) -> bool {
381 self.inner.is_empty()
382 }
383 /// Delegates to upstream `MinHeap::iter` with the governed allocation.
384 pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
385 self.inner.iter()
386 }
387}
388
389/// Governed upstream `Log`; initialization and failures follow upstream contracts.
390pub struct StableLog<T: Storable, M: Memory = DefaultMemoryImpl> {
391 inner: substrate::Log<T, RuntimeMemory<M>, RuntimeMemory<M>>,
392}
393impl<T: Storable, M: Memory> StableLog<T, M> {
394 /// Create a new collection, replacing any prior collection in the selected memory.
395 /// This is destructive even when a valid collection already exists.
396 ///
397 /// # Panics
398 /// Panics on insufficient memory or codec/backing failure.
399 pub fn new(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
400 Self {
401 inner: substrate::Log::new(index_memory, data_memory),
402 }
403 }
404 /// Open a retained log, or initialize both memories when data memory is
405 /// empty or has foreign magic. The latter overwrites previous contents.
406 ///
407 /// # Panics
408 /// With recognized data magic, panics for an invalid index or unsupported
409 /// layout. Growth and codec/backing failures also retain upstream panics.
410 pub fn init(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
411 Self {
412 inner: substrate::Log::init(index_memory, data_memory),
413 }
414 }
415 /// Delegates to upstream `Log::first` with the governed allocation.
416 pub fn first(&self) -> Option<T> {
417 self.inner.first()
418 }
419 /// Delegates to upstream `Log::last` with the governed allocation.
420 pub fn last(&self) -> Option<T> {
421 self.inner.last()
422 }
423 /// Delegates to upstream `Log::get` with the governed allocation.
424 pub fn get(&self, idx: u64) -> Option<T> {
425 self.inner.get(idx)
426 }
427 /// Delegates to upstream `Log::len` with the governed allocation.
428 pub fn len(&self) -> u64 {
429 self.inner.len()
430 }
431 /// Delegates to upstream `Log::is_empty` with the governed allocation.
432 pub fn is_empty(&self) -> bool {
433 self.inner.is_empty()
434 }
435 /// Delegates to upstream `Log::append` with the governed allocation.
436 /// # Errors
437 /// Returns the upstream write error if memory cannot grow.
438 pub fn append(&self, item: &T) -> Result<u64, LogWriteError> {
439 self.inner.append(item)
440 }
441 /// Delegates to upstream `Log::iter` with the governed allocation.
442 pub fn iter(&self) -> LogIter<'_, T, M> {
443 self.inner.iter()
444 }
445}
446
447/// Upstream map iterator with governed memory.
448pub type MapIter<'a, K, V, M = DefaultMemoryImpl> =
449 substrate::btreemap::Iter<'a, K, V, RuntimeMemory<M>>;
450/// Upstream set iterator with governed memory.
451pub type SetIter<'a, K, M = DefaultMemoryImpl> = substrate::btreeset::Iter<'a, K, RuntimeMemory<M>>;
452/// Upstream log iterator with governed index and data memories.
453pub type LogIter<'a, T, M = DefaultMemoryImpl> =
454 substrate::log::Iter<'a, T, RuntimeMemory<M>, RuntimeMemory<M>>;
455
456pub use substrate::log::WriteError as LogWriteError;
457
458impl<'a, K: Storable + Ord + Clone, V: Storable, M: Memory> IntoIterator
459 for &'a StableBTreeMap<K, V, M>
460{
461 type Item = <MapIter<'a, K, V, M> as Iterator>::Item;
462 type IntoIter = MapIter<'a, K, V, M>;
463 fn into_iter(self) -> Self::IntoIter {
464 self.iter()
465 }
466}
467
468impl<'a, K: Storable + Ord + Clone, M: Memory> IntoIterator for &'a StableBTreeSet<K, M> {
469 type Item = K;
470 type IntoIter = SetIter<'a, K, M>;
471 fn into_iter(self) -> Self::IntoIter {
472 self.iter()
473 }
474}
475
476impl<'a, T: Storable, M: Memory> IntoIterator for &'a StableLog<T, M> {
477 type Item = T;
478 type IntoIter = LogIter<'a, T, M>;
479 fn into_iter(self) -> Self::IntoIter {
480 self.iter()
481 }
482}
483/// A stable cell whose writes enforce the record's declared `Storable` bound.
484pub struct BoundedCell<T: Storable, M: Memory = DefaultMemoryImpl> {
485 cell: substrate::Cell<BoundedValue<T>, RuntimeMemory<M>>,
486}
487
488impl<T: Storable, M: Memory> BoundedCell<T, M> {
489 /// Open a current cell or initialize a fresh bounded record.
490 /// Empty memory or foreign magic initializes the supplied value, potentially
491 /// overwriting foreign contents. Recognized incompatible layouts are refused.
492 ///
493 /// # Panics
494 /// Panics for unbounded records or encodings that violate the declared bound.
495 /// Also retains [`StableCell::init`] layout, growth and codec/backing panics.
496 #[must_use]
497 pub fn init(memory: RuntimeMemory<M>, value: T) -> Self {
498 assert!(matches!(T::BOUND, Bound::Bounded { .. }));
499 Self {
500 cell: substrate::Cell::init(memory, BoundedValue(value)),
501 }
502 }
503
504 /// Read the cached current record without serializing or cloning it.
505 #[must_use]
506 pub fn get(&self) -> &T {
507 &self.cell.get().0
508 }
509
510 /// Validate and persist a complete replacement, returning the prior record.
511 ///
512 /// # Panics
513 /// Panics before writing if the encoding violates its declared bound.
514 pub fn set(&mut self, value: T) -> T {
515 self.cell.set(BoundedValue(value)).0
516 }
517}
518
519struct BoundedValue<T>(T);
520
521impl<T: Storable> Storable for BoundedValue<T> {
522 const BOUND: Bound = T::BOUND;
523
524 fn to_bytes(&self) -> Cow<'_, [u8]> {
525 self.0.to_bytes_checked()
526 }
527
528 fn into_bytes(self) -> Vec<u8> {
529 self.0.into_bytes_checked()
530 }
531
532 fn from_bytes(bytes: Cow<'_, [u8]>) -> Self {
533 let Bound::Bounded {
534 max_size,
535 is_fixed_size,
536 } = T::BOUND
537 else {
538 unreachable!("bounded cell record must declare a bound");
539 };
540 if is_fixed_size {
541 assert_eq!(bytes.len(), max_size as usize);
542 } else {
543 assert!(bytes.len() <= max_size as usize);
544 }
545 Self(T::from_bytes(bytes))
546 }
547}