limited_queue/
lib.rs

1//! # LimitedQueue
2//!
3//! `LimitedQueue<T>` is a limited queue that
4//! overrides the oldest data if trying to
5//! push a data when the queue is full.
6//!
7//! All operations are of `O(1)` complexity,
8//! except the constructor with `O(Vec::with_capacity)`.
9
10use std::mem::{replace, take};
11
12/// A circular queue that overrides the oldest data
13/// if trying to push data when queue is full.
14///
15/// All operations are of `O(1)` complexity,
16/// except the constructor with `O(Vec::with_capacity)`.
17///
18/// # Example
19///
20/// ```
21/// let mut q = limited_queue::LimitedQueue::with_capacity(5);
22/// // push_ret: [None x 5, 0, 1, ..., 4]
23/// let push_ret = [[None; 5], core::array::from_fn(|i| Some(i))].concat();
24/// for (i, pr) in (0..10).zip(push_ret) {
25///     assert_eq!(q.push(i), pr);
26/// }
27/// for (n, element) in q.iter().enumerate() {
28///     assert_eq!(element.clone(), q[n]); // 5, 6, 7, 8, 9
29///     assert_eq!(element.clone(), n + 5); // 5, 6, 7, 8, 9
30/// }
31/// ```
32///
33/// # Error
34///
35/// For indexing, no bound check will occur, so please check
36/// the size of the queue with `len` method before subscription.
37///
38/// If you need boundary check, please use `get` method.
39#[derive(Debug)]
40pub struct LimitedQueue<T> {
41    q: Vec<T>,
42    front: usize,
43    rear: usize,
44    sz: usize,
45}
46
47impl<T> LimitedQueue<T> {
48    /// Vec-like constructor
49    ///
50    /// ```
51    /// use limited_queue::LimitedQueue;
52    ///
53    /// let mut q = LimitedQueue::with_capacity(2);
54    ///
55    /// assert_eq!(q.push(1), None);
56    /// assert_eq!(q.push(2), None);
57    ///
58    /// // first element popped since the capacity is 2
59    /// assert_eq!(q.push(3), Some(1));
60    ///
61    /// assert_eq!(q.peek(), Some(&2));
62    /// assert_eq!(q.pop(), Some(2));
63    /// assert_eq!(q.peek(), Some(&3));
64    /// assert_eq!(q.pop(), Some(3));
65    /// ```
66    ///
67    /// @param `cap` Capacity (limit size) of the queue
68    #[inline]
69    pub fn with_capacity(cap: usize) -> LimitedQueue<T> {
70        LimitedQueue {
71            q: Vec::with_capacity(cap),
72            front: 0usize,
73            rear: 0usize,
74            sz: 0usize,
75        }
76    }
77
78    /// Get the element at position `idx`,
79    /// a.k.a. the position from the start of queue
80    ///
81    /// ```
82    /// use limited_queue::LimitedQueue;
83    ///
84    /// let mut q = LimitedQueue::with_capacity(2);
85    /// q.push(1);
86    /// assert_eq!(q.get(100), None);
87    /// ```
88    #[inline]
89    pub fn get(&self, idx: usize) -> Option<&T> {
90        if idx >= self.front {
91            None
92        } else {
93            Some(&self[idx])
94        }
95    }
96
97    /// Peek the oldest element at the front of the queue
98    ///
99    /// ```
100    /// use limited_queue::LimitedQueue;
101    ///
102    /// let mut q = LimitedQueue::with_capacity(1);
103    ///
104    /// q.push(1234);
105    /// assert_eq!(q.peek(), Some(&1234));
106    /// assert_eq!(q.pop(), Some(1234));
107    /// assert_eq!(q.peek(), None);
108    /// ```
109    #[inline]
110    pub fn peek(&self) -> Option<&T> {
111        if self.is_empty() {
112            None
113        } else {
114            Some(&self.q[self.front])
115        }
116    }
117
118    /// Push a new element into queue,
119    /// removing the oldest element if the queue is full
120    #[inline]
121    pub fn push(&mut self, ele: T) -> Option<T> {
122        let mut popped = None;
123        if self.is_full() {
124            // popped = self.pop();
125            // use `replace` so no implicit `Default` will be called
126            popped = Some(replace(&mut self.q[self.rear], ele));
127            // and move forth the front idx to simulate `pop` operation
128            self.front = self.next_idx(self.front);
129        } else {
130            if self.q.len() == self.rear && self.q.len() < self.q.capacity() {
131                // if the vector is shorter than capacity
132                self.q.push(ele);
133            } else if self.q.len() > self.rear {
134                self.q[self.rear] = ele;
135            } else {
136                panic!("[limited-queue::push] Error, bad push position");
137            }
138            self.sz += 1;
139        }
140        self.rear = self.next_idx(self.rear);
141        popped
142    }
143
144    /// Inner method: next index of the inner vector
145    #[inline]
146    fn next_idx(&self, idx: usize) -> usize {
147        (idx + 1) % self.q.capacity()
148    }
149
150    #[inline]
151    pub fn is_empty(&self) -> bool {
152        self.sz == 0
153    }
154
155    #[inline]
156    pub fn is_full(&self) -> bool {
157        self.sz == self.q.capacity()
158    }
159
160    /// Get queue length
161    ///
162    /// ```
163    /// use limited_queue::LimitedQueue;
164    ///
165    /// let mut q = LimitedQueue::with_capacity(3);
166    ///
167    /// q.push(1234);
168    /// assert_eq!(q.len(), 1);
169    /// q.push(1234);
170    /// assert_eq!(q.len(), 2);
171    /// q.push(1234);
172    /// assert_eq!(q.len(), 3);
173    /// q.push(1234);
174    /// assert_eq!(q.len(), 3);
175    /// ```
176    #[inline]
177    pub fn len(&self) -> usize {
178        self.sz
179    }
180
181    /// To traverse all the elements in
182    /// `LimitedQueue`, for example:
183    ///
184    /// ```
185    /// use limited_queue::LimitedQueue;
186    ///
187    /// let mut q = LimitedQueue::with_capacity(5);
188    /// for i in 0..10 {
189    ///     q.push(i);
190    /// }
191    /// for (&n, element) in q.iter().zip(5usize..=9) {
192    ///     // will be 5, 6, 7, 8, 9 since 0 ~ 4
193    ///     // are popped because the queue is full
194    ///     assert_eq!(element.clone(), n);
195    /// }
196    /// ```
197    #[inline]
198    pub fn iter(&self) -> LimitedQueueIterator<T> {
199        LimitedQueueIterator { lq: self, idx: 0 }
200    }
201
202    // #[inline]
203    // pub fn iter_mut(&self) -> LimitedQueueIterator<T> {
204    //     LimitedQueueIterator {
205    //         lq: self,
206    //         idx: self.front,
207    //     }
208    // }
209
210    /// `O(1)` method to (lazily) clear all the elements
211    ///
212    /// ```
213    /// use limited_queue::LimitedQueue;
214    ///
215    /// let mut q = LimitedQueue::with_capacity(5);
216    /// for i in 0..10 {
217    ///     q.push(i);
218    /// }
219    /// q.clear();
220    /// assert_eq!(q.peek(), None);
221    /// assert_eq!(q.is_empty(), true);
222    /// ```
223    #[inline]
224    pub fn clear(&mut self) {
225        self.front = 0;
226        self.rear = 0;
227        self.sz = 0;
228    }
229
230    /// private method of boundary check and indexing
231    #[inline]
232    fn indexing(&self, idx: usize) -> usize {
233        if idx >= self.sz {
234            panic!("Invalid subscription index: {}", idx)
235        }
236        (idx + self.front) % self.q.capacity()
237    }
238}
239
240/// Optionally provide `pop` method for
241/// types that implements `Default` trait
242impl<T: Default> LimitedQueue<T> {
243    /// Pop the first element from queue,
244    /// will replace the element in queue with
245    /// the `Default` value of the element
246    ///
247    /// ```
248    /// use limited_queue::LimitedQueue;
249    ///
250    /// let mut q = LimitedQueue::with_capacity(1);
251    ///
252    /// q.push(1234);
253    /// assert_eq!(q.pop(), Some(1234));
254    /// assert_eq!(q.pop(), None);
255    /// ```
256    #[inline]
257    pub fn pop(&mut self) -> Option<T> {
258        if self.is_empty() {
259            None
260        } else {
261            let ret = take(&mut self.q[self.front]);
262            self.front = self.next_idx(self.front);
263            self.sz -= 1;
264            Some(ret)
265        }
266    }
267}
268
269impl<T> std::ops::Index<usize> for LimitedQueue<T> {
270    type Output = T;
271
272    #[inline]
273    fn index(&self, idx: usize) -> &Self::Output {
274        let real_idx = self.indexing(idx);
275        &self.q[real_idx]
276    }
277}
278
279impl<T> std::ops::IndexMut<usize> for LimitedQueue<T> {
280    #[inline]
281    fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
282        let real_idx = self.indexing(idx);
283        &mut self.q[real_idx]
284    }
285}
286
287pub struct LimitedQueueIterator<'a, T> {
288    lq: &'a LimitedQueue<T>,
289    idx: usize,
290}
291
292impl<'a, T> Iterator for LimitedQueueIterator<'a, T> {
293    type Item = &'a T;
294
295    fn next(&mut self) -> Option<Self::Item> {
296        let cur_idx = self.idx;
297        if cur_idx == self.lq.len() {
298            None
299        } else {
300            self.idx += 1;
301            Some(&self.lq[cur_idx])
302        }
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use std::panic;
309
310    use rand::Rng;
311
312    use crate::LimitedQueue;
313
314    #[test]
315    fn test_iter() {
316        let mut q = crate::LimitedQueue::with_capacity(5);
317        // push_ret: [None x 5, 0, 1, ..., 4]
318        let push_ret = [[None; 5], core::array::from_fn(|i| Some(i))].concat();
319        for (i, pr) in (0..10).zip(push_ret) {
320            assert_eq!(q.push(i), pr);
321        }
322        assert_eq!(q.len(), 5);
323        for (n, element) in q.iter().enumerate() {
324            assert_eq!(element.clone(), q[n]); // 5, 6, 7, 8, 9
325            assert_eq!(element.clone(), n + 5); // 5, 6, 7, 8, 9
326        }
327    }
328
329    #[test]
330    fn test_change_size() {
331        const MAX_SZ: usize = 25;
332        let mut q: LimitedQueue<i32> = crate::LimitedQueue::with_capacity(MAX_SZ);
333        let mut sz = 0;
334        let mut rng = rand::thread_rng();
335
336        for _ in 0..1000 {
337            let op = rng.gen_range(0..=2);
338            match op {
339                0 => {
340                    if q.push(rng.gen()).is_none() {
341                        sz += 1
342                    };
343                }
344                1 => {
345                    if q.pop().is_some() {
346                        sz -= 1
347                    };
348                }
349                _ => {
350                    assert!(match sz {
351                        0 => q.is_empty() && q.pop().is_none() && q.peek().is_none(),
352                        MAX_SZ => q.is_full(),
353                        _ => sz == q.len() && sz < MAX_SZ,
354                    });
355                }
356            };
357        }
358    }
359
360    #[test]
361    #[should_panic]
362    fn test_zero_len_invalid_indexing() {
363        LimitedQueue::with_capacity(0)[0]
364    }
365
366    #[test]
367    fn test_invalid_indexing() {
368        // shadow out panic message for unwind in the loop
369        let old_hook = panic::take_hook();
370        panic::set_hook(Box::new(|_info| {}));
371
372        let mut q = LimitedQueue::with_capacity(5);
373        q.push(1);
374        for i in 5..100 {
375            let invalid_access = || q[i];
376            let should_be_false = panic::catch_unwind(invalid_access).is_err();
377            if !should_be_false {
378                // reset panic hook to show error message
379                panic::set_hook(old_hook);
380                // panic with reason
381                panic!("Indexing with idx: {} cannot trigger panic.", i);
382            }
383        }
384
385        panic::set_hook(old_hook);
386    }
387
388    #[test]
389    fn test_clear() {
390        let mut q = LimitedQueue::with_capacity(10);
391        for _ in 0..3 {
392            q.push(1);
393        }
394        assert_eq!(q.len(), 3);
395        assert_eq!(q.pop(), Some(1));
396        assert_eq!(q.len(), 2);
397        q.clear();
398        assert_eq!(q.len(), 0);
399        for _ in 0..3 {
400            q.push(1);
401        }
402        assert_eq!(q.len(), 3);
403        assert_eq!(q.pop(), Some(1));
404        assert_eq!(q.len(), 2);
405        q.clear();
406        assert_eq!(q.len(), 0);
407    }
408}