kernel/collections/
ring_buffer.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of a ring buffer.
6
7use crate::collections::queue;
8
9pub struct RingBuffer<'a, T: 'a> {
10    ring: &'a mut [T],
11    head: usize,
12    tail: usize,
13}
14
15impl<'a, T: Copy> RingBuffer<'a, T> {
16    pub fn new(ring: &'a mut [T]) -> RingBuffer<'a, T> {
17        RingBuffer {
18            head: 0,
19            tail: 0,
20            ring,
21        }
22    }
23
24    /// Returns the number of elements that can be enqueued until the ring buffer is full.
25    pub fn available_len(&self) -> usize {
26        // The maximum capacity of the queue is ring.len - 1, because head == tail for the empty
27        // queue.
28        self.ring.len().saturating_sub(1 + queue::Queue::len(self))
29    }
30
31    /// Returns up to 2 slices that together form the contents of the ring buffer.
32    ///
33    /// Returns:
34    /// - `(None, None)` if the buffer is empty.
35    /// - `(Some(slice), None)` if the head is before the tail (therefore all the contents is
36    /// contiguous).
37    /// - `(Some(left), Some(right))` if the head is after the tail. In that case, the logical
38    /// contents of the buffer is `[left, right].concat()` (although physically the "left" slice is
39    /// stored after the "right" slice).
40    pub fn as_slices(&'a self) -> (Option<&'a [T]>, Option<&'a [T]>) {
41        if self.head < self.tail {
42            (Some(&self.ring[self.head..self.tail]), None)
43        } else if self.head > self.tail {
44            let (left, right) = self.ring.split_at(self.head);
45            (
46                Some(right),
47                if self.tail == 0 {
48                    None
49                } else {
50                    Some(&left[..self.tail])
51                },
52            )
53        } else {
54            (None, None)
55        }
56    }
57}
58
59impl<T: Copy> queue::Queue<T> for RingBuffer<'_, T> {
60    fn has_elements(&self) -> bool {
61        self.head != self.tail
62    }
63
64    fn is_full(&self) -> bool {
65        self.head == ((self.tail + 1) % self.ring.len())
66    }
67
68    fn len(&self) -> usize {
69        if self.tail > self.head {
70            self.tail - self.head
71        } else if self.tail < self.head {
72            (self.ring.len() - self.head) + self.tail
73        } else {
74            // head equals tail, length is zero
75            0
76        }
77    }
78
79    fn enqueue(&mut self, val: T) -> bool {
80        if self.is_full() {
81            // Incrementing tail will overwrite head
82            false
83        } else {
84            self.ring[self.tail] = val;
85            self.tail = (self.tail + 1) % self.ring.len();
86            true
87        }
88    }
89
90    fn push(&mut self, val: T) -> Option<T> {
91        let result = if self.is_full() {
92            let val = self.ring[self.head];
93            self.head = (self.head + 1) % self.ring.len();
94            Some(val)
95        } else {
96            None
97        };
98
99        self.ring[self.tail] = val;
100        self.tail = (self.tail + 1) % self.ring.len();
101        result
102    }
103
104    fn dequeue(&mut self) -> Option<T> {
105        if self.has_elements() {
106            let val = self.ring[self.head];
107            self.head = (self.head + 1) % self.ring.len();
108            Some(val)
109        } else {
110            None
111        }
112    }
113
114    /// Removes the first element for which the provided closure returns `true`.
115    ///
116    /// This walks the ring buffer and, upon finding a matching element, removes
117    /// it. It then shifts all subsequent elements forward (filling the hole
118    /// created by removing the element).
119    ///
120    /// If an element was removed, this function returns it as `Some(elem)`.
121    fn remove_first_matching<F>(&mut self, f: F) -> Option<T>
122    where
123        F: Fn(&T) -> bool,
124    {
125        let len = self.ring.len();
126        let mut slot = self.head;
127        while slot != self.tail {
128            if f(&self.ring[slot]) {
129                // This is the desired element, remove it and return it
130                let val = self.ring[slot];
131
132                let mut next_slot = (slot + 1) % len;
133                // Move everything past this element forward in the ring
134                while next_slot != self.tail {
135                    self.ring[slot] = self.ring[next_slot];
136                    slot = next_slot;
137                    next_slot = (next_slot + 1) % len;
138                }
139                self.tail = slot;
140                return Some(val);
141            }
142            slot = (slot + 1) % len;
143        }
144        None
145    }
146
147    fn empty(&mut self) {
148        self.head = 0;
149        self.tail = 0;
150    }
151
152    fn retain<F>(&mut self, mut f: F)
153    where
154        F: FnMut(&T) -> bool,
155    {
156        let len = self.ring.len();
157        // Index over the elements before the retain operation.
158        let mut src = self.head;
159        // Index over the retained elements.
160        let mut dst = self.head;
161
162        while src != self.tail {
163            if f(&self.ring[src]) {
164                // When the predicate is true, move the current element to the
165                // destination if needed, and increment the destination index.
166                if src != dst {
167                    self.ring[dst] = self.ring[src];
168                }
169                dst = (dst + 1) % len;
170            }
171            src = (src + 1) % len;
172        }
173
174        self.tail = dst;
175    }
176}
177
178#[cfg(test)]
179mod test {
180    use super::super::queue::Queue;
181    use super::RingBuffer;
182
183    #[test]
184    fn test_enqueue_dequeue() {
185        const LEN: usize = 10;
186        let mut ring = [0; LEN];
187        let mut buf = RingBuffer::new(&mut ring);
188
189        for _ in 0..2 * LEN {
190            assert!(buf.enqueue(42));
191            assert_eq!(buf.len(), 1);
192            assert!(buf.has_elements());
193
194            assert_eq!(buf.dequeue(), Some(42));
195            assert_eq!(buf.len(), 0);
196            assert!(!buf.has_elements());
197        }
198    }
199
200    #[test]
201    fn test_push() {
202        const LEN: usize = 10;
203        const MAX: usize = 100;
204        let mut ring = [0; LEN + 1];
205        let mut buf = RingBuffer::new(&mut ring);
206
207        for i in 0..LEN {
208            assert_eq!(buf.len(), i);
209            assert!(!buf.is_full());
210            assert_eq!(buf.push(i), None);
211            assert!(buf.has_elements());
212        }
213
214        for i in LEN..MAX {
215            assert!(buf.is_full());
216            assert_eq!(buf.push(i), Some(i - LEN));
217        }
218
219        for i in 0..LEN {
220            assert!(buf.has_elements());
221            assert_eq!(buf.len(), LEN - i);
222            assert_eq!(buf.dequeue(), Some(MAX - LEN + i));
223            assert!(!buf.is_full());
224        }
225
226        assert!(!buf.has_elements());
227    }
228
229    // Enqueue integers 1 <= n < len, checking that it succeeds and that the
230    // queue is full at the end.
231    // See std::iota in C++.
232    fn enqueue_iota(buf: &mut RingBuffer<usize>, len: usize) {
233        for i in 1..len {
234            assert!(!buf.is_full());
235            assert!(buf.enqueue(i));
236            assert!(buf.has_elements());
237            assert_eq!(buf.len(), i);
238        }
239
240        assert!(buf.is_full());
241        assert!(!buf.enqueue(0));
242        assert!(buf.has_elements());
243    }
244
245    // Dequeue all elements, expecting integers 1 <= n < len, checking that the
246    // queue is empty at the end.
247    // See std::iota in C++.
248    fn dequeue_iota(buf: &mut RingBuffer<usize>, len: usize) {
249        for i in 1..len {
250            assert!(buf.has_elements());
251            assert_eq!(buf.len(), len - i);
252            assert_eq!(buf.dequeue(), Some(i));
253            assert!(!buf.is_full());
254        }
255
256        assert!(!buf.has_elements());
257        assert_eq!(buf.len(), 0);
258    }
259
260    // Move the head by `count` elements, by enqueueing/dequeueing `count`
261    // times an element.
262    // This assumes an empty queue at the beginning, and yields an empty queue.
263    fn move_head(buf: &mut RingBuffer<usize>, count: usize) {
264        assert!(!buf.has_elements());
265        assert_eq!(buf.len(), 0);
266
267        for _ in 0..count {
268            assert!(buf.enqueue(0));
269            assert_eq!(buf.dequeue(), Some(0));
270        }
271
272        assert!(!buf.has_elements());
273        assert_eq!(buf.len(), 0);
274    }
275
276    #[test]
277    fn test_fill_once() {
278        const LEN: usize = 10;
279        let mut ring = [0; LEN];
280        let mut buf = RingBuffer::new(&mut ring);
281
282        assert!(!buf.has_elements());
283        assert_eq!(buf.len(), 0);
284
285        enqueue_iota(&mut buf, LEN);
286        dequeue_iota(&mut buf, LEN);
287    }
288
289    #[test]
290    fn test_refill() {
291        const LEN: usize = 10;
292        let mut ring = [0; LEN];
293        let mut buf = RingBuffer::new(&mut ring);
294
295        for _ in 0..10 {
296            enqueue_iota(&mut buf, LEN);
297            dequeue_iota(&mut buf, LEN);
298        }
299    }
300
301    #[test]
302    fn test_retain() {
303        const LEN: usize = 10;
304        let mut ring = [0; LEN];
305        let mut buf = RingBuffer::new(&mut ring);
306
307        move_head(&mut buf, LEN - 2);
308        enqueue_iota(&mut buf, LEN);
309
310        buf.retain(|x| x % 2 == 1);
311        assert_eq!(buf.len(), LEN / 2);
312
313        assert_eq!(buf.dequeue(), Some(1));
314        assert_eq!(buf.dequeue(), Some(3));
315        assert_eq!(buf.dequeue(), Some(5));
316        assert_eq!(buf.dequeue(), Some(7));
317        assert_eq!(buf.dequeue(), Some(9));
318        assert_eq!(buf.dequeue(), None);
319    }
320}