1pub struct WriteBuf {
55 buf: Box<[u8]>,
56 head: usize,
57 tail: usize,
58 reset_offset: usize,
59}
60
61impl WriteBuf {
62 #[must_use]
69 pub fn new(capacity: usize, headroom: usize) -> Self {
70 assert!(
71 headroom < capacity,
72 "headroom ({headroom}) must be less than capacity ({capacity})"
73 );
74 Self {
75 buf: vec![0u8; capacity].into_boxed_slice(),
76 head: headroom,
77 tail: headroom,
78 reset_offset: headroom,
79 }
80 }
81
82 #[inline]
92 pub fn prepend(&mut self, src: &[u8]) {
93 if src.len() > self.headroom() {
94 Self::panic_headroom(src.len(), self.headroom());
95 }
96 let new_head = self.head - src.len();
97 self.buf[new_head..self.head].copy_from_slice(src);
98 self.head = new_head;
99 }
100
101 #[inline]
106 pub fn append(&mut self, src: &[u8]) {
107 if src.len() > self.tailroom() {
108 Self::panic_tailroom(src.len(), self.tailroom());
109 }
110 self.buf[self.tail..self.tail + src.len()].copy_from_slice(src);
111 self.tail += src.len();
112 }
113
114 #[inline]
120 pub fn extend_zeroed(&mut self, n: usize) {
121 if n > self.tailroom() {
122 Self::panic_tailroom(n, self.tailroom());
123 }
124 self.buf[self.tail..self.tail + n].fill(0);
125 self.tail += n;
126 }
127
128 #[inline]
134 pub fn data(&self) -> &[u8] {
135 &self.buf[self.head..self.tail]
136 }
137
138 #[inline]
141 pub fn data_mut(&mut self) -> &mut [u8] {
142 &mut self.buf[self.head..self.tail]
143 }
144
145 #[inline]
154 pub fn spare(&mut self) -> &mut [u8] {
155 &mut self.buf[self.tail..]
156 }
157
158 #[inline]
163 pub fn filled(&mut self, n: usize) {
164 let new_tail = self.tail + n;
165 if new_tail > self.buf.len() {
166 Self::panic_filled(n, self.tail, self.buf.len());
167 }
168 self.tail = new_tail;
169 }
170
171 #[inline]
183 pub fn advance(&mut self, n: usize) {
184 if n > self.len() {
185 Self::panic_advance(n, self.len());
186 }
187 self.head += n;
188 if self.head == self.tail {
189 self.head = self.reset_offset;
190 self.tail = self.reset_offset;
191 }
192 }
193
194 #[inline]
200 pub fn headroom(&self) -> usize {
201 self.head
202 }
203
204 #[inline]
206 pub fn tailroom(&self) -> usize {
207 self.buf.len() - self.tail
208 }
209
210 #[inline]
212 pub fn len(&self) -> usize {
213 self.tail - self.head
214 }
215
216 #[inline]
218 pub fn is_empty(&self) -> bool {
219 self.head == self.tail
220 }
221
222 pub fn shrink_tail(&mut self, n: usize) {
228 if n > self.len() {
229 Self::panic_shrink(n, self.len());
230 }
231 self.tail -= n;
232 }
233
234 pub fn clear(&mut self) {
236 self.head = self.reset_offset;
237 self.tail = self.reset_offset;
238 }
239
240 #[cold]
241 #[inline(never)]
242 fn panic_headroom(needed: usize, available: usize) -> ! {
243 panic!("prepend: {needed} bytes exceeds headroom ({available})")
244 }
245
246 #[cold]
247 #[inline(never)]
248 fn panic_tailroom(needed: usize, available: usize) -> ! {
249 panic!("append: {needed} bytes exceeds tailroom ({available})")
250 }
251
252 #[cold]
253 #[inline(never)]
254 fn panic_advance(n: usize, len: usize) -> ! {
255 panic!("advance({n}) exceeds data length ({len})")
256 }
257
258 #[cold]
259 #[inline(never)]
260 fn panic_shrink(n: usize, len: usize) -> ! {
261 panic!("shrink_tail({n}) exceeds data length ({len})")
262 }
263
264 #[cold]
265 #[inline(never)]
266 fn panic_filled(n: usize, tail: usize, end: usize) -> ! {
267 panic!("filled({n}) would exceed buffer (tail={tail}, end={end})")
268 }
269}
270
271pub struct WriteBufWriter<'a> {
277 buf: &'a mut WriteBuf,
278 written: usize,
279}
280
281impl<'a> WriteBufWriter<'a> {
282 pub fn new(buf: &'a mut WriteBuf) -> Self {
284 Self { buf, written: 0 }
285 }
286
287 pub fn written(&self) -> usize {
289 self.written
290 }
291}
292
293impl std::io::Write for WriteBufWriter<'_> {
294 fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
295 let available = self.buf.tailroom();
296 if data.len() > available {
297 return Err(std::io::Error::new(
298 std::io::ErrorKind::WriteZero,
299 "write exceeds buffer capacity",
300 ));
301 }
302 self.buf.append(data);
303 self.written += data.len();
304 Ok(data.len())
305 }
306
307 fn flush(&mut self) -> std::io::Result<()> {
308 Ok(())
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
317 fn new_layout() {
318 let buf = WriteBuf::new(128, 14);
319 assert_eq!(buf.buf.len(), 128);
320 assert_eq!(buf.headroom(), 14);
321 assert_eq!(buf.tailroom(), 114);
322 assert!(buf.is_empty());
323 }
324
325 #[test]
326 fn append_data() {
327 let mut buf = WriteBuf::new(128, 14);
328 buf.append(b"Hello");
329 assert_eq!(buf.data(), b"Hello");
330 assert_eq!(buf.len(), 5);
331 }
332
333 #[test]
334 fn prepend_data() {
335 let mut buf = WriteBuf::new(128, 14);
336 buf.append(b"World");
337 buf.prepend(b"Hello");
338 assert_eq!(buf.data(), b"HelloWorld");
339 assert_eq!(buf.len(), 10);
340 }
341
342 #[test]
343 fn prepend_then_append() {
344 let mut buf = WriteBuf::new(128, 14);
345 buf.append(b"payload");
346 buf.prepend(&[0x81, 0x07]); let d = buf.data();
348 assert_eq!(&d[..2], &[0x81, 0x07]);
349 assert_eq!(&d[2..], b"payload");
350 }
351
352 #[test]
353 fn advance_partial_write() {
354 let mut buf = WriteBuf::new(128, 14);
355 buf.append(b"Hello, world!");
356 buf.advance(7);
357 assert_eq!(buf.data(), b"world!");
358 assert_eq!(buf.len(), 6);
359 }
360
361 #[test]
362 fn headroom_tailroom_tracking() {
363 let mut buf = WriteBuf::new(128, 14);
364 assert_eq!(buf.headroom(), 14);
365 assert_eq!(buf.tailroom(), 114);
366
367 buf.append(b"12345");
368 assert_eq!(buf.headroom(), 14);
369 assert_eq!(buf.tailroom(), 109);
370
371 buf.prepend(b"AB");
372 assert_eq!(buf.headroom(), 12);
373 assert_eq!(buf.tailroom(), 109);
374 }
375
376 #[test]
377 fn clear_resets() {
378 let mut buf = WriteBuf::new(128, 14);
379 buf.append(b"data");
380 buf.prepend(b"hdr");
381 buf.clear();
382 assert!(buf.is_empty());
383 assert_eq!(buf.headroom(), 14);
384 assert_eq!(buf.tailroom(), 114);
385 }
386
387 #[test]
388 fn multiple_cycles() {
389 let mut buf = WriteBuf::new(64, 10);
390 for i in 0u8..5 {
391 buf.clear();
392 buf.append(&[i; 4]);
393 buf.prepend(&[0xFF, i]);
394 assert_eq!(buf.len(), 6);
395 assert_eq!(buf.data()[0], 0xFF);
396 assert_eq!(buf.data()[1], i);
397 assert_eq!(&buf.data()[2..], &[i; 4]);
398 }
399 }
400
401 #[test]
402 #[should_panic(expected = "headroom")]
403 fn prepend_exceeds_headroom() {
404 let mut buf = WriteBuf::new(64, 4);
405 buf.prepend(&[0; 8]); }
407
408 #[test]
409 #[should_panic(expected = "tailroom")]
410 fn append_exceeds_tailroom() {
411 let mut buf = WriteBuf::new(16, 4);
412 buf.append(&[0; 16]); }
414
415 #[test]
416 #[should_panic(expected = "headroom")]
417 fn headroom_ge_capacity_panics() {
418 let _ = WriteBuf::new(10, 10);
419 }
420
421 #[test]
422 #[should_panic(expected = "advance")]
423 fn advance_exceeds_data() {
424 let mut buf = WriteBuf::new(64, 10);
425 buf.append(b"Hi");
426 buf.advance(5);
427 }
428
429 #[test]
430 fn zero_length_operations() {
431 let mut buf = WriteBuf::new(32, 8);
432 buf.append(b"");
433 buf.prepend(b"");
434 assert!(buf.is_empty());
435 buf.advance(0);
436 assert!(buf.is_empty());
437 }
438
439 #[test]
440 fn advance_auto_resets_on_empty() {
441 let mut buf = WriteBuf::new(64, 10);
442 buf.append(b"Hello");
443 buf.advance(5);
444 assert!(buf.is_empty());
445 assert_eq!(buf.headroom(), 10);
447 assert_eq!(buf.tailroom(), 54);
448 }
449
450 #[test]
451 fn advance_partial_does_not_reset() {
452 let mut buf = WriteBuf::new(64, 10);
453 buf.append(b"Hello");
454 buf.advance(2);
455 assert_eq!(buf.data(), b"llo");
457 assert_eq!(buf.headroom(), 12);
458 }
459
460 #[test]
461 fn cursor_fifo_cycle() {
462 let mut buf = WriteBuf::new(32, 0);
466
467 buf.spare()[..10].copy_from_slice(b"0123456789");
468 buf.filled(10);
469 assert_eq!(buf.data(), b"0123456789");
470
471 buf.advance(4);
472 assert_eq!(buf.data(), b"456789");
473
474 assert_eq!(buf.spare().len(), 22);
476 buf.spare()[..3].copy_from_slice(b"ABC");
477 buf.filled(3);
478 assert_eq!(buf.data(), b"456789ABC");
479
480 buf.advance(9);
481 assert!(buf.is_empty());
482 assert_eq!(buf.tailroom(), 32);
484 }
485
486 #[test]
487 fn spare_is_post_tail_region() {
488 let mut buf = WriteBuf::new(32, 8);
489 assert_eq!(buf.spare().len(), 24);
491 buf.append(b"hi");
492 assert_eq!(buf.spare().len(), 22);
494 }
495
496 #[test]
497 #[should_panic(expected = "filled")]
498 fn filled_exceeds_buffer() {
499 let mut buf = WriteBuf::new(16, 0);
500 buf.filled(32);
501 }
502}