1#[repr(align(4))]
7pub struct RingBuf<const N: usize> {
8 buf: core::mem::MaybeUninit<[u8; N]>,
9 head: usize,
10 tail: usize,
11}
12
13impl<const N: usize> Default for RingBuf<N> {
14 fn default() -> Self {
15 Self {
16 buf: core::mem::MaybeUninit::uninit(),
17 head: 0,
18 tail: 0,
19 }
20 }
21}
22
23impl<const N: usize> RingBuf<N> {
24 #[inline(always)]
26 pub fn len(&self) -> usize {
27 self.head.wrapping_sub(self.tail) % N
28 }
29
30 #[inline(always)]
32 pub fn is_empty(&self) -> bool {
33 self.head == self.tail
34 }
35
36 pub fn push(&mut self, data: &[u8]) -> usize {
39 let free = N - 1 - self.len();
40 let n = data.len().min(free);
41
42 let ptr = self.buf.as_mut_ptr() as *mut u8;
43 let mut head = self.head;
44 for i in 0..n {
45 unsafe { *ptr.add(head) = *data.get_unchecked(i) };
47 head += 1;
48 if head == N {
49 head = 0;
50 }
51 }
52 self.head = head;
53 n
54 }
55
56 pub fn peek(&self, n: usize) -> &[u8] {
62 let available = self.len().min(n);
63 unsafe {
65 core::slice::from_raw_parts(self.buf.as_ptr().cast::<u8>().add(self.tail), available)
66 }
67 }
68
69 pub fn consume(&mut self, n: usize) {
71 self.tail = (self.tail + n) % N;
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn empty() {
81 let buf = RingBuf::<8>::default();
82 assert_eq!(buf.len(), 0);
83 assert_eq!(buf.peek(8), &[]);
84 }
85
86 #[test]
87 fn push_and_peek() {
88 let mut buf = RingBuf::<8>::default();
89 assert_eq!(buf.push(&[1, 2, 3]), 3);
90 assert_eq!(buf.len(), 3);
91 assert_eq!(buf.peek(3), &[1, 2, 3]);
92 }
93
94 #[test]
95 fn consume_advances() {
96 let mut buf = RingBuf::<8>::default();
97 buf.push(&[1, 2, 3, 4]);
98 buf.consume(2);
99 assert_eq!(buf.len(), 2);
100 assert_eq!(buf.peek(2), &[3, 4]);
101 }
102
103 #[test]
104 fn push_wraps() {
105 let mut buf = RingBuf::<8>::default();
106 buf.push(&[1, 2, 3, 4, 5]);
107 buf.consume(4); assert_eq!(buf.push(&[6, 7, 8, 9]), 4); assert_eq!(buf.len(), 5);
110 assert_eq!(buf.peek(1), &[5]);
111 }
112
113 #[test]
114 fn full_returns_short() {
115 let mut buf = RingBuf::<4>::default();
116 assert_eq!(buf.push(&[1, 2, 3]), 3); assert_eq!(buf.push(&[4]), 0); assert_eq!(buf.len(), 3);
119 }
120
121 #[test]
122 fn page_aligned_peek_is_contiguous() {
123 let mut buf = RingBuf::<8>::default();
125
126 buf.push(&[1, 2, 3, 4]);
128 assert_eq!(buf.peek(4), &[1, 2, 3, 4]);
129 buf.consume(4);
130
131 buf.push(&[5, 6, 7, 8]);
133 assert_eq!(buf.peek(4), &[5, 6, 7, 8]);
134 buf.consume(4);
135
136 buf.push(&[9, 10, 11, 12]);
138 assert_eq!(buf.peek(4), &[9, 10, 11, 12]);
139 }
140}