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