multiversx_sc/storage/mappers/
vec_mapper.rs

1use super::{
2    source::{CurrentStorage, StorageAddress},
3    StorageClearable, StorageMapper, StorageMapperFromAddress,
4};
5use crate::{
6    abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName},
7    api::{ErrorApiImpl, StorageMapperApi},
8    codec::{
9        multi_encode_iter_or_handle_err, EncodeErrorHandler, TopDecode, TopEncode, TopEncodeMulti,
10        TopEncodeMultiOutput,
11    },
12    storage::{storage_clear, storage_set, StorageKey},
13    types::{ManagedAddress, ManagedType, MultiValueEncoded},
14};
15use core::marker::PhantomData;
16
17const ITEM_SUFFIX: &[u8] = b".item";
18const LEN_SUFFIX: &[u8] = b".len";
19
20static INDEX_OUT_OF_RANGE_ERR_MSG: &[u8] = b"index out of range";
21
22/// Manages a list of items of the same type.
23/// Saves each of the items under a separate key in storage.
24/// To produce each individual key, it concatenates the main key with a serialized 4-byte index.
25/// Indexes start from 1, instead of 0. (We avoid 0-value indexes to prevent confusion between an uninitialized variable and zero.)
26/// It also stores the count separately, at what would be index 0.
27/// The count is always kept in sync automatically.
28pub struct VecMapper<SA, T, A = CurrentStorage>
29where
30    SA: StorageMapperApi,
31    T: TopEncode + TopDecode + 'static,
32{
33    _phantom_api: PhantomData<SA>,
34    address: A,
35    base_key: StorageKey<SA>,
36    len_key: StorageKey<SA>,
37    _phantom_item: PhantomData<T>,
38}
39
40impl<SA, T> StorageMapper<SA> for VecMapper<SA, T, CurrentStorage>
41where
42    SA: StorageMapperApi,
43    T: TopEncode + TopDecode,
44{
45    fn new(base_key: StorageKey<SA>) -> Self {
46        let mut len_key = base_key.clone();
47        len_key.append_bytes(LEN_SUFFIX);
48
49        VecMapper {
50            _phantom_api: PhantomData,
51            address: CurrentStorage,
52            base_key,
53            len_key,
54            _phantom_item: PhantomData,
55        }
56    }
57}
58
59impl<SA, T> StorageMapperFromAddress<SA> for VecMapper<SA, T, ManagedAddress<SA>>
60where
61    SA: StorageMapperApi,
62    T: TopEncode + TopDecode,
63{
64    fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
65        let mut len_key = base_key.clone();
66        len_key.append_bytes(LEN_SUFFIX);
67
68        VecMapper {
69            _phantom_api: PhantomData,
70            address,
71            base_key,
72            len_key,
73            _phantom_item: PhantomData,
74        }
75    }
76}
77
78impl<SA, T> StorageClearable for VecMapper<SA, T, CurrentStorage>
79where
80    SA: StorageMapperApi,
81    T: TopEncode + TopDecode,
82{
83    fn clear(&mut self) {
84        self.clear();
85    }
86}
87
88impl<SA, T, A> VecMapper<SA, T, A>
89where
90    SA: StorageMapperApi,
91    A: StorageAddress<SA>,
92    T: TopEncode + TopDecode,
93{
94    fn item_key(&self, index: usize) -> StorageKey<SA> {
95        let mut item_key = self.base_key.clone();
96        item_key.append_bytes(ITEM_SUFFIX);
97        item_key.append_item(&index);
98        item_key
99    }
100
101    /// Number of items managed by the mapper.
102    pub fn len(&self) -> usize {
103        self.address.address_storage_get(self.len_key.as_ref())
104    }
105
106    /// True if no items present in the mapper.
107    pub fn is_empty(&self) -> bool {
108        self.len() == 0
109    }
110
111    /// Get item at index from storage.
112    /// Index must be valid (1 <= index <= count).
113    pub fn get(&self, index: usize) -> T {
114        if index == 0 || index > self.len() {
115            SA::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_ERR_MSG);
116        }
117        self.get_unchecked(index)
118    }
119
120    /// Get item at index from storage.
121    /// There are no restrictions on the index,
122    /// calling for an invalid index will simply return the zero-value.
123    pub fn get_unchecked(&self, index: usize) -> T {
124        self.address
125            .address_storage_get(self.item_key(index).as_ref())
126    }
127
128    /// Get item at index from storage.
129    /// If index is valid (1 <= index <= count), returns value at index,
130    /// else calls lambda given as argument.
131    /// The lambda only gets called lazily if the index is not valid.
132    pub fn get_or_else<F: FnOnce() -> T>(self, index: usize, or_else: F) -> T {
133        if index == 0 || index > self.len() {
134            or_else()
135        } else {
136            self.get_unchecked(index)
137        }
138    }
139
140    /// Checks whether or not there is anything ins storage at index.
141    /// Index must be valid (1 <= index <= count).
142    pub fn item_is_empty(&self, index: usize) -> bool {
143        if index == 0 || index > self.len() {
144            SA::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_ERR_MSG);
145        }
146        self.item_is_empty_unchecked(index)
147    }
148
149    /// Checks whether or not there is anything in storage at index.
150    /// There are no restrictions on the index,
151    /// calling for an invalid index will simply return `true`.
152    pub fn item_is_empty_unchecked(&self, index: usize) -> bool {
153        self.address
154            .address_storage_get_len(self.item_key(index).as_ref())
155            == 0
156    }
157
158    /// Loads all items from storage and places them in a Vec.
159    /// Can easily consume a lot of gas.
160    #[cfg(feature = "alloc")]
161    pub fn load_as_vec(&self) -> alloc::vec::Vec<T> {
162        self.iter().collect()
163    }
164
165    /// Provides a forward iterator.
166    pub fn iter(&self) -> Iter<'_, SA, T, A> {
167        Iter::new(self)
168    }
169}
170
171impl<SA, T> VecMapper<SA, T, CurrentStorage>
172where
173    SA: StorageMapperApi,
174    T: TopEncode + TopDecode,
175{
176    fn save_count(&self, new_len: usize) {
177        storage_set(self.len_key.as_ref(), &new_len);
178    }
179
180    /// Add one item at the end of the list.
181    /// Returns the index of the newly inserted item, which is also equal to the new number of elements.
182    pub fn push(&mut self, item: &T) -> usize {
183        let mut len = self.len();
184        len += 1;
185        storage_set(self.item_key(len).as_ref(), item);
186        self.save_count(len);
187        len
188    }
189
190    /// Adds multiple items at the end of the list.
191    /// Cheaper than multiple `push`-es because the count only gets updated once at the end.
192    /// Returns the index of the last inserted item, which is also equal to the new number of elements.
193    pub fn extend_from_slice(&mut self, items: &[T]) -> usize {
194        let mut len = self.len();
195        for item in items {
196            len += 1;
197            storage_set(self.item_key(len).as_ref(), item);
198        }
199        self.save_count(len);
200        len
201    }
202
203    /// Set item at index in storage.
204    /// Index must be valid (1 <= index <= count).
205    pub fn set(&mut self, index: usize, item: &T) {
206        if index == 0 || index > self.len() {
207            SA::error_api_impl().signal_error(&b"index out of range"[..]);
208        }
209        self.set_unchecked(index, item);
210    }
211
212    /// Syntactic sugar, to more compactly express a get, update and set in one line.
213    /// Takes whatever lies in storage, apples the given closure and saves the final value back to storage.
214    /// Propagates the return value of the given function.
215    pub fn update<R, F: FnOnce(&mut T) -> R>(&mut self, index: usize, f: F) -> R {
216        let mut value = self.get(index);
217        let result = f(&mut value);
218        self.set(index, &value);
219        result
220    }
221
222    /// Keeping `set_unchecked` private on purpose, so developers don't write out of index limits by accident.
223    fn set_unchecked(&self, index: usize, item: &T) {
224        storage_set(self.item_key(index).as_ref(), item);
225    }
226
227    /// Clears item at index from storage.
228    /// Index must be valid (1 <= index <= count).
229    pub fn clear_entry(&self, index: usize) {
230        if index == 0 || index > self.len() {
231            SA::error_api_impl().signal_error(&b"index out of range"[..]);
232        }
233        self.clear_entry_unchecked(index)
234    }
235
236    /// Clears item at index from storage.
237    /// There are no restrictions on the index,
238    /// calling for an invalid index will simply do nothing.
239    pub fn clear_entry_unchecked(&self, index: usize) {
240        storage_clear(self.item_key(index).as_ref());
241    }
242
243    /// Clears item at index from storage by swap remove
244    /// last item takes the index of the item to remove
245    /// and we remove the last index.
246    pub fn swap_remove(&mut self, index: usize) {
247        let _ = self.swap_remove_and_get_old_last(index);
248    }
249
250    pub(crate) fn swap_remove_and_get_old_last(&mut self, index: usize) -> Option<T> {
251        let last_item_index = self.len();
252        if index == 0 || index > last_item_index {
253            SA::error_api_impl().signal_error(&b"index out of range"[..]);
254        }
255
256        let mut last_item_as_option = Option::None;
257        if index != last_item_index {
258            let last_item = self.get(last_item_index);
259            self.set(index, &last_item);
260            last_item_as_option = Some(last_item);
261        }
262        self.clear_entry(last_item_index);
263        self.save_count(last_item_index - 1);
264        last_item_as_option
265    }
266
267    /// Deletes all contents form storage and sets count to 0.
268    /// Can easily consume a lot of gas.
269    pub fn clear(&mut self) {
270        let len = self.len();
271        for i in 1..=len {
272            storage_clear(self.item_key(i).as_ref());
273        }
274        self.save_count(0);
275    }
276}
277
278impl<'a, SA, T, A> IntoIterator for &'a VecMapper<SA, T, A>
279where
280    SA: StorageMapperApi,
281    A: StorageAddress<SA>,
282    T: TopEncode + TopDecode + 'static,
283{
284    type Item = T;
285
286    type IntoIter = Iter<'a, SA, T, A>;
287
288    fn into_iter(self) -> Self::IntoIter {
289        self.iter()
290    }
291}
292
293/// An iterator over the elements of a `VecMapper`.
294///
295/// This `struct` is created by [`VecMapper::iter()`]. See its
296/// documentation for more.
297pub struct Iter<'a, SA, T, A>
298where
299    SA: StorageMapperApi,
300    A: StorageAddress<SA>,
301    T: TopEncode + TopDecode + 'static,
302{
303    index: usize,
304    len: usize,
305    vec: &'a VecMapper<SA, T, A>,
306}
307
308impl<'a, SA, T, A> Iter<'a, SA, T, A>
309where
310    SA: StorageMapperApi,
311    A: StorageAddress<SA>,
312    T: TopEncode + TopDecode + 'static,
313{
314    fn new(vec: &'a VecMapper<SA, T, A>) -> Iter<'a, SA, T, A> {
315        Iter {
316            index: 1,
317            len: vec.len(),
318            vec,
319        }
320    }
321}
322
323impl<SA, T, A> Iterator for Iter<'_, SA, T, A>
324where
325    SA: StorageMapperApi,
326    A: StorageAddress<SA>,
327    T: TopEncode + TopDecode + 'static,
328{
329    type Item = T;
330
331    #[inline]
332    fn next(&mut self) -> Option<T> {
333        let current_index = self.index;
334        if current_index > self.len {
335            return None;
336        }
337        self.index += 1;
338        Some(self.vec.get_unchecked(current_index))
339    }
340}
341
342/// Behaves like a MultiResultVec when an endpoint result.
343impl<SA, T> TopEncodeMulti for VecMapper<SA, T, CurrentStorage>
344where
345    SA: StorageMapperApi,
346    T: TopEncode + TopDecode,
347{
348    fn multi_encode_or_handle_err<O, H>(&self, output: &mut O, h: H) -> Result<(), H::HandledErr>
349    where
350        O: TopEncodeMultiOutput,
351        H: EncodeErrorHandler,
352    {
353        multi_encode_iter_or_handle_err(self.iter(), output, h)
354    }
355}
356
357impl<SA, T> TypeAbiFrom<VecMapper<SA, T, CurrentStorage>> for MultiValueEncoded<SA, T>
358where
359    SA: StorageMapperApi,
360    T: TopEncode + TopDecode,
361{
362}
363
364impl<SA, T> TypeAbiFrom<Self> for VecMapper<SA, T, CurrentStorage>
365where
366    SA: StorageMapperApi,
367    T: TopEncode + TopDecode,
368{
369}
370
371/// Behaves like a MultiResultVec when an endpoint result.
372impl<SA, T> TypeAbi for VecMapper<SA, T, CurrentStorage>
373where
374    SA: StorageMapperApi,
375    T: TopEncode + TopDecode + TypeAbi,
376{
377    type Unmanaged = Self;
378
379    fn type_name() -> TypeName {
380        crate::abi::type_name_variadic::<T>()
381    }
382
383    fn type_name_rust() -> TypeName {
384        crate::abi::type_name_multi_value_encoded::<T>()
385    }
386
387    fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {
388        T::provide_type_descriptions(accumulator);
389    }
390
391    fn is_variadic() -> bool {
392        true
393    }
394}