Skip to main content

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}