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