Skip to main content

thruput_sdk/storage/
array.rs

1use super::deletable::Deletable;
2use super::keypath::Keypath;
3use super::map::DisjointMut;
4use super::node::StorageType;
5use super::slot::{SlotMut, SlotRef};
6use core::fmt::Debug;
7use core::marker::PhantomData;
8
9/// A fixed-length array of `N` storage slots of type `S`.
10///
11/// Element `i` lives at the array's keypath extended by `i` as a `u32` — the
12/// same slot [`Map<u32, S>`](super::Map) at that keypath would use for key
13/// `i`, so the two layouts are interchangeable. Like `Map`, an `Array` holds
14/// only its own keypath and derives an element's key when the element is
15/// accessed, so its size and load cost do not depend on `N`. Unlike `Map`, the
16/// length is known at compile time: indexing is bounds-checked without a
17/// storage read, and the array can enumerate (iterate and delete) its
18/// elements.
19///
20/// Unlike [`Vector`](super::Vector), there is no stored length: every index
21/// in `0..N` is always in bounds, and an element that was never written (or
22/// was deleted) reads back as missing. Read with `try_get` +
23/// [`none_if_missing`](crate::exn::NoneIfMissing::none_if_missing) when an
24/// element may be unset. Elements share no storage, so writes to distinct
25/// elements never conflict with each other.
26///
27/// `N` must fit in a `u32`; a larger `N` fails to compile on first use.
28pub struct Array<S, const N: usize> {
29    keypath: Keypath,
30    _marker: PhantomData<fn() -> S>,
31}
32
33impl<S, const N: usize> Debug for Array<S, N> {
34    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        f.debug_struct("Array")
36            .field("keypath", &self.keypath)
37            .field("len", &N)
38            .finish()
39    }
40}
41
42impl<S, const N: usize> From<Keypath> for Array<S, N> {
43    fn from(keypath: Keypath) -> Self {
44        const { assert!(N <= u32::MAX as usize, "Array length must fit in a u32") };
45        let _marker = PhantomData;
46        Self { keypath, _marker }
47    }
48}
49
50impl<S, const N: usize> super::sealed::Sealed for Array<S, N> {}
51impl<S, const N: usize> super::node::StorageType for Array<S, N> {}
52
53impl<S, const N: usize> Array<S, N> {
54    /// The array's length, `N`.
55    pub const LEN: u32 = N as u32;
56
57    pub fn keypath(&self) -> &Keypath {
58        &self.keypath
59    }
60
61    /// The array's length, `N`. Never reads storage.
62    pub const fn len(&self) -> u32 {
63        Self::LEN
64    }
65
66    /// Returns true if `N == 0`.
67    pub const fn is_empty(&self) -> bool {
68        N == 0
69    }
70}
71
72impl<S: StorageType, const N: usize> Array<S, N> {
73    /// Borrow element at `index` (bounds-checked).
74    pub fn at(&self, index: u32) -> SlotRef<'_, Self, S> {
75        let slot = S::from(self.make_elem_keypath(index));
76        SlotRef::new(self, slot)
77    }
78
79    /// Mutably borrow element at `index` (bounds-checked).
80    pub fn at_mut(&mut self, index: u32) -> SlotMut<'_, Self, S> {
81        let slot = S::from(self.make_elem_keypath(index));
82        SlotMut::new(self, slot)
83    }
84
85    /// Like [`at_mut`](Self::at_mut), but every successful write through the
86    /// returned handle is followed by a request for an exclusive lock on the
87    /// element. See [`Map::at_mut_exclusive`](super::Map::at_mut_exclusive).
88    pub fn at_mut_exclusive(&mut self, index: u32) -> SlotMut<'_, Self, S> {
89        let mut keypath = self.make_elem_keypath(index);
90        keypath.set_exclusive();
91        let slot = S::from(keypath);
92        SlotMut::new(self, slot)
93    }
94
95    /// Mutably borrow M elements at once (bounds-checked). Panics if any
96    /// indices are equal.
97    ///
98    /// See [`Map::at_disjoint_mut`](super::Map::at_disjoint_mut).
99    pub fn at_disjoint_mut<const M: usize>(
100        &mut self,
101        indices: [u32; M],
102    ) -> DisjointMut<'_, Self, S, M> {
103        for i in 0..M {
104            for j in (i + 1)..M {
105                assert!(
106                    indices[i] != indices[j],
107                    "at_disjoint_mut: duplicate indices at positions {i} and {j}"
108                );
109            }
110        }
111        let slots =
112            indices.map(|index| S::from(self.make_elem_keypath(index)));
113        DisjointMut::new(slots)
114    }
115
116    /// Iterate over all `N` elements. Yields read-only `SlotRef` handles,
117    /// deriving each element's key as it is reached.
118    pub fn iter(&self) -> ArrayIter<'_, S, N> {
119        ArrayIter {
120            arr: self,
121            index: 0,
122            end: Self::LEN,
123        }
124    }
125
126    /// Iterate over all `N` elements with mutable access. Holds an exclusive
127    /// borrow on the array.
128    pub fn iter_mut(&mut self) -> ArrayIterMut<'_, S, N> {
129        ArrayIterMut {
130            arr: self,
131            index: 0,
132            end: Self::LEN,
133        }
134    }
135
136    fn make_elem_keypath(&self, index: u32) -> Keypath {
137        if index >= Self::LEN {
138            super::error::oob_throw(N as u64, index as u64);
139        }
140        self.keypath.child(&index)
141    }
142}
143
144impl<S: StorageType + Deletable, const N: usize> Array<S, N> {
145    /// Delete the storage of the element at `index` (bounds-checked). The
146    /// element reads back as missing afterwards.
147    #[inline(always)]
148    pub fn delete_at(&mut self, index: u32) {
149        self.at_mut(index).delete();
150    }
151}
152
153impl<S: StorageType + Deletable, const N: usize> Deletable for Array<S, N> {
154    fn delete_internal(&mut self, exclusive: bool) {
155        for i in 0..Self::LEN {
156            let mut elem = S::from(self.make_elem_keypath(i));
157            elem.delete_internal(exclusive);
158        }
159    }
160}
161
162/// Iterator over array elements (shared borrow, read-only).
163pub struct ArrayIter<'a, S, const N: usize> {
164    arr: &'a Array<S, N>,
165    index: u32,
166    end: u32,
167}
168
169impl<'a, S: StorageType, const N: usize> Iterator for ArrayIter<'a, S, N> {
170    type Item = SlotRef<'a, Array<S, N>, S>;
171
172    fn next(&mut self) -> Option<Self::Item> {
173        if self.index >= self.end {
174            return None;
175        }
176        let elem = S::from(self.arr.make_elem_keypath(self.index));
177        self.index += 1;
178        Some(SlotRef::new(self.arr, elem))
179    }
180
181    fn size_hint(&self) -> (usize, Option<usize>) {
182        let remaining = (self.end - self.index) as usize;
183        (remaining, Some(remaining))
184    }
185}
186
187impl<S: StorageType, const N: usize> ExactSizeIterator
188    for ArrayIter<'_, S, N>
189{
190}
191
192impl<S: StorageType, const N: usize> DoubleEndedIterator
193    for ArrayIter<'_, S, N>
194{
195    fn next_back(&mut self) -> Option<Self::Item> {
196        if self.index >= self.end {
197            return None;
198        }
199        self.end -= 1;
200        let elem = S::from(self.arr.make_elem_keypath(self.end));
201        Some(SlotRef::new(self.arr, elem))
202    }
203}
204
205/// Iterator over array elements (exclusive borrow).
206pub struct ArrayIterMut<'a, S, const N: usize> {
207    arr: &'a mut Array<S, N>,
208    index: u32,
209    end: u32,
210}
211
212impl<'a, S: StorageType, const N: usize> Iterator for ArrayIterMut<'a, S, N> {
213    type Item = SlotMut<'a, Array<S, N>, S>;
214
215    fn next(&mut self) -> Option<Self::Item> {
216        if self.index >= self.end {
217            return None;
218        }
219        let elem = S::from(self.arr.make_elem_keypath(self.index));
220        self.index += 1;
221        // The iterator holds &'a mut Array<S, N>, ensuring exclusive access
222        // to the array for 'a.
223        Some(SlotMut::new_detached(elem))
224    }
225
226    fn size_hint(&self) -> (usize, Option<usize>) {
227        let remaining = (self.end - self.index) as usize;
228        (remaining, Some(remaining))
229    }
230}
231
232impl<S: StorageType, const N: usize> ExactSizeIterator
233    for ArrayIterMut<'_, S, N>
234{
235}
236
237impl<S: StorageType, const N: usize> DoubleEndedIterator
238    for ArrayIterMut<'_, S, N>
239{
240    fn next_back(&mut self) -> Option<Self::Item> {
241        if self.index >= self.end {
242            return None;
243        }
244        self.end -= 1;
245        let elem = S::from(self.arr.make_elem_keypath(self.end));
246        Some(SlotMut::new_detached(elem))
247    }
248}
249
250impl<'a, S: StorageType, const N: usize> IntoIterator for &'a Array<S, N> {
251    type Item = SlotRef<'a, Array<S, N>, S>;
252    type IntoIter = ArrayIter<'a, S, N>;
253
254    fn into_iter(self) -> Self::IntoIter {
255        self.iter()
256    }
257}
258
259impl<'a, S: StorageType, const N: usize> IntoIterator for &'a mut Array<S, N> {
260    type Item = SlotMut<'a, Array<S, N>, S>;
261    type IntoIter = ArrayIterMut<'a, S, N>;
262
263    fn into_iter(self) -> Self::IntoIter {
264        self.iter_mut()
265    }
266}
267
268#[cfg(test)]
269mod test_array_slots {
270    use super::*;
271    use crate::storage::{Map, Value};
272
273    // Layout guard: element i addresses exactly the slot a Map<u32, _> at the
274    // same keypath uses for key i.
275    crate::contract_test!(elements_match_map_u32_slots {
276        let root = Keypath::default().child("A");
277        let arr: Array<Value<u32>, 4> = Array::from(root.clone());
278        let map: Map<u32, Value<u32>> = Map::from(root);
279        for i in 0..4u32 {
280            assert_eq!(*arr.at(i).key(), *map.at(&i).key());
281        }
282    });
283}