Skip to main content

bytearray_ringbuffer/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![doc = include_str!("../README.md")]
3
4pub struct BytearrayRingbuffer<const N: usize> {
5    buffer: [u8; N],
6    /// points to where the next packet will be written
7    head: usize,
8    /// points to where the oldest packet starts
9    tail: usize,
10    /// resolves ambiguity between head == tail for empty and full
11    empty: bool,
12}
13
14#[derive(Copy, Clone, Debug)]
15pub struct NotEnoughSpaceError;
16
17impl<const N: usize> BytearrayRingbuffer<N> {
18    /// create empty `BytearrayRingbuffer`
19    pub const fn new() -> Self {
20        assert!(N > 8);
21        assert!(N < (u32::MAX as usize));
22        Self {
23            buffer: [0; N],
24            head: 0,
25            tail: 0,
26            empty: true,
27        }
28    }
29
30    /// number of bytes available for payload, 8 bytes for header + end are already subtracted
31    pub const fn free(&self) -> usize {
32        self.bytes_unused().saturating_sub(8)
33    }
34
35    /// add element, returns `NotEnoughSpaceError` if there is not enough space available
36    pub fn push(&mut self, data: &[u8]) -> Result<(), NotEnoughSpaceError> {
37        self._push(data, false)
38    }
39
40    /// add element, discards oldest elements until `data` fits. Only when data is too large to fit the buffer (ie `data.len() > N - 8`), returns `NotEnoughSpaceError`
41    pub fn push_force(&mut self, data: &[u8]) -> Result<(), NotEnoughSpaceError> {
42        self._push(data, true)
43    }
44
45    /// number of bytes are currently not in use
46    const fn bytes_unused(&self) -> usize {
47        if self.empty {
48            N
49        } else if self.head > self.tail {
50            N + self.tail - self.head
51        } else {
52            self.tail - self.head
53        }
54    }
55
56    fn _push(&mut self, data: &[u8], force: bool) -> Result<(), NotEnoughSpaceError> {
57        assert!(data.len() <= u32::MAX as usize);
58
59        // data is longer than entire buffer
60        if data.len() > N - 8 {
61            return Err(NotEnoughSpaceError);
62        }
63
64        // need to overwrite old data to fit new data
65        if (data.len() + 8) > self.bytes_unused() {
66            if !force {
67                return Err(NotEnoughSpaceError);
68            }
69            while (data.len() + 8) > self.bytes_unused() {
70                self.pop_front();
71            }
72        }
73
74        // write length + data + length
75        let addr_a = self.head;
76        let addr_b = add_wrapping::<N>(self.head, 4);
77        let addr_c = add_wrapping::<N>(self.head, 4 + data.len());
78        let len_buffer: [u8; 4] = (data.len() as u32).to_ne_bytes();
79        write_wrapping(&mut self.buffer, addr_a, &len_buffer);
80        write_wrapping(&mut self.buffer, addr_b, data);
81        write_wrapping(&mut self.buffer, addr_c, &len_buffer);
82
83        self.head = add_wrapping::<N>(self.head, 8 + data.len());
84        self.empty = false;
85
86        Ok(())
87    }
88
89    /// retrieve the oldest element, removing it from the buffer
90    pub fn pop_front(&mut self) -> Option<(&[u8], &[u8])> {
91        if self.empty {
92            return None;
93        }
94        let mut len_buffer = [0; 4];
95        read_wrapping(&self.buffer, self.tail, &mut len_buffer);
96        let len = u32::from_ne_bytes(len_buffer) as usize;
97
98        let index_data = add_wrapping::<N>(self.tail, 4);
99        let len_a = (N - index_data).min(len);
100        let a = &self.buffer[index_data..index_data + len_a];
101        let b = if len_a == len {
102            &[]
103        } else {
104            &self.buffer[..len - len_a]
105        };
106
107        self.tail = add_wrapping::<N>(self.tail, len + 8);
108        self.empty = self.head == self.tail;
109        Some((a, b))
110    }
111
112    /// iterate over all items, starting with the newest one
113    pub fn iter_backwards<'a>(&'a self) -> IterBackwards<'a, N> {
114        IterBackwards {
115            buffer: &self.buffer,
116            head: self.head,
117            tail: self.tail,
118            empty: self.empty,
119        }
120    }
121
122    /// iterate over all items, starting with the oldest one
123    pub fn iter<'a>(&'a self) -> Iter<'a, N> {
124        Iter {
125            buffer: &self.buffer,
126            head: self.head,
127            tail: self.tail,
128            empty: self.empty,
129        }
130    }
131
132    /// return the number of entries
133    pub fn count(&self) -> usize {
134        self.iter().count()
135    }
136
137    /// obtain the n-th entry
138    pub fn nth(&self, n: usize) -> Option<(&[u8], &[u8])> {
139        self.iter().nth(n)
140    }
141
142    /// obtain the n-th entry, rolling the buffer around in case the element wraps around at the end of the buffer
143    pub fn nth_contiguous(&mut self, mut n: usize) -> Option<&[u8]> {
144        if self.empty {
145            return None;
146        }
147
148        // iterate through buffer until we find this one
149        let mut tail = self.tail;
150        let len_data = loop {
151            let mut buf = [0u8; 4];
152            read_wrapping(&self.buffer, tail, &mut buf);
153            let len_data = u32::from_ne_bytes(buf) as usize;
154
155            if n == 0 {
156                break len_data;
157            }
158            n -= 1;
159
160            tail = add_wrapping::<N>(tail, len_data + 8);
161        };
162
163        let index_data = add_wrapping::<N>(tail, 4);
164
165        // happy path, no rotate necessary
166        if index_data + len_data <= N {
167            return Some(&self.buffer[index_data..index_data + len_data]);
168        }
169
170        // otherwise rotate
171        self.buffer.rotate_left(index_data);
172        self.tail = sub_wrapping::<N>(self.tail, index_data);
173        self.head = sub_wrapping::<N>(self.head, index_data);
174
175        Some(&self.buffer[..len_data])
176    }
177}
178
179pub struct IterBackwards<'a, const N: usize> {
180    buffer: &'a [u8; N],
181    head: usize,
182    tail: usize,
183    empty: bool,
184}
185
186impl<'a, const N: usize> Iterator for IterBackwards<'a, N> {
187    type Item = (&'a [u8], &'a [u8]);
188
189    fn next(&mut self) -> Option<Self::Item> {
190        if self.empty {
191            return None;
192        }
193
194        // read length of newest packet
195        let index_len = sub_wrapping::<N>(self.head, 4);
196        let mut buf = [0u8; 4];
197        read_wrapping(self.buffer, index_len, &mut buf);
198        let len_data = u32::from_ne_bytes(buf) as usize;
199        debug_assert!((len_data + 8) <= N);
200
201        #[cfg(test)]
202        {
203            let index_len = sub_wrapping::<N>(self.head, 8 + len_data);
204            let mut buf = [0u8; 4];
205            read_wrapping(self.buffer, index_len, &mut buf);
206            let len_2 = u32::from_ne_bytes(buf) as usize;
207            assert_eq!(len_data, len_2);
208        }
209
210        // read out data
211        let index_data = sub_wrapping::<N>(self.head, 4 + len_data);
212        let first = (N - index_data).min(len_data);
213        let slice_a = &self.buffer[index_data..index_data + first];
214        let slice_b = if first < len_data {
215            &self.buffer[..len_data - first]
216        } else {
217            &[]
218        };
219
220        self.head = sub_wrapping::<N>(self.head, 8 + len_data);
221        self.empty = self.head == self.tail;
222
223        Some((slice_a, slice_b))
224    }
225}
226
227impl<const N: usize> Default for BytearrayRingbuffer<N> {
228    fn default() -> Self {
229        Self::new()
230    }
231}
232
233pub struct Iter<'a, const N: usize> {
234    buffer: &'a [u8; N],
235    head: usize,
236    tail: usize,
237    empty: bool,
238}
239
240impl<'a, const N: usize> Iterator for Iter<'a, N> {
241    type Item = (&'a [u8], &'a [u8]);
242
243    fn next(&mut self) -> Option<Self::Item> {
244        if self.empty {
245            return None;
246        }
247
248        // check how many bytes are valid
249        let bytes_used = if self.head > self.tail {
250            N + self.tail - self.head
251        } else {
252            self.tail - self.head
253        };
254        let bytes_valid = N - bytes_used;
255        debug_assert!(bytes_valid >= 8);
256
257        // read length of newest packet
258        let mut buf = [0u8; 4];
259        read_wrapping(self.buffer, self.tail, &mut buf);
260        let len_data = u32::from_ne_bytes(buf) as usize;
261        debug_assert!((len_data + 8) <= N);
262        debug_assert!((len_data + 8) <= bytes_valid);
263
264        // read out data
265        let index_data = add_wrapping::<N>(self.tail, 4);
266        let first = (N - index_data).min(len_data);
267        let slice_a = &self.buffer[index_data..index_data + first];
268        let slice_b = if first < len_data {
269            &self.buffer[..len_data - first]
270        } else {
271            &[]
272        };
273
274        self.tail = add_wrapping::<N>(self.tail, 8 + len_data);
275        self.empty = self.head == self.tail;
276
277        Some((slice_a, slice_b))
278    }
279}
280
281fn add_wrapping<const N: usize>(addr: usize, offset: usize) -> usize {
282    debug_assert!(addr < N);
283    debug_assert!(offset <= N);
284    let s = addr + offset;
285    if s < N { s } else { s - N }
286}
287
288fn sub_wrapping<const N: usize>(addr: usize, offset: usize) -> usize {
289    debug_assert!(addr < N);
290    debug_assert!(offset <= N);
291    if addr >= offset {
292        addr - offset
293    } else {
294        N + addr - offset
295    }
296}
297
298/// write data to buffer, starting at index and wrapping around at the end of the buffer
299fn write_wrapping(buffer: &mut [u8], index: usize, data: &[u8]) {
300    let first = (buffer.len() - index).min(data.len());
301    buffer[index..index + first].copy_from_slice(&data[..first]);
302    if first < data.len() {
303        buffer[..data.len() - first].copy_from_slice(&data[first..]);
304    }
305}
306
307fn read_wrapping(buffer: &[u8], index: usize, data: &mut [u8]) {
308    let first = (buffer.len() - index).min(data.len());
309    data[..first].copy_from_slice(&buffer[index..index + first]);
310    if first < data.len() {
311        let remaining = data.len() - first;
312        data[first..].copy_from_slice(&buffer[..remaining]);
313    }
314}
315
316#[cfg(test)]
317mod tests {
318    use std::collections::VecDeque;
319
320    use super::BytearrayRingbuffer;
321
322    #[test]
323    fn push_some_packets() {
324        const N: usize = 64;
325        for start_offset in 0..N {
326            let mut buf = BytearrayRingbuffer::<N>::new();
327            buf.head = start_offset;
328            buf.tail = start_offset;
329
330            let free = 64 - 8;
331            assert_eq!(buf.free(), free);
332
333            buf.push(b"01234567").unwrap();
334            let free = free - 8 - 8;
335            assert_eq!(buf.free(), free);
336
337            buf.push(b"").unwrap();
338            let free = free - 8;
339            assert_eq!(buf.free(), free);
340
341            buf.push(b"0123").unwrap();
342            let free = free - 4 - 8;
343            assert_eq!(buf.free(), free);
344
345            buf.push(b"0123").unwrap();
346            let free = free - 4 - 8;
347            assert_eq!(buf.free(), free);
348        }
349    }
350
351    #[test]
352    fn push_force() {
353        let mut buf = BytearrayRingbuffer::<16>::new();
354        assert_eq!(buf.bytes_unused(), 16);
355
356        let a = b"012345";
357        let b = b"0123";
358
359        buf.push(a).unwrap();
360        assert_eq!(buf.bytes_unused(), 16 - a.len() - 8);
361
362        buf.push(b).unwrap_err();
363        assert_eq!(buf.bytes_unused(), 16 - a.len() - 8);
364
365        buf.push_force(b).unwrap();
366        assert_eq!(buf.bytes_unused(), 16 - b.len() - 8);
367    }
368
369    #[test]
370    fn push_all_data_lengths() {
371        for n in 0..(32 - 8) {
372            let mut buf = BytearrayRingbuffer::<32>::new();
373            // push n bytes
374            let data = (0..n as u8).collect::<Vec<u8>>();
375
376            assert_eq!(buf.free(), 32 - 8);
377            buf.push(&data).unwrap();
378            assert_eq!(buf.free(), (32usize - 16).saturating_sub(n));
379        }
380    }
381
382    #[test]
383    fn push_sum_of_lengths_possible() {
384        let mut buf = BytearrayRingbuffer::<32>::new();
385        // push 2 x 8 bytes
386        assert_eq!(buf.free(), 32 - 8);
387        buf.push(b"01234567").unwrap();
388        assert_eq!(buf.free(), 32 - 8 - 16);
389        buf.push(b"01234567").unwrap();
390        assert_eq!(buf.free(), 0);
391    }
392
393    #[test]
394    fn push_pop() {
395        const N: usize = 64;
396        for start_offset in 0..N {
397            eprintln!("--------------");
398            let mut buf = BytearrayRingbuffer::<N>::new();
399            buf.head = start_offset;
400            buf.tail = start_offset;
401
402            let data = b"01234567";
403            buf.push(data).unwrap();
404
405            let (a, b) = buf.pop_front().unwrap();
406            let mut out = Vec::new();
407            out.extend_from_slice(a);
408            out.extend_from_slice(b);
409
410            dbg!(out.as_slice());
411            assert!(data == out.as_slice());
412
413            assert_eq!(buf.head, buf.tail);
414            assert_eq!(buf.bytes_unused(), N);
415        }
416    }
417
418    #[test]
419    fn push_read_back() {
420        let data = [b"hello world" as &[u8], b"", b"test"];
421
422        const N: usize = 64;
423        for start_offset in 0..N {
424            let mut buf = BytearrayRingbuffer::<N>::new();
425            buf.head = start_offset;
426            buf.tail = start_offset;
427
428            for &d in &data {
429                buf.push(d).unwrap();
430            }
431
432            // test forward iteration
433            let mut it = buf.iter();
434            for &d in data.iter() {
435                let (a, b) = it.next().unwrap();
436                let mut ab = Vec::new();
437                ab.extend_from_slice(a);
438                ab.extend_from_slice(b);
439                let ab = ab.as_slice();
440                assert_eq!(d, ab);
441            }
442            assert_eq!(it.next(), None);
443
444            // test backward iteration
445            let mut it = buf.iter_backwards();
446            for &d in data.iter().rev() {
447                let (a, b) = it.next().unwrap();
448                let mut ab = Vec::new();
449                ab.extend_from_slice(a);
450                ab.extend_from_slice(b);
451                let ab = ab.as_slice();
452                assert_eq!(d, ab);
453            }
454            assert_eq!(it.next(), None);
455        }
456    }
457
458    #[test]
459    fn push_count() {
460        let mut buf = BytearrayRingbuffer::<64>::new();
461        buf.push(b"1234").unwrap();
462        assert_eq!(buf.count(), 1);
463        buf.push(b"1234").unwrap();
464        assert_eq!(buf.count(), 2);
465        buf.push(b"1234").unwrap();
466        assert_eq!(buf.count(), 3);
467    }
468
469    fn test_with_readback<const N: usize>(words: &[&'static str]) {
470        eprintln!("--------------------------");
471        let mut buf = BytearrayRingbuffer::<N>::new();
472        let mut current_words = VecDeque::new();
473        for &word in words {
474            eprintln!("adding {word:?}");
475            let word = word.to_owned();
476            let current_bytes: usize = current_words.iter().map(|w: &String| w.len() + 8).sum();
477            if current_bytes + 8 + word.len() > N {
478                current_words.pop_front();
479            }
480
481            buf.push_force(word.as_bytes()).unwrap();
482            current_words.push_back(word);
483
484            for (a, b) in buf.iter_backwards().zip(current_words.iter().rev()) {
485                eprintln!("read back {b:?}");
486                let mut st = String::new();
487                st.push_str(core::str::from_utf8(a.0).unwrap());
488                st.push_str(core::str::from_utf8(a.1).unwrap());
489                assert_eq!(st, *b);
490            }
491        }
492    }
493
494    #[test]
495    fn readback_various() {
496        test_with_readback::<32>(&["ab", "123", "hello", "world"]);
497        test_with_readback::<32>(&["", "", "a", "", "", ""]);
498        test_with_readback::<32>(&["", "", "ab", "", "", ""]);
499        test_with_readback::<32>(&["", "", "abc", "", "", ""]);
500        test_with_readback::<32>(&["", "", "abcd", "", "", ""]);
501        test_with_readback::<32>(&["", "", "abcde", "", "", ""]);
502
503        test_with_readback::<24>(&["0", "1", "a", "2", "3", "4"]);
504        test_with_readback::<24>(&["0", "1", "ab", "2", "3", "4"]);
505        test_with_readback::<24>(&["0", "1", "abc", "2", "3", "4"]);
506        test_with_readback::<24>(&["0", "1", "abcd", "2", "3", "4"]);
507        test_with_readback::<24>(&["0", "1", "abcde", "2", "3", "4"]);
508        test_with_readback::<24>(&["0", "1", "abcdef", "2", "3", "4"]);
509        test_with_readback::<24>(&["0", "1", "abcdefg", "2", "3", "4"]);
510    }
511
512    #[test]
513    fn rotate_contiguous() {
514        const N: usize = 48;
515        let data: [&[u8]; _] = [b"012345", b"hello world", b"xyz"];
516
517        for offset in 0..N {
518            let mut buf = BytearrayRingbuffer::<N>::new();
519            buf.head = offset;
520            buf.tail = offset;
521
522            for &d in &data {
523                buf.push(d).unwrap();
524            }
525
526            let read = buf.nth_contiguous(1).unwrap();
527            assert_eq!(data[1], read);
528
529            // check if the contents are still the same
530            for (&r, (a, b)) in data.iter().zip(buf.iter()) {
531                let mut out = Vec::new();
532                out.extend_from_slice(a);
533                out.extend_from_slice(b);
534                assert_eq!(out.as_slice(), r);
535            }
536        }
537    }
538}