Skip to main content

ic_storage/adapter/
vector.rs

1//! Governed vector over the copied ICSS fixed-stride engine.
2use super::{
3    Memory, Storable,
4    bridge::{self, Backing, Codec},
5    error::{CollectionError as Error, Result, ensure},
6};
7use crate::{DefaultMemoryImpl, RuntimeMemory, engine};
8
9///
10/// `StableVec`
11///
12/// ICSS-baseline fixed-stride vector in one governed allocation. There are no
13/// custom checksums, cached length, read windows or mutation epochs. Malformed
14/// stored values trap; IC execution rollback supplies atomicity.
15///
16pub struct StableVec<T: Storable, M: Memory = DefaultMemoryImpl> {
17    inner: engine::vec::Vec<Codec<T>, Backing<RuntimeMemory<M>>>,
18}
19impl<T: Storable, M: Memory> StableVec<T, M> {
20    /// Create only in empty admitted memory.
21    /// # Errors
22    /// Refuses nonempty memory or initial growth refusal.
23    pub fn create(memory: RuntimeMemory<M>) -> Result<Self> {
24        ensure(memory.size() == 0, Error::NotEmpty)?;
25        Self::reset(memory)
26    }
27    /// Explicitly replace this vector image.
28    /// # Errors
29    /// Refuses initial growth; invalid bounds/codecs trap.
30    pub fn reset(memory: RuntimeMemory<M>) -> Result<Self> {
31        bridge::reserve(&memory)?;
32        #[cfg(not(target_arch = "wasm32"))]
33        let _guard = memory.poison_on_unwind();
34        Ok(Self {
35            inner: engine::vec::Vec::new(Backing(memory)),
36        })
37    }
38    /// Load the current SVC format without initialization.
39    /// # Errors
40    /// Refuses empty/foreign framing; engine/codec failures trap.
41    pub fn load(memory: RuntimeMemory<M>) -> Result<Self> {
42        ensure(memory.size() != 0, Error::Format)?;
43        let mut marker = [0; 4];
44        memory.read(0, &mut marker);
45        ensure(&marker == b"SVC\x01", Error::Format)?;
46        Ok(Self {
47            inner: engine::vec::Vec::init(Backing(memory)),
48        })
49    }
50    /// Read one indexed record; malformed stored bytes trap.
51    /// # Errors
52    /// Engine failures trap instead of returning an ordinary error.
53    pub fn get(&self, index: u64) -> Result<Option<T>> {
54        Ok(self.inner.get(index).map(|v| v.0))
55    }
56    /// Replace one record; invalid codecs, bounds or writes trap.
57    /// # Errors
58    /// Engine failures trap instead of returning an ordinary error.
59    pub fn set(&self, index: u64, item: &T) -> Result<()> {
60        #[cfg(not(target_arch = "wasm32"))]
61        let _guard = self.inner.memory().0.poison_on_unwind();
62        self.inner.set(index, Codec::borrowed(item));
63        Ok(())
64    }
65    /// Append one record, publishing its length last.
66    /// # Errors
67    /// Engine failures trap instead of returning an ordinary error.
68    pub fn push(&self, item: &T) -> Result<()> {
69        #[cfg(not(target_arch = "wasm32"))]
70        let _guard = self.inner.memory().0.poison_on_unwind();
71        self.inner.push(Codec::borrowed(item));
72        Ok(())
73    }
74    /// Remove the final record; failures trap.
75    /// # Errors
76    /// Engine failures trap instead of returning an ordinary error.
77    pub fn pop(&self) -> Result<Option<T>> {
78        #[cfg(not(target_arch = "wasm32"))]
79        let _guard = self.inner.memory().0.poison_on_unwind();
80        Ok(self.inner.pop().map(|v| v.0))
81    }
82    /// Read the current committed length from stable memory.
83    #[must_use]
84    pub fn len(&self) -> u64 {
85        self.inner.len()
86    }
87    /// Whether there are no records.
88    #[must_use]
89    pub fn is_empty(&self) -> bool {
90        self.inner.is_empty()
91    }
92    /// Scalar bidirectional iteration with one reusable buffer, as in ICSS.
93    pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<T>> + '_ {
94        Iter(self.inner.iter())
95    }
96}
97
98// A general Map adaptor executes its closure for skipped elements. Forward
99// skipping/counting directly so the facade retains ICSS's specialized methods.
100struct Iter<I>(I);
101impl<T, I: Iterator<Item = Codec<T>>> Iterator for Iter<I> {
102    type Item = Result<T>;
103    fn next(&mut self) -> Option<Self::Item> {
104        self.0.next().map(|v| Ok(v.0))
105    }
106    fn nth(&mut self, n: usize) -> Option<Self::Item> {
107        self.0.nth(n).map(|v| Ok(v.0))
108    }
109    fn count(self) -> usize {
110        self.0.count()
111    }
112    fn last(self) -> Option<Self::Item> {
113        self.0.last().map(|v| Ok(v.0))
114    }
115    fn size_hint(&self) -> (usize, Option<usize>) {
116        self.0.size_hint()
117    }
118}
119impl<T, I: DoubleEndedIterator<Item = Codec<T>>> DoubleEndedIterator for Iter<I> {
120    fn next_back(&mut self) -> Option<Self::Item> {
121        self.0.next_back().map(|v| Ok(v.0))
122    }
123    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
124        self.0.nth_back(n).map(|v| Ok(v.0))
125    }
126}
127// This suite meters host IO and injects corrupt bytes through native-only views.
128// Installed Wasm qualification exercises the production Vector separately.
129#[cfg(all(test, not(target_arch = "wasm32")))]
130mod tests;