Skip to main content

thruput_sdk/storage/
vector.rs

1use super::deletable::Deletable;
2use super::keypath::{FramedComponent, Keypath};
3use super::map::Map;
4use super::node::StorageType;
5use super::slot::{SlotMut, SlotRef};
6use super::value::Value;
7use crate::exn::NoneIfMissing;
8use core::fmt::Debug;
9
10/// Element slots are keyed by the element index (`u32`); see [`Map`] for the
11/// key-encoding injectivity invariant that governs the underlying derivation.
12///
13/// # Concurrency
14///
15/// `len` is a plain storage value that every structural mutation —
16/// [`push_with`](Self::push_with), [`push_empty_slot`](Self::push_empty_slot),
17/// [`pop`](Self::pop) — reads and rewrites under an exclusive lock. Of a set
18/// of concurrent transactions that grow or shrink the same vector, only one
19/// can be included in a block; the rest conflict at commit. A `Vector` is
20/// therefore a poor fit for hot append paths (an event log every caller pushes
21/// to, say) — prefer keying a [`Map`] by something caller-specific, or a
22/// mergeable counter like [`NNInt`](super::NNInt), when concurrent writers
23/// matter. Element reads and in-place element writes through
24/// [`at`](Self::at)/[`at_mut`](Self::at_mut) read `len` for their bounds check
25/// but never rewrite it; apart from that read they carry only the
26/// per-element storage semantics.
27pub struct Vector<S> {
28    keypath: Keypath,
29    len: Value<u32>,
30    values: Map<u32, S>,
31}
32
33impl<S> Debug for Vector<S> {
34    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        f.debug_struct("Vector")
36            .field("keypath", &self.keypath)
37            .field("len", &self.len)
38            .finish_non_exhaustive()
39    }
40}
41
42impl<S> From<Keypath> for Vector<S> {
43    fn from(keypath: Keypath) -> Self {
44        // Fixed layout slots (0 = len, 1 = values), pre-framed at const
45        // time: state-load appends constant bytes instead of runtime-CXS-
46        // encoding through the scratch buffer. Element slots stay runtime-
47        // derived (indices are dynamic).
48        const LEN_SLOT: FramedComponent = FramedComponent::from_u32(0);
49        const VALUES_SLOT: FramedComponent = FramedComponent::from_u32(1);
50        let len_kp = keypath.child_framed(&LEN_SLOT);
51        let values_kp = keypath.child_framed(&VALUES_SLOT);
52
53        Self {
54            keypath,
55            len: Value::from(len_kp),
56            values: Map::from(values_kp),
57        }
58    }
59}
60
61impl<S> super::sealed::Sealed for Vector<S> {}
62impl<S> super::node::StorageType for Vector<S> {}
63
64impl<S> Vector<S> {
65    pub fn keypath(&self) -> &Keypath {
66        &self.keypath
67    }
68}
69
70impl<S: StorageType> Vector<S> {
71    /// Current logical extent (number of in-bounds indices).
72    ///
73    /// A missing len slot reads as 0 (a never-written vector is empty).
74    /// Any other failure — a value at the slot that is not a `u32`, a
75    /// wrong storage type, a host fault — aborts typed like every other
76    /// storage accessor, instead of masquerading as an empty vector.
77    ///
78    /// Vector slots are sparse-capable: callers may delete element storage
79    /// without shrinking len, so an in-bounds index does not guarantee that
80    /// the slot currently has backing data.
81    pub fn len(&self) -> u32 {
82        self.len.try_get().none_if_missing().unwrap_or(0)
83    }
84
85    /// Returns true if the vector contains no elements.
86    pub fn is_empty(&self) -> bool {
87        self.len() == 0
88    }
89
90    /// Borrow element at `index` (bounds-checked).
91    pub fn at(&self, index: u32) -> SlotRef<'_, Self, S> {
92        self.bounds_check(index);
93        let keypath = self.make_elem_keypath(index);
94        let slot = S::from(keypath);
95        SlotRef::new(self, slot)
96    }
97
98    /// Mutably borrow element at `index` (bounds-checked).
99    ///
100    /// For a `Deletable` element type the returned slot is itself
101    /// [`Deletable`], and deleting it removes only that element's storage:
102    /// `len` is unchanged, `index` stays in bounds, and the slot reads back
103    /// as missing. Elements do not shift down and the hole is not reused by
104    /// a later push. After any element delete, read with `try_get` +
105    /// [`none_if_missing`](crate::exn::NoneIfMissing::none_if_missing) —
106    /// `get` aborts on a hole — and treat [`len`](Self::len) as a high-water
107    /// mark rather than a count of live elements. To reset the vector
108    /// entirely, delete the `Vector` itself, which clears `len` too.
109    pub fn at_mut(&mut self, index: u32) -> SlotMut<'_, Self, S> {
110        self.bounds_check(index);
111        let keypath = self.make_elem_keypath(index);
112        let slot = S::from(keypath);
113        SlotMut::new(self, slot)
114    }
115
116    /// Allocate a new sparse-capable slot at the end and return a mutable
117    /// handle to it.
118    ///
119    /// This increments len before the returned slot is written, making the new
120    /// index immediately in-bounds for the rest of the invocation.
121    ///
122    /// Writes `len` under an exclusive lock, so concurrent same-block pushes
123    /// conflict at commit — see the Concurrency notes on [`Vector`].
124    #[must_use = "push_empty_slot increments len immediately; dropping the slot leaves it uninitialized. Use push_with to initialize atomically, or bind with `let _ = ...` if an empty slot is intentional."]
125    pub fn push_empty_slot(&mut self) -> SlotMut<'_, Self, S> {
126        let idx = self.len();
127        let new_len = idx.checked_add(1).unwrap();
128        self.len.set_exclusive(&new_len);
129        let keypath = self.make_elem_keypath(idx);
130        let slot = S::from(keypath);
131        SlotMut::new(self, slot)
132    }
133
134    /// Construct a new slot at the end, initialize it through `f`, then make
135    /// the index visible by incrementing len.
136    ///
137    /// If `f` panics or traps, the entire invocation frame faults and reverts,
138    /// so neither the slot data nor the len increment are committed.
139    ///
140    /// Writes `len` under an exclusive lock, so concurrent same-block pushes
141    /// conflict at commit — see the Concurrency notes on [`Vector`].
142    pub fn push_with<R>(&mut self, f: impl FnOnce(&mut S) -> R) -> R {
143        let idx = self.len();
144        let mut slot = S::from(self.make_elem_keypath(idx));
145        let out = f(&mut slot);
146        let new_len = idx.checked_add(1).unwrap();
147        self.len.set_exclusive(&new_len);
148        out
149    }
150
151    /// Iterate over all elements. Yields read-only `SlotRef` handles.
152    /// Holds a shared borrow on the vector, preventing structural
153    /// modifications (`push_with`/`push_empty_slot`/pop/at_mut) while the
154    /// iterator is alive.
155    pub fn iter(&self) -> VectorIter<'_, S> {
156        VectorIter {
157            vec: self,
158            index: 0,
159            len: self.len(),
160        }
161    }
162
163    /// Iterate over all elements with mutable access. Yields owned
164    /// storage handles that may be used for mutations. Holds an
165    /// exclusive borrow on the vector.
166    pub fn iter_mut(&mut self) -> VectorIterMut<'_, S> {
167        let len = self.len();
168        VectorIterMut {
169            vec: self,
170            index: 0,
171            len,
172        }
173    }
174
175    fn bounds_check(&self, index: u32) {
176        let len = self.len();
177        if index >= len {
178            super::error::oob_throw(len as u64, index as u64);
179        }
180    }
181
182    fn make_elem_keypath(&self, index: u32) -> Keypath {
183        self.values.keypath().child(&index)
184    }
185}
186
187impl<S: StorageType + Deletable> Vector<S> {
188    /// Remove the last element, deleting its storage.
189    /// Aborts if the vector is empty.
190    ///
191    /// Writes `len` under an exclusive lock, so concurrent same-block
192    /// structural mutations conflict at commit — see the Concurrency notes on
193    /// [`Vector`].
194    pub fn pop(&mut self) {
195        let len = self.len();
196        if len == 0 {
197            super::error::oob_throw(0, 0);
198        }
199        let new_len = len - 1;
200        let mut elem = S::from(self.make_elem_keypath(new_len));
201        elem.delete_exclusive();
202        self.len.set_exclusive(&new_len);
203    }
204
205    /// Delete the storage of the element at `index` (bounds-checked).
206    ///
207    /// Shorthand for `at_mut(index).delete()`. This leaves a hole rather than
208    /// removing the element: `len` is untouched and `index` stays in bounds.
209    /// See [`at_mut`](Self::at_mut) for the full sparse-vector semantics, and
210    /// [`pop`](Self::pop) to actually shrink the vector.
211    #[inline(always)]
212    pub fn delete_at(&mut self, index: u32) {
213        self.at_mut(index).delete();
214    }
215}
216
217impl<S: StorageType + Deletable> Deletable for Vector<S> {
218    fn delete_internal(&mut self, exclusive: bool) {
219        let len = self.len();
220        for i in 0..len {
221            let mut elem = S::from(self.make_elem_keypath(i));
222            elem.delete_internal(exclusive);
223        }
224        self.len.delete_internal(exclusive);
225    }
226}
227
228/// Iterator over vector elements (shared borrow, read-only).
229pub struct VectorIter<'a, S> {
230    vec: &'a Vector<S>,
231    index: u32,
232    len: u32,
233}
234
235impl<'a, S: StorageType> Iterator for VectorIter<'a, S> {
236    type Item = SlotRef<'a, Vector<S>, S>;
237
238    fn next(&mut self) -> Option<Self::Item> {
239        if self.index >= self.len {
240            return None;
241        }
242        let elem = S::from(self.vec.make_elem_keypath(self.index));
243        self.index += 1;
244        Some(SlotRef::new(self.vec, elem))
245    }
246
247    fn size_hint(&self) -> (usize, Option<usize>) {
248        let remaining = (self.len - self.index) as usize;
249        (remaining, Some(remaining))
250    }
251}
252
253impl<S: StorageType> ExactSizeIterator for VectorIter<'_, S> {}
254
255impl<'a, S: StorageType> DoubleEndedIterator for VectorIter<'a, S> {
256    fn next_back(&mut self) -> Option<Self::Item> {
257        if self.index >= self.len {
258            return None;
259        }
260        self.len -= 1;
261        let elem = S::from(self.vec.make_elem_keypath(self.len));
262        Some(SlotRef::new(self.vec, elem))
263    }
264}
265
266/// Iterator over vector elements (exclusive borrow).
267pub struct VectorIterMut<'a, S> {
268    vec: &'a mut Vector<S>,
269    index: u32,
270    len: u32,
271}
272
273impl<'a, S: StorageType> Iterator for VectorIterMut<'a, S> {
274    type Item = SlotMut<'a, Vector<S>, S>;
275
276    fn next(&mut self) -> Option<Self::Item> {
277        if self.index >= self.len {
278            return None;
279        }
280        let elem = S::from(self.vec.make_elem_keypath(self.index));
281        self.index += 1;
282        // SAFETY: the iterator holds &'a mut Vector<S>, ensuring
283        // exclusive access to the vector for 'a.
284        Some(SlotMut::new_detached(elem))
285    }
286
287    fn size_hint(&self) -> (usize, Option<usize>) {
288        let remaining = (self.len - self.index) as usize;
289        (remaining, Some(remaining))
290    }
291}
292
293impl<S: StorageType> ExactSizeIterator for VectorIterMut<'_, S> {}
294
295impl<'a, S: StorageType> DoubleEndedIterator for VectorIterMut<'a, S> {
296    fn next_back(&mut self) -> Option<Self::Item> {
297        if self.index >= self.len {
298            return None;
299        }
300        self.len -= 1;
301        let elem = S::from(self.vec.make_elem_keypath(self.len));
302        Some(SlotMut::new_detached(elem))
303    }
304}
305
306impl<'a, S: StorageType> IntoIterator for &'a Vector<S> {
307    type Item = SlotRef<'a, Vector<S>, S>;
308    type IntoIter = VectorIter<'a, S>;
309
310    fn into_iter(self) -> Self::IntoIter {
311        self.iter()
312    }
313}
314
315impl<'a, S: StorageType> IntoIterator for &'a mut Vector<S> {
316    type Item = SlotMut<'a, Vector<S>, S>;
317    type IntoIter = VectorIterMut<'a, S>;
318
319    fn into_iter(self) -> Self::IntoIter {
320        self.iter_mut()
321    }
322}
323
324#[cfg(test)]
325mod test_vector_slots {
326    use super::*;
327    use crate::storage::Value;
328
329    // Drift guard: the const-framed fixed slots (0 = len, 1 = values)
330    // must address exactly what runtime child() derives.
331    crate::contract_test!(fixed_slots_match_runtime_child {
332        let root = Keypath::default().child("V");
333        let v: Vector<Value<u32>> = Vector::from(root.clone());
334        assert_eq!(*v.len.key(), root.child(&0u32).generate_key());
335        assert!(v.values.keypath().addresses_same_slot(&root.child(&1u32)));
336        assert!(v.keypath().addresses_same_slot(&root));
337    });
338}