Skip to main content

cu29_runtime/
copperlist.rs

1//! CopperList is the main data structure used by Copper to communicate between tasks.
2//! It is a queue that can be used to store preallocated messages between tasks in memory order.
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6use alloc::alloc::{alloc_zeroed, handle_alloc_error};
7use alloc::boxed::Box;
8use alloc::vec::Vec;
9use core::alloc::Layout;
10
11use bincode::{Decode, Encode};
12use core::fmt;
13
14use core::fmt::Display;
15use core::iter::{Chain, Rev};
16use core::slice::{Iter as SliceIter, IterMut as SliceIterMut};
17use cu29_traits::{CopperListTuple, ErasedCuStampedData, ErasedCuStampedDataSet};
18use serde_derive::{Deserialize, Serialize};
19
20const MAX_TASKS: usize = 512;
21
22/// Not implemented yet.
23/// This mask will be used to for example filter out necessary regions of a copper list between remote systems.
24#[derive(Debug, Encode, Decode, PartialEq, Clone, Copy)]
25pub struct CopperLiskMask {
26    #[allow(dead_code)]
27    mask: [u128; MAX_TASKS / 128 + 1],
28}
29
30/// Those are the possible states along the lifetime of a CopperList.
31#[derive(Debug, Encode, Decode, Serialize, Deserialize, PartialEq, Copy, Clone)]
32pub enum CopperListState {
33    Free,
34    Initialized,
35    Processing,
36    DoneProcessing,
37    QueuedForSerialization,
38    BeingSerialized,
39}
40
41impl Display for CopperListState {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        match self {
44            CopperListState::Free => write!(f, "Free"),
45            CopperListState::Initialized => write!(f, "Initialized"),
46            CopperListState::Processing => write!(f, "Processing"),
47            CopperListState::DoneProcessing => write!(f, "DoneProcessing"),
48            CopperListState::QueuedForSerialization => write!(f, "QueuedForSerialization"),
49            CopperListState::BeingSerialized => write!(f, "BeingSerialized"),
50        }
51    }
52}
53
54/// A cycle's messages and identifier. Lifecycle state exists only in memory;
55/// every serialization contains `id` followed by `msgs`.
56#[derive(Debug, Encode, Decode, Serialize, Deserialize)]
57pub struct CopperList<P: CopperListTuple> {
58    pub id: u64,
59    // Runtime bookkeeping only; recorded lists reconstruct the serialization state.
60    #[bincode(skip, default = "deserialized_state")]
61    #[serde(skip, default = "deserialized_state")]
62    state: CopperListState,
63    pub msgs: P, // This is generated from the runtime.
64}
65
66fn deserialized_state() -> CopperListState {
67    CopperListState::BeingSerialized
68}
69
70impl<P: CopperListTuple> Default for CopperList<P> {
71    fn default() -> Self {
72        CopperList {
73            id: 0,
74            state: CopperListState::Free,
75            msgs: P::default(),
76        }
77    }
78}
79
80impl<P: CopperListTuple> CopperList<P> {
81    // This is not the usual way to create a CopperList, this is just for testing.
82    pub fn new(id: u64, msgs: P) -> Self {
83        CopperList {
84            id,
85            state: CopperListState::Initialized,
86            msgs,
87        }
88    }
89
90    pub fn change_state(&mut self, new_state: CopperListState) {
91        self.state = new_state; // TODO: probably wise here to enforce a state machine.
92    }
93
94    pub fn get_state(&self) -> CopperListState {
95        self.state
96    }
97
98    /// Restores the lifecycle state expected at allocation time and reruns
99    /// zero-memory fixups for payload containers that cannot remain valid after
100    /// raw zeroing. This does not imply a full `P::default()` payload reset.
101    #[doc(hidden)]
102    pub fn reset_for_runtime_use(&mut self, id: u64)
103    where
104        P: CuListZeroedInit,
105    {
106        self.id = id;
107        self.state = CopperListState::Initialized;
108        self.msgs.init_zeroed();
109    }
110}
111
112impl<P: CopperListTuple> ErasedCuStampedDataSet for CopperList<P> {
113    fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
114        self.msgs.cumsgs()
115    }
116}
117
118/// This structure maintains the entire memory needed by Copper for one loop for the inter tasks communication within a process.
119/// P or Payload is typically a Tuple of various types of messages that are exchanged between tasks.
120/// N is the maximum number of in flight Copper List the runtime can support.
121pub struct CuListsManager<P: CopperListTuple, const N: usize> {
122    data: Box<[CopperList<P>; N]>,
123    length: usize,
124    insertion_index: usize,
125    current_cl_id: u64,
126}
127
128impl<P: CopperListTuple + fmt::Debug, const N: usize> fmt::Debug for CuListsManager<P, N> {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        f.debug_struct("CuListsManager")
131            .field("data", &self.data)
132            .field("length", &self.length)
133            .field("insertion_index", &self.insertion_index)
134            // Do not include on_drop field
135            .finish()
136    }
137}
138
139pub type Iter<'a, T> = Chain<Rev<SliceIter<'a, T>>, Rev<SliceIter<'a, T>>>;
140pub type IterMut<'a, T> = Chain<Rev<SliceIterMut<'a, T>>, Rev<SliceIterMut<'a, T>>>;
141pub type AscIter<'a, T> = Chain<SliceIter<'a, T>, SliceIter<'a, T>>;
142pub type AscIterMut<'a, T> = Chain<SliceIterMut<'a, T>, SliceIterMut<'a, T>>;
143
144/// Initializes fields that cannot be zeroed after allocating a zeroed
145/// [`CopperList`].
146pub trait CuListZeroedInit: CopperListTuple {
147    /// Fixes up a zero-initialized copper list so that all internal fields are
148    /// in a valid state.
149    fn init_zeroed(&mut self);
150}
151
152impl<P: CopperListTuple + CuListZeroedInit, const N: usize> Default for CuListsManager<P, N> {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158impl<P: CopperListTuple, const N: usize> CuListsManager<P, N> {
159    pub fn new() -> Self
160    where
161        P: CuListZeroedInit,
162    {
163        // SAFETY: We allocate zeroed memory and immediately initialize required fields.
164        let data = unsafe {
165            let layout = Layout::new::<[CopperList<P>; N]>();
166            let ptr = alloc_zeroed(layout) as *mut [CopperList<P>; N];
167            if ptr.is_null() {
168                handle_alloc_error(layout);
169            }
170            Box::from_raw(ptr)
171        };
172        let mut manager = CuListsManager {
173            data,
174            length: 0,
175            insertion_index: 0,
176            current_cl_id: 0,
177        };
178
179        for cl in manager.data.iter_mut() {
180            cl.msgs.init_zeroed();
181        }
182
183        manager
184    }
185
186    /// Returns the current number of elements in the queue.
187    ///
188    #[inline]
189    pub fn len(&self) -> usize {
190        self.length
191    }
192
193    /// Returns `true` if the queue contains no elements.
194    ///
195    #[inline]
196    pub fn is_empty(&self) -> bool {
197        self.length == 0
198    }
199
200    /// Returns `true` if the queue is full.
201    ///
202    #[inline]
203    pub fn is_full(&self) -> bool {
204        N == self.len()
205    }
206
207    /// Clears the queue.
208    ///
209    #[inline]
210    pub fn clear(&mut self) {
211        self.insertion_index = 0;
212        self.length = 0;
213    }
214
215    #[inline]
216    pub fn create(&mut self) -> Option<&mut CopperList<P>>
217    where
218        P: CuListZeroedInit,
219    {
220        if self.is_full() {
221            return None;
222        }
223        let next_id = self.current_cl_id;
224        let result = &mut self.data[self.insertion_index];
225        self.insertion_index = (self.insertion_index + 1) % N;
226        self.length += 1;
227
228        // We assign a unique id to each CopperList to be able to track them across their lifetime.
229        result.reset_for_runtime_use(next_id);
230        self.current_cl_id += 1;
231
232        Some(result)
233    }
234
235    /// Returns the next copper-list id that will be assigned by [`create`](Self::create).
236    #[inline]
237    pub fn next_cl_id(&self) -> u64 {
238        self.current_cl_id
239    }
240
241    pub(crate) fn set_next_replay_id(&mut self, id: u64) {
242        debug_assert_eq!(self.length, 0);
243        self.current_cl_id = id;
244    }
245
246    /// Returns the most recently assigned copper-list id.
247    ///
248    /// Before the first call to [`create`](Self::create), this returns `0`.
249    #[inline]
250    pub fn last_cl_id(&self) -> u64 {
251        self.current_cl_id.saturating_sub(1)
252    }
253
254    /// Peeks at the last element in the queue.
255    #[inline]
256    pub fn peek(&self) -> Option<&CopperList<P>> {
257        if self.length == 0 {
258            return None;
259        }
260        let index = if self.insertion_index == 0 {
261            N - 1
262        } else {
263            self.insertion_index - 1
264        };
265        Some(&self.data[index])
266    }
267
268    #[inline]
269    #[allow(dead_code)]
270    fn drop_last(&mut self) {
271        if self.length == 0 {
272            return;
273        }
274        if self.insertion_index == 0 {
275            self.insertion_index = N - 1;
276        } else {
277            self.insertion_index -= 1;
278        }
279        self.length -= 1;
280    }
281
282    #[inline]
283    pub fn pop(&mut self) -> Option<&mut CopperList<P>> {
284        if self.length == 0 {
285            return None;
286        }
287        if self.insertion_index == 0 {
288            self.insertion_index = N - 1;
289        } else {
290            self.insertion_index -= 1;
291        }
292        self.length -= 1;
293        Some(&mut self.data[self.insertion_index])
294    }
295
296    /// Returns an iterator over the queue's contents.
297    ///
298    /// The iterator goes from the most recently pushed items to the oldest ones.
299    ///
300    #[inline]
301    pub fn iter(&self) -> Iter<'_, CopperList<P>> {
302        let (a, b) = self.data[0..self.length].split_at(self.insertion_index);
303        a.iter().rev().chain(b.iter().rev())
304    }
305
306    /// Returns a mutable iterator over the queue's contents.
307    ///
308    /// The iterator goes from the most recently pushed items to the oldest ones.
309    ///
310    #[inline]
311    pub fn iter_mut(&mut self) -> IterMut<'_, CopperList<P>> {
312        let (a, b) = self.data[0..self.length].split_at_mut(self.insertion_index);
313        a.iter_mut().rev().chain(b.iter_mut().rev())
314    }
315
316    /// Returns an ascending iterator over the queue's contents.
317    ///
318    /// The iterator goes from the least recently pushed items to the newest ones.
319    ///
320    #[inline]
321    pub fn asc_iter(&self) -> AscIter<'_, CopperList<P>> {
322        let (a, b) = self.data[0..self.length].split_at(self.insertion_index);
323        b.iter().chain(a.iter())
324    }
325
326    /// Returns a mutable ascending iterator over the queue's contents.
327    ///
328    /// The iterator goes from the least recently pushed items to the newest ones.
329    ///
330    #[inline]
331    pub fn asc_iter_mut(&mut self) -> AscIterMut<'_, CopperList<P>> {
332        let (a, b) = self.data[0..self.length].split_at_mut(self.insertion_index);
333        b.iter_mut().chain(a.iter_mut())
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340    use cu29_traits::{ErasedCuStampedData, ErasedCuStampedDataSet, MatchingTasks};
341    use serde::{Deserialize, Serialize, Serializer};
342
343    #[derive(Debug, Encode, Decode, PartialEq, Clone, Copy, Serialize, Deserialize, Default)]
344    struct CuStampedDataSet(i32);
345
346    impl ErasedCuStampedDataSet for CuStampedDataSet {
347        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
348            Vec::new()
349        }
350    }
351
352    impl MatchingTasks for CuStampedDataSet {
353        fn get_all_task_ids() -> &'static [&'static str] {
354            &[]
355        }
356    }
357
358    impl CuListZeroedInit for CuStampedDataSet {
359        fn init_zeroed(&mut self) {}
360    }
361
362    #[test]
363    fn serialization_omits_runtime_state() {
364        let expected =
365            bincode::encode_to_vec((42u64, CuStampedDataSet(123)), bincode::config::standard())
366                .unwrap();
367        for state in [
368            CopperListState::Free,
369            CopperListState::Initialized,
370            CopperListState::Processing,
371            CopperListState::DoneProcessing,
372            CopperListState::QueuedForSerialization,
373            CopperListState::BeingSerialized,
374        ] {
375            let mut list = CopperList::new(42, CuStampedDataSet(123));
376            list.change_state(state);
377            let bytes = bincode::encode_to_vec(&list, bincode::config::standard()).unwrap();
378            assert_eq!(bytes, expected);
379            let old_bytes =
380                bincode::encode_to_vec((list.id, state, &list.msgs), bincode::config::standard())
381                    .unwrap();
382            assert_eq!(old_bytes.len(), bytes.len() + 1);
383            let (decoded, used): (CopperList<CuStampedDataSet>, usize) =
384                bincode::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
385            assert_eq!(decoded.id, list.id);
386            assert_eq!(decoded.msgs, list.msgs);
387            assert_eq!(decoded.get_state(), CopperListState::BeingSerialized);
388            assert_eq!(used, bytes.len());
389            let (borrowed, used): (CopperList<CuStampedDataSet>, usize) =
390                bincode::borrow_decode_from_slice(&bytes, bincode::config::standard()).unwrap();
391            assert_eq!(borrowed.get_state(), CopperListState::BeingSerialized);
392            assert_eq!(borrowed.msgs, list.msgs);
393            assert_eq!(used, bytes.len());
394            assert_eq!(list.get_state(), state);
395            #[cfg(feature = "std")]
396            {
397                let json = serde_json::to_value(&list).unwrap();
398                assert_eq!(json, serde_json::json!({"id": 42, "msgs": 123}));
399                let decoded: CopperList<CuStampedDataSet> = serde_json::from_value(json).unwrap();
400                assert_eq!(decoded.get_state(), CopperListState::BeingSerialized);
401                assert_eq!(decoded.msgs, list.msgs);
402            }
403        }
404    }
405
406    #[test]
407    fn empty_queue() {
408        let q = CuListsManager::<CuStampedDataSet, 5>::new();
409
410        assert!(q.is_empty());
411        assert!(q.iter().next().is_none());
412        assert!(q.asc_iter().next().is_none());
413        assert!(q.peek().is_none());
414    }
415
416    #[test]
417    fn partially_full_queue() {
418        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
419        q.create().unwrap().msgs.0 = 1;
420        q.create().unwrap().msgs.0 = 2;
421        q.create().unwrap().msgs.0 = 3;
422
423        assert!(!q.is_empty());
424        assert_eq!(q.len(), 3);
425
426        let res: Vec<i32> = q.iter().map(|x| x.msgs.0).collect();
427        assert_eq!(res, [3, 2, 1]);
428    }
429
430    #[test]
431    fn full_queue() {
432        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
433        q.create().unwrap().msgs.0 = 1;
434        q.create().unwrap().msgs.0 = 2;
435        q.create().unwrap().msgs.0 = 3;
436        q.create().unwrap().msgs.0 = 4;
437        q.create().unwrap().msgs.0 = 5;
438        assert_eq!(q.len(), 5);
439
440        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
441        assert_eq!(res, [5, 4, 3, 2, 1]);
442    }
443
444    #[test]
445    fn over_full_queue() {
446        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
447        q.create().unwrap().msgs.0 = 1;
448        q.create().unwrap().msgs.0 = 2;
449        q.create().unwrap().msgs.0 = 3;
450        q.create().unwrap().msgs.0 = 4;
451        q.create().unwrap().msgs.0 = 5;
452        assert!(q.create().is_none());
453        assert_eq!(q.len(), 5);
454
455        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
456        assert_eq!(res, [5, 4, 3, 2, 1]);
457    }
458
459    #[test]
460    fn clear() {
461        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
462        q.create().unwrap().msgs.0 = 1;
463        q.create().unwrap().msgs.0 = 2;
464        q.create().unwrap().msgs.0 = 3;
465        q.create().unwrap().msgs.0 = 4;
466        q.create().unwrap().msgs.0 = 5;
467        assert!(q.create().is_none());
468        assert_eq!(q.len(), 5);
469
470        q.clear();
471
472        assert_eq!(q.len(), 0);
473        assert!(q.iter().next().is_none());
474
475        q.create().unwrap().msgs.0 = 1;
476        q.create().unwrap().msgs.0 = 2;
477        q.create().unwrap().msgs.0 = 3;
478
479        assert_eq!(q.len(), 3);
480
481        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
482        assert_eq!(res, [3, 2, 1]);
483    }
484
485    #[test]
486    fn create_fresh_slot_starts_initialized_with_zeroed_payload() {
487        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
488
489        let cl = q.create().unwrap();
490        assert_eq!(cl.id, 0);
491        assert_eq!(cl.get_state(), CopperListState::Initialized);
492        assert_eq!(cl.msgs.0, 0);
493        assert_eq!(q.next_cl_id(), 1);
494        assert_eq!(q.last_cl_id(), 0);
495    }
496
497    #[test]
498    fn create_reused_slot_reinitializes_state_but_preserves_payload_storage() {
499        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
500
501        {
502            let cl = q.create().unwrap();
503            cl.msgs.0 = 41;
504            cl.change_state(CopperListState::Processing);
505        }
506
507        let popped = q.pop().unwrap();
508        assert_eq!(popped.id, 0);
509        assert_eq!(popped.get_state(), CopperListState::Processing);
510        assert_eq!(popped.msgs.0, 41);
511
512        let reused = q.create().unwrap();
513        assert_eq!(reused.id, 1);
514        assert_eq!(reused.get_state(), CopperListState::Initialized);
515        assert_eq!(reused.msgs.0, 41);
516        assert_eq!(q.next_cl_id(), 2);
517        assert_eq!(q.last_cl_id(), 1);
518    }
519
520    #[test]
521    fn mutable_iterator() {
522        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
523        q.create().unwrap().msgs.0 = 1;
524        q.create().unwrap().msgs.0 = 2;
525        q.create().unwrap().msgs.0 = 3;
526        q.create().unwrap().msgs.0 = 4;
527        q.create().unwrap().msgs.0 = 5;
528
529        for x in q.iter_mut() {
530            x.msgs.0 *= 2;
531        }
532
533        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
534        assert_eq!(res, [10, 8, 6, 4, 2]);
535    }
536
537    #[test]
538    fn mutable_iterator_non_wrapped_only_visits_active_slots() {
539        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
540        q.create().unwrap().msgs.0 = 1;
541        q.create().unwrap().msgs.0 = 2;
542        q.create().unwrap().msgs.0 = 3;
543
544        let mut visited = Vec::new();
545        for cl in q.iter_mut() {
546            visited.push(cl.id);
547            cl.msgs.0 *= 10;
548        }
549
550        assert_eq!(visited, vec![2, 1, 0]);
551        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
552        assert_eq!(res, [30, 20, 10]);
553    }
554
555    #[test]
556    fn test_drop_last() {
557        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
558        q.create().unwrap().msgs.0 = 1;
559        q.create().unwrap().msgs.0 = 2;
560        q.create().unwrap().msgs.0 = 3;
561        q.create().unwrap().msgs.0 = 4;
562        q.create().unwrap().msgs.0 = 5;
563        assert_eq!(q.len(), 5);
564
565        q.drop_last();
566        assert_eq!(q.len(), 4);
567
568        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
569        assert_eq!(res, [4, 3, 2, 1]);
570    }
571
572    #[test]
573    fn drop_last_on_empty_queue_is_a_noop() {
574        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
575
576        q.drop_last();
577
578        assert!(q.is_empty());
579        assert!(q.peek().is_none());
580        assert!(q.pop().is_none());
581    }
582
583    #[test]
584    fn drop_last_on_non_wrapped_queue_removes_most_recent_slot() {
585        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
586        q.create().unwrap().msgs.0 = 1;
587        q.create().unwrap().msgs.0 = 2;
588        q.create().unwrap().msgs.0 = 3;
589
590        q.drop_last();
591
592        assert_eq!(q.len(), 2);
593        assert_eq!(q.peek().unwrap().msgs.0, 2);
594        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
595        assert_eq!(res, [2, 1]);
596    }
597
598    #[test]
599    fn test_pop() {
600        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
601        q.create().unwrap().msgs.0 = 1;
602        q.create().unwrap().msgs.0 = 2;
603        q.create().unwrap().msgs.0 = 3;
604        q.create().unwrap().msgs.0 = 4;
605        q.create().unwrap().msgs.0 = 5;
606        assert_eq!(q.len(), 5);
607
608        let last = q.pop().unwrap();
609        assert_eq!(last.msgs.0, 5);
610        assert_eq!(q.len(), 4);
611
612        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
613        assert_eq!(res, [4, 3, 2, 1]);
614    }
615
616    #[test]
617    fn pop_on_empty_queue_returns_none() {
618        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
619
620        assert!(q.pop().is_none());
621        assert!(q.is_empty());
622    }
623
624    #[test]
625    fn test_peek() {
626        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
627        q.create().unwrap().msgs.0 = 1;
628        q.create().unwrap().msgs.0 = 2;
629        q.create().unwrap().msgs.0 = 3;
630        q.create().unwrap().msgs.0 = 4;
631        q.create().unwrap().msgs.0 = 5;
632        assert_eq!(q.len(), 5);
633
634        let last = q.peek().unwrap();
635        assert_eq!(last.msgs.0, 5);
636        assert_eq!(q.len(), 5);
637
638        let res: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
639        assert_eq!(res, [5, 4, 3, 2, 1]);
640    }
641
642    #[test]
643    fn peek_on_empty_queue_returns_none() {
644        let q = CuListsManager::<CuStampedDataSet, 5>::new();
645
646        assert!(q.peek().is_none());
647    }
648
649    #[test]
650    fn next_and_last_cl_id_track_assigned_ids() {
651        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
652
653        // Before first allocation, next id is 0 and last id saturates to 0.
654        assert_eq!(q.next_cl_id(), 0);
655        assert_eq!(q.last_cl_id(), 0);
656
657        let cl0 = q.create().unwrap();
658        assert_eq!(cl0.id, 0);
659        assert_eq!(q.next_cl_id(), 1);
660        assert_eq!(q.last_cl_id(), 0);
661
662        let cl1 = q.create().unwrap();
663        assert_eq!(cl1.id, 1);
664        assert_eq!(q.next_cl_id(), 2);
665        assert_eq!(q.last_cl_id(), 1);
666
667        let _ = q.pop().unwrap();
668        let cl2 = q.create().unwrap();
669        assert_eq!(cl2.id, 2);
670        assert_eq!(q.next_cl_id(), 3);
671        assert_eq!(q.last_cl_id(), 2);
672    }
673
674    #[test]
675    fn asc_iter_non_wrapped_returns_oldest_to_newest_without_free_slots() {
676        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
677        q.create().unwrap().msgs.0 = 10;
678        q.create().unwrap().msgs.0 = 20;
679        q.create().unwrap().msgs.0 = 30;
680
681        let res: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
682        assert_eq!(res, [10, 20, 30]);
683    }
684
685    #[test]
686    fn asc_iter_mut_non_wrapped_only_visits_active_slots_in_oldest_first_order() {
687        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
688        q.create().unwrap().msgs.0 = 10;
689        q.create().unwrap().msgs.0 = 20;
690        q.create().unwrap().msgs.0 = 30;
691
692        let mut visited = Vec::new();
693        for (offset, cl) in q.asc_iter_mut().enumerate() {
694            visited.push(cl.id);
695            cl.msgs.0 += offset as i32;
696        }
697
698        assert_eq!(visited, vec![0, 1, 2]);
699        let res: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
700        assert_eq!(res, [10, 21, 32]);
701    }
702
703    #[test]
704    fn asc_iter_wrapped_layout_tracks_reused_slots_in_ascending_order() {
705        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
706        for value in 1..=5 {
707            q.create().unwrap().msgs.0 = value;
708        }
709        assert_eq!(q.pop().unwrap().msgs.0, 5);
710        assert_eq!(q.pop().unwrap().msgs.0, 4);
711        q.create().unwrap().msgs.0 = 6;
712        q.create().unwrap().msgs.0 = 7;
713
714        let desc: Vec<_> = q.iter().map(|x| x.msgs.0).collect();
715        assert_eq!(desc, [7, 6, 3, 2, 1]);
716
717        let asc: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
718        assert_eq!(asc, [1, 2, 3, 6, 7]);
719    }
720
721    #[test]
722    fn asc_iter_mut_wrapped_layout_updates_oldest_to_newest_order() {
723        let mut q = CuListsManager::<CuStampedDataSet, 5>::new();
724        for value in 1..=5 {
725            q.create().unwrap().msgs.0 = value;
726        }
727        let _ = q.pop().unwrap();
728        let _ = q.pop().unwrap();
729        q.create().unwrap().msgs.0 = 6;
730        q.create().unwrap().msgs.0 = 7;
731
732        let mut visited = Vec::new();
733        for (offset, cl) in q.asc_iter_mut().enumerate() {
734            visited.push(cl.id);
735            cl.msgs.0 += offset as i32;
736        }
737
738        assert_eq!(visited, vec![0, 1, 2, 5, 6]);
739        let asc: Vec<_> = q.asc_iter().map(|x| x.msgs.0).collect();
740        assert_eq!(asc, [1, 3, 5, 9, 11]);
741    }
742
743    #[derive(Decode, Encode, Debug, PartialEq, Clone, Copy)]
744    struct TestStruct {
745        content: [u8; 10_000_000],
746    }
747
748    impl Default for TestStruct {
749        fn default() -> Self {
750            TestStruct {
751                content: [0; 10_000_000],
752            }
753        }
754    }
755
756    impl ErasedCuStampedDataSet for TestStruct {
757        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
758            Vec::new()
759        }
760    }
761
762    impl Serialize for TestStruct {
763        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
764        where
765            S: Serializer,
766        {
767            serializer.serialize_i8(0)
768        }
769    }
770
771    impl MatchingTasks for TestStruct {
772        fn get_all_task_ids() -> &'static [&'static str] {
773            &[]
774        }
775    }
776
777    impl CuListZeroedInit for TestStruct {
778        fn init_zeroed(&mut self) {}
779    }
780
781    #[test]
782    fn be_sure_we_wont_stackoverflow_at_init() {
783        let _ = CuListsManager::<TestStruct, 3>::new();
784    }
785}