Skip to main content

multiversx_sc/storage/mappers/
queue_mapper.rs

1use core::marker::PhantomData;
2
3use super::{
4    StorageClearable, StorageMapper, StorageMapperFromAddress,
5    source::{CurrentStorage, StorageAddress},
6};
7use crate::{
8    abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName},
9    api::StorageMapperApi,
10    codec::{
11        self, DecodeDefault, EncodeDefault, EncodeErrorHandler, TopDecode, TopEncode,
12        TopEncodeMulti, TopEncodeMultiOutput,
13        derive::{TopDecode, TopDecodeOrDefault, TopEncode, TopEncodeOrDefault},
14        multi_encode_iter_or_handle_err,
15    },
16    storage::{StorageKey, storage_set},
17    types::{ManagedAddress, ManagedType, MultiValueEncoded},
18};
19use alloc::vec::Vec;
20
21const NULL_ENTRY: u32 = 0;
22const INFO_IDENTIFIER: &[u8] = b".info";
23const NODE_IDENTIFIER: &[u8] = b".node_links";
24const VALUE_IDENTIFIER: &[u8] = b".value";
25
26#[derive(TopEncode, TopDecode, PartialEq, Eq, Clone, Copy)]
27pub struct Node {
28    pub previous: u32,
29    pub next: u32,
30}
31
32#[derive(TopEncodeOrDefault, TopDecodeOrDefault, PartialEq, Eq, Clone, Copy)]
33pub struct QueueMapperInfo {
34    pub len: u32,
35    pub front: u32,
36    pub back: u32,
37    pub new: u32,
38}
39
40impl EncodeDefault for QueueMapperInfo {
41    fn is_default(&self) -> bool {
42        self.len == 0
43    }
44}
45
46impl DecodeDefault for QueueMapperInfo {
47    fn default() -> Self {
48        Self {
49            len: 0,
50            front: 0,
51            back: 0,
52            new: 0,
53        }
54    }
55}
56
57impl QueueMapperInfo {
58    pub fn generate_new_node_id(&mut self) -> u32 {
59        self.new += 1;
60        self.new
61    }
62}
63
64/// A storage mapper implementing a double-ended queue (deque) with efficient operations at both ends.
65///
66/// # Storage Layout
67///
68/// The `QueueMapper` stores metadata and separates node structure from values:
69///
70/// 1. **Metadata**:
71///    - `base_key + ".info"` → `QueueMapperInfo` struct containing:
72///      - `len`: number of elements
73///      - `front`: node ID of the first element
74///      - `back`: node ID of the last element
75///      - `new`: counter for generating unique node IDs
76///
77/// 2. **Node links**:
78///    - `base_key + ".node_links" + node_id` → `Node` struct with:
79///      - `previous`: ID of the previous node (0 if none)
80///      - `next`: ID of the next node (0 if none)
81///
82/// 3. **Values**:
83///    - `base_key + ".value" + node_id` → the stored item
84///
85/// # Main Operations
86///
87/// - **Enqueue back**: `push_back(item)` - Adds to the end. O(1) with constant storage writes.
88/// - **Enqueue front**: `push_front(item)` - Adds to the beginning. O(1) with constant storage writes.
89/// - **Dequeue front**: `pop_front()` - Removes from the beginning. O(1).
90/// - **Dequeue back**: `pop_back()` - Removes from the end. O(1).
91/// - **Peek**: `front()` / `back()` - Views elements without removing. O(1).
92/// - **Iteration**: `iter()` - Traverses from front to back in order.
93///
94/// # Trade-offs
95///
96/// - **Pros**: O(1) operations at both ends; ideal for FIFO/LIFO patterns; maintains insertion order.
97/// - **Cons**: Cannot insert/remove from middle; no random access by index; higher storage overhead
98///   (separate storage for links and values); removed nodes leave gaps in node ID space.
99///
100/// # Use Cases
101///
102/// - Task/job queues (FIFO processing)
103/// - Undo/redo stacks
104/// - Any scenario requiring efficient head/tail operations
105///
106/// # Example
107///
108/// ```rust
109/// # use multiversx_sc::storage::mappers::{StorageMapper, QueueMapper};
110/// # fn example<SA: multiversx_sc::api::StorageMapperApi>() {
111/// # let mut mapper = QueueMapper::<SA, u64>::new(
112/// #     multiversx_sc::storage::StorageKey::new(&b"task_queue"[..])
113/// # );
114/// // Add tasks to the queue
115/// mapper.push_back(100);
116/// mapper.push_back(200);
117/// mapper.push_back(300);
118///
119/// assert_eq!(mapper.len(), 3);
120/// assert_eq!(mapper.front(), Some(100));
121/// assert_eq!(mapper.back(), Some(300));
122///
123/// // Add high-priority task at front
124/// mapper.push_front(50);
125/// assert_eq!(mapper.front(), Some(50));
126/// assert_eq!(mapper.len(), 4);
127///
128/// // Process tasks from front (FIFO)
129/// let task1 = mapper.pop_front();
130/// assert_eq!(task1, Some(50));
131/// assert_eq!(mapper.len(), 3);
132///
133/// let task2 = mapper.pop_front();
134/// assert_eq!(task2, Some(100));
135///
136/// // Can also remove from back
137/// let last = mapper.pop_back();
138/// assert_eq!(last, Some(300));
139/// assert_eq!(mapper.len(), 1);
140///
141/// // Iterate through remaining tasks
142/// for task in mapper.iter() {
143///     // Process task
144/// }
145/// # }
146/// ```
147pub struct QueueMapper<SA, T, A = CurrentStorage>
148where
149    SA: StorageMapperApi,
150    A: StorageAddress<SA>,
151    T: TopEncode + TopDecode + 'static,
152{
153    _phantom_api: PhantomData<SA>,
154    address: A,
155    base_key: StorageKey<SA>,
156    _phantom_item: PhantomData<T>,
157}
158
159impl<SA, T> StorageMapper<SA> for QueueMapper<SA, T, CurrentStorage>
160where
161    SA: StorageMapperApi,
162    T: TopEncode + TopDecode,
163{
164    fn new(base_key: StorageKey<SA>) -> Self {
165        QueueMapper {
166            _phantom_api: PhantomData,
167            address: CurrentStorage,
168            base_key,
169            _phantom_item: PhantomData,
170        }
171    }
172}
173
174impl<SA, T> StorageClearable for QueueMapper<SA, T, CurrentStorage>
175where
176    SA: StorageMapperApi,
177    T: TopEncode + TopDecode,
178{
179    fn clear(&mut self) {
180        let info = self.get_info();
181        let mut node_id = info.front;
182        while node_id != NULL_ENTRY {
183            let node = self.get_node(node_id);
184            self.clear_node(node_id);
185            self.clear_value(node_id);
186            node_id = node.next;
187        }
188        self.set_info(QueueMapperInfo::default());
189    }
190}
191
192impl<SA, T> QueueMapper<SA, T, CurrentStorage>
193where
194    SA: StorageMapperApi,
195    T: TopEncode + TopDecode,
196{
197    fn set_info(&mut self, value: QueueMapperInfo) {
198        storage_set(self.build_name_key(INFO_IDENTIFIER).as_ref(), &value);
199    }
200
201    fn set_node(&mut self, node_id: u32, item: Node) {
202        storage_set(
203            self.build_node_id_named_key(NODE_IDENTIFIER, node_id)
204                .as_ref(),
205            &item,
206        );
207    }
208
209    fn clear_node(&mut self, node_id: u32) {
210        storage_set(
211            self.build_node_id_named_key(NODE_IDENTIFIER, node_id)
212                .as_ref(),
213            &codec::Empty,
214        );
215    }
216
217    fn set_value(&mut self, node_id: u32, value: &T) {
218        storage_set(
219            self.build_node_id_named_key(VALUE_IDENTIFIER, node_id)
220                .as_ref(),
221            value,
222        )
223    }
224
225    fn clear_value(&mut self, node_id: u32) {
226        storage_set(
227            self.build_node_id_named_key(VALUE_IDENTIFIER, node_id)
228                .as_ref(),
229            &codec::Empty,
230        )
231    }
232
233    /// Appends an element to the back of a queue
234    /// and returns the node id of the newly added node.
235    ///
236    /// This operation should compute in *O*(1) time.
237    pub(crate) fn push_back_node_id(&mut self, elt: &T) -> u32 {
238        let mut info = self.get_info();
239        let new_node_id = info.generate_new_node_id();
240        let mut previous = NULL_ENTRY;
241        if info.len == 0 {
242            info.front = new_node_id;
243        } else {
244            let back = info.back;
245            let mut back_node = self.get_node(back);
246            back_node.next = new_node_id;
247            previous = back;
248            self.set_node(back, back_node);
249        }
250        self.set_node(
251            new_node_id,
252            Node {
253                previous,
254                next: NULL_ENTRY,
255            },
256        );
257        info.back = new_node_id;
258        self.set_value(new_node_id, elt);
259        info.len += 1;
260        self.set_info(info);
261        new_node_id
262    }
263
264    /// Appends an element to the back of a queue.
265    ///
266    /// This operation should compute in *O*(1) time.
267    pub fn push_back(&mut self, elt: T) {
268        let _ = self.push_back_node_id(&elt);
269    }
270
271    /// Adds an element first in the queue.
272    ///
273    /// This operation should compute in *O*(1) time.
274    pub fn push_front(&mut self, elt: T) {
275        let mut info = self.get_info();
276        let new_node_id = info.generate_new_node_id();
277        let mut next = NULL_ENTRY;
278        if info.len == 0 {
279            info.back = new_node_id;
280        } else {
281            let front = info.front;
282            let mut front_node = self.get_node(front);
283            front_node.previous = new_node_id;
284            next = front;
285            self.set_node(front, front_node);
286        }
287        self.set_node(
288            new_node_id,
289            Node {
290                previous: NULL_ENTRY,
291                next,
292            },
293        );
294        info.front = new_node_id;
295        self.set_value(new_node_id, &elt);
296        info.len += 1;
297        self.set_info(info);
298    }
299
300    /// Removes the last element from a queue and returns it, or `None` if
301    /// it is empty.
302    ///
303    /// This operation should compute in *O*(1) time.
304    pub fn pop_back(&mut self) -> Option<T> {
305        self.remove_by_node_id(self.get_info().back)
306    }
307
308    /// Removes the first element and returns it, or `None` if the queue is
309    /// empty.
310    ///
311    /// This operation should compute in *O*(1) time.
312    pub fn pop_front(&mut self) -> Option<T> {
313        self.remove_by_node_id(self.get_info().front)
314    }
315
316    /// Removes element with the given node id and returns it, or `None` if the queue is
317    /// empty.
318    /// Note: has undefined behavior if there's no node with the given node id in the queue
319    ///
320    /// This operation should compute in *O*(1) time.
321    pub(crate) fn remove_by_node_id(&mut self, node_id: u32) -> Option<T> {
322        if node_id == NULL_ENTRY {
323            return None;
324        }
325        let node = self.get_node(node_id);
326
327        let mut info = self.get_info();
328        if node.previous == NULL_ENTRY {
329            info.front = node.next;
330        } else {
331            let mut previous = self.get_node(node.previous);
332            previous.next = node.next;
333            self.set_node(node.previous, previous);
334        }
335
336        if node.next == NULL_ENTRY {
337            info.back = node.previous;
338        } else {
339            let mut next = self.get_node(node.next);
340            next.previous = node.previous;
341            self.set_node(node.next, next);
342        }
343
344        self.clear_node(node_id);
345        let removed_value = self.get_value(node_id);
346        self.clear_value(node_id);
347        info.len -= 1;
348        self.set_info(info);
349        Some(removed_value)
350    }
351}
352
353impl<SA, T> StorageMapperFromAddress<SA> for QueueMapper<SA, T, ManagedAddress<SA>>
354where
355    SA: StorageMapperApi,
356    T: TopEncode + TopDecode,
357{
358    fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
359        QueueMapper {
360            _phantom_api: PhantomData::<SA>,
361            address,
362            base_key,
363            _phantom_item: PhantomData::<T>,
364        }
365    }
366}
367
368impl<SA, A, T> QueueMapper<SA, T, A>
369where
370    SA: StorageMapperApi,
371    A: StorageAddress<SA>,
372    T: TopEncode + TopDecode + 'static,
373{
374    fn build_node_id_named_key(&self, name: &[u8], node_id: u32) -> StorageKey<SA> {
375        let mut named_key = self.base_key.clone();
376        named_key.append_bytes(name);
377        named_key.append_item(&node_id);
378        named_key
379    }
380
381    fn build_name_key(&self, name: &[u8]) -> StorageKey<SA> {
382        let mut name_key = self.base_key.clone();
383        name_key.append_bytes(name);
384        name_key
385    }
386
387    pub fn iter(&self) -> Iter<'_, SA, A, T> {
388        Iter::new(self)
389    }
390
391    pub fn iter_from_node_id(&self, node_id: u32) -> Iter<'_, SA, A, T> {
392        Iter::new_from_node_id(self, node_id)
393    }
394
395    fn get_info(&self) -> QueueMapperInfo {
396        self.address
397            .address_storage_get(self.build_name_key(INFO_IDENTIFIER).as_ref())
398    }
399
400    pub fn get_node(&self, node_id: u32) -> Node {
401        self.address.address_storage_get(
402            self.build_node_id_named_key(NODE_IDENTIFIER, node_id)
403                .as_ref(),
404        )
405    }
406
407    fn get_value(&self, node_id: u32) -> T {
408        self.address.address_storage_get(
409            self.build_node_id_named_key(VALUE_IDENTIFIER, node_id)
410                .as_ref(),
411        )
412    }
413
414    pub fn get_value_option(&self, node_id: u32) -> Option<T> {
415        if node_id == NULL_ENTRY {
416            return None;
417        }
418        Some(self.get_value(node_id))
419    }
420
421    /// Returns `true` if the `Queue` is empty.
422    ///
423    /// This operation should compute in *O*(1) time.
424    pub fn is_empty(&self) -> bool {
425        self.get_info().len == 0
426    }
427
428    /// Returns the length of the `Queue`.
429    ///
430    /// This operation should compute in *O*(1) time.
431    pub fn len(&self) -> usize {
432        self.get_info().len as usize
433    }
434
435    /// Provides a copy to the front element, or `None` if the queue is
436    /// empty.
437    pub fn front(&self) -> Option<T> {
438        self.get_value_option(self.get_info().front)
439    }
440
441    /// Provides a copy to the back element, or `None` if the queue is
442    /// empty.
443    pub fn back(&self) -> Option<T> {
444        self.get_value_option(self.get_info().back)
445    }
446
447    /// Runs several checks in order to verify that both forwards and backwards iteration
448    /// yields the same node entries and that the number of items in the queue is correct.
449    /// Used for unit testing.
450    ///
451    /// This operation should compute in *O*(n) time.
452    pub fn check_internal_consistency(&self) -> bool {
453        let info = self.get_info();
454        let mut front = info.front;
455        let mut back = info.back;
456        if info.len == 0 {
457            // if the queue is empty, both ends should point to null entries
458            if front != NULL_ENTRY {
459                return false;
460            }
461            if back != NULL_ENTRY {
462                return false;
463            }
464            true
465        } else {
466            // if the queue is non-empty, both ends should point to non-null entries
467            if front == NULL_ENTRY {
468                return false;
469            }
470            if back == NULL_ENTRY {
471                return false;
472            }
473
474            // the node before the first and the one after the last should both be null
475            if self.get_node(front).previous != NULL_ENTRY {
476                return false;
477            }
478            if self.get_node(back).next != NULL_ENTRY {
479                return false;
480            }
481
482            // iterate forwards
483            let mut forwards = Vec::new();
484            while front != NULL_ENTRY {
485                forwards.push(front);
486                front = self.get_node(front).next;
487            }
488            if forwards.len() != info.len as usize {
489                return false;
490            }
491
492            // iterate backwards
493            let mut backwards = Vec::new();
494            while back != NULL_ENTRY {
495                backwards.push(back);
496                back = self.get_node(back).previous;
497            }
498            if backwards.len() != info.len as usize {
499                return false;
500            }
501
502            // check that both iterations match element-wise
503            let backwards_reversed: Vec<u32> = backwards.iter().rev().cloned().collect();
504            if forwards != backwards_reversed {
505                return false;
506            }
507
508            // check that the node IDs are unique
509            forwards.sort_unstable();
510            forwards.dedup();
511            if forwards.len() != info.len as usize {
512                return false;
513            }
514            true
515        }
516    }
517}
518
519impl<'a, SA, A, T> IntoIterator for &'a QueueMapper<SA, T, A>
520where
521    SA: StorageMapperApi,
522    A: StorageAddress<SA>,
523    T: TopEncode + TopDecode + 'static,
524{
525    type Item = T;
526
527    type IntoIter = Iter<'a, SA, A, T>;
528
529    fn into_iter(self) -> Self::IntoIter {
530        self.iter()
531    }
532}
533
534/// An iterator over the elements of a `QueueMapper`.
535///
536/// This `struct` is created by [`QueueMapper::iter()`]. See its
537/// documentation for more.
538pub struct Iter<'a, SA, A, T>
539where
540    SA: StorageMapperApi,
541    A: StorageAddress<SA>,
542    T: TopEncode + TopDecode + 'static,
543{
544    node_id: u32,
545    queue: &'a QueueMapper<SA, T, A>,
546}
547
548impl<'a, SA, A, T> Iter<'a, SA, A, T>
549where
550    SA: StorageMapperApi,
551    A: StorageAddress<SA>,
552    T: TopEncode + TopDecode + 'static,
553{
554    fn new(queue: &'a QueueMapper<SA, T, A>) -> Iter<'a, SA, A, T> {
555        Iter {
556            node_id: queue.get_info().front,
557            queue,
558        }
559    }
560
561    pub fn new_from_node_id(queue: &'a QueueMapper<SA, T, A>, node_id: u32) -> Iter<'a, SA, A, T> {
562        Iter { node_id, queue }
563    }
564}
565
566impl<SA, A, T> Iterator for Iter<'_, SA, A, T>
567where
568    SA: StorageMapperApi,
569    A: StorageAddress<SA>,
570    T: TopEncode + TopDecode + 'static,
571{
572    type Item = T;
573
574    #[inline]
575    fn next(&mut self) -> Option<T> {
576        let current_node_id = self.node_id;
577        if current_node_id == NULL_ENTRY {
578            return None;
579        }
580        self.node_id = self.queue.get_node(current_node_id).next;
581        Some(self.queue.get_value(current_node_id))
582    }
583}
584
585/// Behaves like a MultiResultVec when an endpoint result.
586impl<SA, T> TopEncodeMulti for QueueMapper<SA, T, CurrentStorage>
587where
588    SA: StorageMapperApi,
589    T: TopEncode + TopDecode,
590{
591    fn multi_encode_or_handle_err<O, H>(&self, output: &mut O, h: H) -> Result<(), H::HandledErr>
592    where
593        O: TopEncodeMultiOutput,
594        H: EncodeErrorHandler,
595    {
596        multi_encode_iter_or_handle_err(self.iter(), output, h)
597    }
598}
599
600impl<SA, T> TypeAbiFrom<QueueMapper<SA, T, CurrentStorage>> for MultiValueEncoded<SA, T>
601where
602    SA: StorageMapperApi,
603    T: TopEncode + TopDecode,
604{
605}
606
607impl<SA, T> TypeAbiFrom<Self> for QueueMapper<SA, T, CurrentStorage>
608where
609    SA: StorageMapperApi,
610    T: TopEncode + TopDecode,
611{
612}
613
614/// Behaves like a MultiResultVec when an endpoint result.
615impl<SA, T> TypeAbi for QueueMapper<SA, T, CurrentStorage>
616where
617    SA: StorageMapperApi,
618    T: TopEncode + TopDecode + TypeAbi,
619{
620    type Unmanaged = Self;
621
622    fn type_name() -> TypeName {
623        crate::abi::type_name_variadic::<T>()
624    }
625
626    fn type_name_rust() -> TypeName {
627        crate::abi::type_name_multi_value_encoded::<T>()
628    }
629
630    fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {
631        T::provide_type_descriptions(accumulator);
632    }
633
634    fn is_variadic() -> bool {
635        true
636    }
637}