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///
241/// `StableCell`
242///
243/// Governed upstream `Cell` with explicit initialization and read-only loading.
244///
245/// Values and stored bytes retain the upstream codec and layout contracts.
246///
247pub struct StableCell<T: Storable, M: Memory = DefaultMemoryImpl> {
248    inner: substrate::Cell<CellValue<T>, RuntimeMemory<M>>,
249}
250
251// Upstream has no load constructor. An absent value is an internal loading
252// marker, never a default T or a persisted record. Serialization refuses it
253// before upstream can grow/write on its initialization fallback. Successful
254// construction always holds Some(T), decoded exactly once by upstream.
255struct CellValue<T>(Option<T>);
256
257impl<T: Storable> Storable for CellValue<T> {
258    const BOUND: Bound = T::BOUND;
259
260    fn to_bytes(&self) -> Cow<'_, [u8]> {
261        self.0
262            .as_ref()
263            .expect("cannot initialize a loading cell")
264            .to_bytes()
265    }
266
267    fn into_bytes(self) -> Vec<u8> {
268        self.0
269            .expect("cannot initialize a loading cell")
270            .into_bytes()
271    }
272
273    fn from_bytes(bytes: Cow<'_, [u8]>) -> Self {
274        Self(Some(T::from_bytes(bytes)))
275    }
276}
277
278impl<T: Storable, M: Memory> StableCell<T, M> {
279    /// Create a new collection, replacing any prior collection in the selected memory.
280    /// This is destructive even when a valid collection already exists.
281    ///
282    /// # Panics
283    /// Panics on insufficient memory or codec/backing failure.
284    pub fn new(memory: RuntimeMemory<M>, value: T) -> Self {
285        Self {
286            inner: substrate::Cell::new(memory, CellValue(Some(value))),
287        }
288    }
289    /// Open a recognized retained collection; initialize a new one when memory
290    /// is empty or has foreign magic. Foreign contents may be overwritten.
291    /// This is not a corruption-recovery or schema-admission operation.
292    ///
293    /// # Panics
294    /// Panics for unsupported recognized layouts, invalid retained contents,
295    /// insufficient memory or codec/backing failure.
296    pub fn init(memory: RuntimeMemory<M>, default_value: T) -> Self {
297        Self {
298            inner: substrate::Cell::init(memory, CellValue(Some(default_value))),
299        }
300    }
301    /// Load an existing cell without initializing, writing or growing memory.
302    ///
303    /// Requires no default value or `Default`/`Clone` bound. The retained value
304    /// is decoded by upstream using `T::from_bytes`. A successful load retains
305    /// the allocation's exclusive lease until this cell is dropped.
306    ///
307    /// # Errors
308    /// Returns [`ic_memory::StableCellPayloadError`] for empty/foreign memory,
309    /// unsupported layout versions or a payload extending beyond memory. The
310    /// consumed lease is released on failure; no bytes are changed.
311    ///
312    /// # Panics
313    /// Codec and backing failures propagate as upstream panics. Loading does
314    /// not write or grow even on these failures. Ordinary unwinding releases
315    /// the consumed lease; Wasm traps retain the host's rollback semantics.
316    /// This validates the envelope, not the application's schema or recovery policy.
317    pub fn load(memory: RuntimeMemory<M>) -> Result<Self, ic_memory::StableCellPayloadError> {
318        ic_memory::stable_cell_payload_length(&memory, usize::MAX)?;
319        Ok(Self {
320            inner: substrate::Cell::init(memory, CellValue(None)),
321        })
322    }
323    /// Delegates to upstream `Cell::get` with the governed allocation.
324    #[expect(
325        clippy::missing_panics_doc,
326        reason = "constructors always establish a cached value"
327    )]
328    pub fn get(&self) -> &T {
329        self.inner.get().0.as_ref().expect("loaded cell value")
330    }
331    /// Delegates to upstream `Cell::set` with the governed allocation.
332    ///
333    /// # Panics
334    /// Panics on insufficient memory or codec/backing failure, as upstream.
335    pub fn set(&mut self, value: T) -> T {
336        self.inner
337            .set(CellValue(Some(value)))
338            .0
339            .expect("loaded cell value")
340    }
341}
342
343/// Governed upstream `Vec`; initialization and failures follow upstream contracts.
344pub struct StableVec<T: Storable, M: Memory = DefaultMemoryImpl> {
345    inner: substrate::Vec<T, RuntimeMemory<M>>,
346}
347impl<T: Storable, M: Memory> StableVec<T, M> {
348    /// Create a new collection, replacing any prior collection in the selected memory.
349    /// This is destructive even when a valid collection already exists.
350    ///
351    /// # Panics
352    /// Panics on insufficient memory or codec/backing failure.
353    pub fn new(memory: RuntimeMemory<M>) -> Self {
354        Self {
355            inner: substrate::Vec::new(memory),
356        }
357    }
358    /// Open a valid retained collection, or create one in zero-page memory.
359    ///
360    /// # Panics
361    /// Panics for foreign magic, unsupported layout, incompatible element bounds,
362    /// insufficient memory or codec/backing failure. Foreign memory is not reset.
363    pub fn init(memory: RuntimeMemory<M>) -> Self {
364        Self {
365            inner: substrate::Vec::init(memory),
366        }
367    }
368    /// Delegates to upstream `Vec::get` with the governed allocation.
369    pub fn get(&self, index: u64) -> Option<T> {
370        self.inner.get(index)
371    }
372    /// Delegates to upstream `Vec::set` with the governed allocation.
373    pub fn set(&self, index: u64, item: &T) {
374        self.inner.set(index, item);
375    }
376    /// Delegates to upstream `Vec::push` with the governed allocation.
377    pub fn push(&self, item: &T) {
378        self.inner.push(item);
379    }
380    /// Delegates to upstream `Vec::pop` with the governed allocation.
381    pub fn pop(&self) -> Option<T> {
382        self.inner.pop()
383    }
384    /// Reset to an empty vector while retaining this allocation's exclusive lease.
385    /// Existing payload bytes follow upstream `Vec::new` semantics.
386    #[must_use]
387    pub fn reset(self) -> Self {
388        Self::new(self.inner.into_memory())
389    }
390    /// Delegates to upstream `Vec::len` with the governed allocation.
391    pub fn len(&self) -> u64 {
392        self.inner.len()
393    }
394    /// Delegates to upstream `Vec::is_empty` with the governed allocation.
395    pub fn is_empty(&self) -> bool {
396        self.inner.is_empty()
397    }
398    /// Delegates to upstream `Vec::iter` with the governed allocation.
399    pub fn iter(&self) -> impl DoubleEndedIterator<Item = T> + '_ {
400        self.inner.iter()
401    }
402}
403
404/// Governed upstream `MinHeap`; initialization and failures follow upstream contracts.
405pub struct StableMinHeap<T: Storable + Ord, M: Memory = DefaultMemoryImpl> {
406    inner: substrate::MinHeap<T, RuntimeMemory<M>>,
407}
408impl<T: Storable + Ord, M: Memory> StableMinHeap<T, M> {
409    /// Create a new collection, replacing any prior collection in the selected memory.
410    /// This is destructive even when a valid collection already exists.
411    ///
412    /// # Panics
413    /// Panics on insufficient memory or codec/backing failure.
414    pub fn new(memory: RuntimeMemory<M>) -> Self {
415        Self {
416            inner: substrate::MinHeap::new(memory),
417        }
418    }
419    /// Open a valid retained collection, or create one in zero-page memory.
420    ///
421    /// # Panics
422    /// Panics for foreign magic, unsupported layout, incompatible element bounds,
423    /// insufficient memory or codec/backing failure. Foreign memory is not reset.
424    pub fn init(memory: RuntimeMemory<M>) -> Self {
425        Self {
426            inner: substrate::MinHeap::init(memory),
427        }
428    }
429    /// Delegates to upstream `MinHeap::push` with the governed allocation.
430    pub fn push(&mut self, item: &T) {
431        self.inner.push(item);
432    }
433    /// Delegates to upstream `MinHeap::pop` with the governed allocation.
434    pub fn pop(&mut self) -> Option<T> {
435        self.inner.pop()
436    }
437    /// Delegates to upstream `MinHeap::peek` with the governed allocation.
438    pub fn peek(&self) -> Option<T> {
439        self.inner.peek()
440    }
441    /// Delegates to upstream `MinHeap::len` with the governed allocation.
442    pub fn len(&self) -> u64 {
443        self.inner.len()
444    }
445    /// Delegates to upstream `MinHeap::is_empty` with the governed allocation.
446    pub fn is_empty(&self) -> bool {
447        self.inner.is_empty()
448    }
449    /// Delegates to upstream `MinHeap::iter` with the governed allocation.
450    pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
451        self.inner.iter()
452    }
453}
454
455/// Governed upstream `Log`; initialization and failures follow upstream contracts.
456pub struct StableLog<T: Storable, M: Memory = DefaultMemoryImpl> {
457    inner: substrate::Log<T, RuntimeMemory<M>, RuntimeMemory<M>>,
458}
459impl<T: Storable, M: Memory> StableLog<T, M> {
460    /// Create a new collection, replacing any prior collection in the selected memory.
461    /// This is destructive even when a valid collection already exists.
462    ///
463    /// # Panics
464    /// Panics on insufficient memory or codec/backing failure.
465    pub fn new(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
466        Self {
467            inner: substrate::Log::new(index_memory, data_memory),
468        }
469    }
470    /// Open a retained log, or initialize both memories when data memory is
471    /// empty or has foreign magic. The latter overwrites previous contents.
472    ///
473    /// # Panics
474    /// With recognized data magic, panics for an invalid index or unsupported
475    /// layout. Growth and codec/backing failures also retain upstream panics.
476    pub fn init(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
477        Self {
478            inner: substrate::Log::init(index_memory, data_memory),
479        }
480    }
481    /// Delegates to upstream `Log::first` with the governed allocation.
482    pub fn first(&self) -> Option<T> {
483        self.inner.first()
484    }
485    /// Delegates to upstream `Log::last` with the governed allocation.
486    pub fn last(&self) -> Option<T> {
487        self.inner.last()
488    }
489    /// Delegates to upstream `Log::get` with the governed allocation.
490    pub fn get(&self, idx: u64) -> Option<T> {
491        self.inner.get(idx)
492    }
493    /// Delegates to upstream `Log::len` with the governed allocation.
494    pub fn len(&self) -> u64 {
495        self.inner.len()
496    }
497    /// Delegates to upstream `Log::is_empty` with the governed allocation.
498    pub fn is_empty(&self) -> bool {
499        self.inner.is_empty()
500    }
501    /// Delegates to upstream `Log::append` with the governed allocation.
502    /// # Errors
503    /// Returns the upstream write error if memory cannot grow.
504    pub fn append(&self, item: &T) -> Result<u64, LogWriteError> {
505        self.inner.append(item)
506    }
507    /// Delegates to upstream `Log::iter` with the governed allocation.
508    pub fn iter(&self) -> LogIter<'_, T, M> {
509        self.inner.iter()
510    }
511}
512
513/// Upstream map iterator with governed memory.
514pub type MapIter<'a, K, V, M = DefaultMemoryImpl> =
515    substrate::btreemap::Iter<'a, K, V, RuntimeMemory<M>>;
516/// Upstream set iterator with governed memory.
517pub type SetIter<'a, K, M = DefaultMemoryImpl> = substrate::btreeset::Iter<'a, K, RuntimeMemory<M>>;
518/// Upstream log iterator with governed index and data memories.
519pub type LogIter<'a, T, M = DefaultMemoryImpl> =
520    substrate::log::Iter<'a, T, RuntimeMemory<M>, RuntimeMemory<M>>;
521
522pub use substrate::log::WriteError as LogWriteError;
523
524impl<'a, K: Storable + Ord + Clone, V: Storable, M: Memory> IntoIterator
525    for &'a StableBTreeMap<K, V, M>
526{
527    type Item = <MapIter<'a, K, V, M> as Iterator>::Item;
528    type IntoIter = MapIter<'a, K, V, M>;
529    fn into_iter(self) -> Self::IntoIter {
530        self.iter()
531    }
532}
533
534impl<'a, K: Storable + Ord + Clone, M: Memory> IntoIterator for &'a StableBTreeSet<K, M> {
535    type Item = K;
536    type IntoIter = SetIter<'a, K, M>;
537    fn into_iter(self) -> Self::IntoIter {
538        self.iter()
539    }
540}
541
542impl<'a, T: Storable, M: Memory> IntoIterator for &'a StableLog<T, M> {
543    type Item = T;
544    type IntoIter = LogIter<'a, T, M>;
545    fn into_iter(self) -> Self::IntoIter {
546        self.iter()
547    }
548}
549/// A stable cell whose writes enforce the record's declared `Storable` bound.
550pub struct BoundedCell<T: Storable, M: Memory = DefaultMemoryImpl> {
551    cell: substrate::Cell<BoundedValue<T>, RuntimeMemory<M>>,
552}
553
554impl<T: Storable, M: Memory> BoundedCell<T, M> {
555    /// Open a current cell or initialize a fresh bounded record.
556    /// Empty memory or foreign magic initializes the supplied value, potentially
557    /// overwriting foreign contents. Recognized incompatible layouts are refused.
558    ///
559    /// # Panics
560    /// Panics for unbounded records or encodings that violate the declared bound.
561    /// Also retains [`StableCell::init`] layout, growth and codec/backing panics.
562    #[must_use]
563    pub fn init(memory: RuntimeMemory<M>, value: T) -> Self {
564        assert!(matches!(T::BOUND, Bound::Bounded { .. }));
565        Self {
566            cell: substrate::Cell::init(memory, BoundedValue(value)),
567        }
568    }
569
570    /// Read the cached current record without serializing or cloning it.
571    #[must_use]
572    pub fn get(&self) -> &T {
573        &self.cell.get().0
574    }
575
576    /// Validate and persist a complete replacement, returning the prior record.
577    ///
578    /// # Panics
579    /// Panics before writing if the encoding violates its declared bound.
580    pub fn set(&mut self, value: T) -> T {
581        self.cell.set(BoundedValue(value)).0
582    }
583}
584
585struct BoundedValue<T>(T);
586
587impl<T: Storable> Storable for BoundedValue<T> {
588    const BOUND: Bound = T::BOUND;
589
590    fn to_bytes(&self) -> Cow<'_, [u8]> {
591        self.0.to_bytes_checked()
592    }
593
594    fn into_bytes(self) -> Vec<u8> {
595        self.0.into_bytes_checked()
596    }
597
598    fn from_bytes(bytes: Cow<'_, [u8]>) -> Self {
599        let Bound::Bounded {
600            max_size,
601            is_fixed_size,
602        } = T::BOUND
603        else {
604            unreachable!("bounded cell record must declare a bound");
605        };
606        if is_fixed_size {
607            assert_eq!(bytes.len(), max_size as usize);
608        } else {
609            assert!(bytes.len() <= max_size as usize);
610        }
611        Self(T::from_bytes(bytes))
612    }
613}