leodos_protocols/utils/
ringbuf.rs1pub struct RingBuffer<const N: usize> {
13 buf: [u8; N],
14 head: usize,
16 tail: usize,
18 count: usize,
20}
21
22const LEN_SIZE: usize = 2;
24
25impl<const N: usize> RingBuffer<N> {
26 pub const fn new() -> Self {
28 Self {
29 buf: [0u8; N],
30 head: 0,
31 tail: 0,
32 count: 0,
33 }
34 }
35
36 pub fn push(&mut self, data: &[u8]) -> bool {
38 let needed = LEN_SIZE + data.len();
39 if needed > N {
40 return false;
41 }
42
43 let space_to_end = N - self.head;
45 let head = if space_to_end < needed {
46 if self.count > 0 && self.head >= self.tail {
49 if needed > self.tail {
52 return false;
53 }
54 }
55 self.buf[self.head] = 0;
58 self.buf[self.head + 1] = 0;
59 0
60 } else {
61 self.head
62 };
63
64 if self.count > 0 && head < self.tail && head + needed > self.tail {
66 return false;
67 }
68
69 let len = data.len() as u16;
70 self.buf[head] = (len >> 8) as u8;
71 self.buf[head + 1] = len as u8;
72 self.buf[head + LEN_SIZE..head + needed].copy_from_slice(data);
73 self.head = head + needed;
74 if self.head == N {
75 self.head = 0;
76 }
77 self.count += 1;
78 true
79 }
80
81 pub fn front(&self) -> Option<&[u8]> {
83 if self.count == 0 {
84 return None;
85 }
86 let tail = self.skip_sentinel(self.tail);
87 let len = self.read_len(tail);
88 Some(&self.buf[tail + LEN_SIZE..tail + LEN_SIZE + len])
89 }
90
91 pub fn pop(&mut self) {
93 if self.count == 0 {
94 return;
95 }
96 let tail = self.skip_sentinel(self.tail);
97 let len = self.read_len(tail);
98 self.tail = tail + LEN_SIZE + len;
99 if self.tail == N {
100 self.tail = 0;
101 }
102 self.count -= 1;
103 if self.count == 0 {
104 self.head = 0;
105 self.tail = 0;
106 }
107 }
108
109 pub fn len(&self) -> usize {
111 self.count
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.count == 0
117 }
118
119 fn read_len(&self, pos: usize) -> usize {
121 ((self.buf[pos] as usize) << 8) | self.buf[pos + 1] as usize
122 }
123
124 fn skip_sentinel(&self, pos: usize) -> usize {
127 if pos + LEN_SIZE <= N && self.read_len(pos) == 0 && pos != self.head {
128 0
129 } else {
130 pos
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn push_pop_single() {
141 let mut ring = RingBuffer::<64>::new();
142 assert!(ring.push(&[1, 2, 3]));
143 assert_eq!(ring.len(), 1);
144 assert_eq!(ring.front(), Some([1, 2, 3].as_slice()));
145 ring.pop();
146 assert!(ring.is_empty());
147 }
148
149 #[test]
150 fn push_pop_multiple() {
151 let mut ring = RingBuffer::<64>::new();
152 assert!(ring.push(&[10, 20]));
153 assert!(ring.push(&[30, 40, 50]));
154 assert_eq!(ring.len(), 2);
155 assert_eq!(ring.front(), Some([10, 20].as_slice()));
156 ring.pop();
157 assert_eq!(ring.front(), Some([30, 40, 50].as_slice()));
158 ring.pop();
159 assert!(ring.is_empty());
160 }
161
162 #[test]
163 fn wraps_around() {
164 let mut ring = RingBuffer::<16>::new();
166 assert!(ring.push(&[1, 2, 3, 4]));
168 assert!(ring.push(&[5, 6, 7, 8]));
170 ring.pop();
172 assert!(ring.push(&[9, 10, 11, 12]));
175 assert_eq!(ring.front(), Some([5, 6, 7, 8].as_slice()));
176 ring.pop();
177 assert_eq!(ring.front(), Some([9, 10, 11, 12].as_slice()));
178 ring.pop();
179 assert!(ring.is_empty());
180 }
181
182 #[test]
183 fn full_returns_false() {
184 let mut ring = RingBuffer::<8>::new();
185 assert!(ring.push(&[1, 2, 3]));
187 assert!(!ring.push(&[4, 5, 6]));
189 assert_eq!(ring.len(), 1);
190 }
191
192 #[test]
193 fn too_large_packet() {
194 let mut ring = RingBuffer::<8>::new();
195 assert!(!ring.push(&[0; 7]));
197 }
198
199 #[test]
200 fn many_small_packets() {
201 let mut ring = RingBuffer::<32>::new();
202 for i in 0..10u8 {
204 assert!(ring.push(&[i]));
205 }
206 assert_eq!(ring.len(), 10);
207 for i in 0..10u8 {
208 assert_eq!(ring.front(), Some([i].as_slice()));
209 ring.pop();
210 }
211 assert!(ring.is_empty());
212 }
213
214 #[test]
215 fn reuse_after_drain() {
216 let mut ring = RingBuffer::<16>::new();
217 for _ in 0..5 {
218 assert!(ring.push(&[1, 2, 3]));
219 ring.pop();
220 }
221 assert!(ring.is_empty());
222 assert!(ring.push(&[4, 5, 6, 7, 8]));
223 assert_eq!(ring.front(), Some([4, 5, 6, 7, 8].as_slice()));
224 }
225}