1#![cfg_attr(not(test), no_std)]
2#![forbid(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5pub struct BytearrayRingbuffer<const N: usize> {
17 buffer: [u8; N],
18 head: usize,
20 tail: usize,
22 count: usize,
24}
25
26#[derive(Copy, Clone, Debug)]
32pub struct NotEnoughSpaceError;
33
34pub struct MultipartPush<'a, const N: usize> {
44 buf: &'a mut BytearrayRingbuffer<N>,
45 start: usize,
47 len: usize,
49 force: bool,
51 cancelled: bool,
53}
54
55impl<'a, const N: usize> MultipartPush<'a, N> {
56 pub fn push(&mut self, data: &[u8]) -> Result<(), NotEnoughSpaceError> {
72 if data.is_empty() {
73 return Ok(());
74 }
75
76 if self.len + data.len() > N - 8 {
78 return Err(NotEnoughSpaceError);
79 }
80
81 let needed = data.len() + 4;
83
84 if self.force {
85 while self.buf.bytes_unused() < needed && !self.buf.empty() {
86 self.buf.pop_front();
87 }
88 if self.buf.bytes_unused() < needed {
89 return Err(NotEnoughSpaceError);
90 }
91 } else if self.buf.bytes_unused() < needed {
92 return Err(NotEnoughSpaceError);
93 }
94
95 write_wrapping(&mut self.buf.buffer, self.buf.head, data);
96 self.buf.head = add_wrapping::<N>(self.buf.head, data.len());
97 self.len += data.len();
98
99 Ok(())
100 }
101
102 pub fn cancel(mut self) {
107 self.cancelled = true;
108 self.buf.head = self.start;
109 }
111}
112
113impl<'a, const N: usize> Drop for MultipartPush<'a, N> {
114 fn drop(&mut self) {
115 if self.cancelled {
116 return;
117 }
118 let len_bytes: [u8; 4] = (self.len as u32).to_ne_bytes();
119 write_wrapping(&mut self.buf.buffer, self.start, &len_bytes);
121 write_wrapping(&mut self.buf.buffer, self.buf.head, &len_bytes);
123 self.buf.head = add_wrapping::<N>(self.buf.head, 4);
124 self.buf.count += 1;
125 }
126}
127
128impl<const N: usize> BytearrayRingbuffer<N> {
129 pub const fn new() -> Self {
135 assert!(N > 8);
136 assert!(N < (u32::MAX as usize));
137 Self {
138 buffer: [0; N],
139 head: 0,
140 tail: 0,
141 count: 0,
142 }
143 }
144
145 pub const fn free(&self) -> usize {
150 self.bytes_unused().saturating_sub(8)
151 }
152
153 pub fn push(&mut self, data: &[u8]) -> Result<(), NotEnoughSpaceError> {
163 self._push(data, false)
164 }
165
166 pub fn push_force(&mut self, data: &[u8]) -> Result<(), NotEnoughSpaceError> {
175 self._push(data, true)
176 }
177
178 pub fn push_multipart(&mut self) -> Result<MultipartPush<'_, N>, NotEnoughSpaceError> {
189 if self.bytes_unused() < 8 {
191 return Err(NotEnoughSpaceError);
192 }
193 let start = self.head;
194 self.head = add_wrapping::<N>(self.head, 4);
195 Ok(MultipartPush {
196 buf: self,
197 start,
198 len: 0,
199 force: false,
200 cancelled: false,
201 })
202 }
203
204 pub fn push_multipart_force(&mut self) -> MultipartPush<'_, N> {
212 while self.bytes_unused() < 8 && !self.empty() {
214 self.pop_front();
215 }
216 let start = self.head;
217 self.head = add_wrapping::<N>(self.head, 4);
218 MultipartPush {
219 buf: self,
220 start,
221 len: 0,
222 force: true,
223 cancelled: false,
224 }
225 }
226
227 #[inline(always)]
229 pub const fn empty(&self) -> bool {
230 self.count == 0
231 }
232
233 const fn bytes_unused(&self) -> usize {
235 if self.empty() {
236 N
237 } else if self.head > self.tail {
238 N + self.tail - self.head
239 } else {
240 self.tail - self.head
241 }
242 }
243
244 fn _push(&mut self, data: &[u8], force: bool) -> Result<(), NotEnoughSpaceError> {
245 assert!(data.len() <= u32::MAX as usize);
246
247 if data.len() > N - 8 {
249 return Err(NotEnoughSpaceError);
250 }
251
252 if (data.len() + 8) > self.bytes_unused() {
254 if !force {
255 return Err(NotEnoughSpaceError);
256 }
257 while (data.len() + 8) > self.bytes_unused() {
258 self.pop_front();
259 }
260 }
261
262 let addr_a = self.head;
264 let addr_b = add_wrapping::<N>(self.head, 4);
265 let addr_c = add_wrapping::<N>(self.head, 4 + data.len());
266 let len_buffer: [u8; 4] = (data.len() as u32).to_ne_bytes();
267 write_wrapping(&mut self.buffer, addr_a, &len_buffer);
268 write_wrapping(&mut self.buffer, addr_b, data);
269 write_wrapping(&mut self.buffer, addr_c, &len_buffer);
270
271 self.head = add_wrapping::<N>(self.head, 8 + data.len());
272 self.count += 1;
273
274 Ok(())
275 }
276
277 pub fn pop_front(&mut self) -> Option<(&[u8], &[u8])> {
282 if self.empty() {
283 return None;
284 }
285 let mut len_buffer = [0; 4];
286 read_wrapping(&self.buffer, self.tail, &mut len_buffer);
287 let len = u32::from_ne_bytes(len_buffer) as usize;
288
289 let index_data = add_wrapping::<N>(self.tail, 4);
290 let len_a = (N - index_data).min(len);
291 let a = &self.buffer[index_data..index_data + len_a];
292 let b = if len_a == len {
293 &[]
294 } else {
295 &self.buffer[..len - len_a]
296 };
297
298 self.tail = add_wrapping::<N>(self.tail, len + 8);
299 self.count -= 1;
300 Some((a, b))
301 }
302
303 pub fn iter_backwards<'a>(&'a self) -> IterBackwards<'a, N> {
305 IterBackwards {
306 buffer: &self.buffer,
307 head: self.head,
308 count: self.count,
309 }
310 }
311
312 pub fn iter<'a>(&'a self) -> Iter<'a, N> {
314 Iter {
315 buffer: &self.buffer,
316 head: self.head,
317 tail: self.tail,
318 count: self.count,
319 }
320 }
321
322 #[inline(always)]
324 pub const fn count(&self) -> usize {
325 self.count
326 }
327
328 pub fn nth(&self, n: usize) -> Option<(&[u8], &[u8])> {
332 self.iter().nth(n)
333 }
334
335 pub fn nth_reverse(&self, n: usize) -> Option<(&[u8], &[u8])> {
339 self.iter_backwards().nth(n)
340 }
341
342 pub fn nth_contiguous(&mut self, mut n: usize) -> Option<&[u8]> {
350 if self.empty() || n >= self.count {
351 return None;
352 }
353
354 let mut tail = self.tail;
356 let len_data = loop {
357 let mut buf = [0u8; 4];
358 read_wrapping(&self.buffer, tail, &mut buf);
359 let len_data = u32::from_ne_bytes(buf) as usize;
360
361 if n == 0 {
362 break len_data;
363 }
364 n -= 1;
365
366 tail = add_wrapping::<N>(tail, len_data + 8);
367 };
368
369 let index_data = add_wrapping::<N>(tail, 4);
370
371 if index_data + len_data <= N {
373 return Some(&self.buffer[index_data..index_data + len_data]);
374 }
375
376 self.buffer.rotate_left(index_data);
378 self.tail = sub_wrapping::<N>(self.tail, index_data);
379 self.head = sub_wrapping::<N>(self.head, index_data);
380
381 Some(&self.buffer[..len_data])
382 }
383}
384
385pub struct IterBackwards<'a, const N: usize> {
387 buffer: &'a [u8; N],
388 head: usize,
389 count: usize,
390}
391
392impl<'a, const N: usize> Iterator for IterBackwards<'a, N> {
393 type Item = (&'a [u8], &'a [u8]);
394
395 fn next(&mut self) -> Option<Self::Item> {
396 if self.count == 0 {
397 return None;
398 }
399
400 let index_len = sub_wrapping::<N>(self.head, 4);
402 let mut buf = [0u8; 4];
403 read_wrapping(self.buffer, index_len, &mut buf);
404 let len_data = u32::from_ne_bytes(buf) as usize;
405 debug_assert!((len_data + 8) <= N);
406
407 #[cfg(test)]
408 {
409 let index_len = sub_wrapping::<N>(self.head, 8 + len_data);
410 let mut buf = [0u8; 4];
411 read_wrapping(self.buffer, index_len, &mut buf);
412 let len_2 = u32::from_ne_bytes(buf) as usize;
413 assert_eq!(len_data, len_2);
414 }
415
416 let index_data = sub_wrapping::<N>(self.head, 4 + len_data);
418 let first = (N - index_data).min(len_data);
419 let slice_a = &self.buffer[index_data..index_data + first];
420 let slice_b = if first < len_data {
421 &self.buffer[..len_data - first]
422 } else {
423 &[]
424 };
425
426 self.head = sub_wrapping::<N>(self.head, 8 + len_data);
427 self.count -= 1;
428
429 Some((slice_a, slice_b))
430 }
431}
432
433impl<const N: usize> Default for BytearrayRingbuffer<N> {
434 fn default() -> Self {
435 Self::new()
436 }
437}
438
439pub struct Iter<'a, const N: usize> {
441 buffer: &'a [u8; N],
442 head: usize,
443 tail: usize,
444 count: usize,
445}
446
447impl<'a, const N: usize> Iterator for Iter<'a, N> {
448 type Item = (&'a [u8], &'a [u8]);
449
450 fn next(&mut self) -> Option<Self::Item> {
451 if self.count == 0 {
452 return None;
453 }
454
455 let bytes_unused = if self.head > self.tail {
457 N + self.tail - self.head
458 } else {
459 self.tail - self.head
460 };
461 let bytes_occupied = N - bytes_unused;
462 debug_assert!(bytes_occupied >= 8);
463
464 let mut buf = [0u8; 4];
466 read_wrapping(self.buffer, self.tail, &mut buf);
467 let len_data = u32::from_ne_bytes(buf) as usize;
468 debug_assert!((len_data + 8) <= N);
469 debug_assert!((len_data + 8) <= bytes_occupied);
470
471 let index_data = add_wrapping::<N>(self.tail, 4);
473 let first = (N - index_data).min(len_data);
474 let slice_a = &self.buffer[index_data..index_data + first];
475 let slice_b = if first < len_data {
476 &self.buffer[..len_data - first]
477 } else {
478 &[]
479 };
480
481 self.tail = add_wrapping::<N>(self.tail, 8 + len_data);
482 self.count -= 1;
483
484 Some((slice_a, slice_b))
485 }
486}
487
488fn add_wrapping<const N: usize>(addr: usize, offset: usize) -> usize {
489 debug_assert!(addr < N);
490 debug_assert!(offset <= N);
491 let s = addr + offset;
492 if s < N { s } else { s - N }
493}
494
495fn sub_wrapping<const N: usize>(addr: usize, offset: usize) -> usize {
496 debug_assert!(addr < N);
497 debug_assert!(offset <= N);
498 if addr >= offset {
499 addr - offset
500 } else {
501 N + addr - offset
502 }
503}
504
505fn write_wrapping(buffer: &mut [u8], index: usize, data: &[u8]) {
507 let first = (buffer.len() - index).min(data.len());
508 buffer[index..index + first].copy_from_slice(&data[..first]);
509 if first < data.len() {
510 buffer[..data.len() - first].copy_from_slice(&data[first..]);
511 }
512}
513
514fn read_wrapping(buffer: &[u8], index: usize, data: &mut [u8]) {
516 let first = (buffer.len() - index).min(data.len());
517 data[..first].copy_from_slice(&buffer[index..index + first]);
518 if first < data.len() {
519 let remaining = data.len() - first;
520 data[first..].copy_from_slice(&buffer[..remaining]);
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use std::collections::VecDeque;
527
528 use super::BytearrayRingbuffer;
529
530 #[test]
531 fn push_some_packets() {
532 const N: usize = 64;
533 for start_offset in 0..N {
534 let mut buf = BytearrayRingbuffer::<N>::new();
535 buf.head = start_offset;
536 buf.tail = start_offset;
537
538 let free = 64 - 8;
539 assert_eq!(buf.free(), free);
540
541 buf.push(b"01234567").unwrap();
542 let free = free - 8 - 8;
543 assert_eq!(buf.free(), free);
544
545 buf.push(b"").unwrap();
546 let free = free - 8;
547 assert_eq!(buf.free(), free);
548
549 buf.push(b"0123").unwrap();
550 let free = free - 4 - 8;
551 assert_eq!(buf.free(), free);
552
553 buf.push(b"0123").unwrap();
554 let free = free - 4 - 8;
555 assert_eq!(buf.free(), free);
556 }
557 }
558
559 #[test]
560 fn push_force() {
561 let mut buf = BytearrayRingbuffer::<16>::new();
562 assert_eq!(buf.bytes_unused(), 16);
563
564 let a = b"012345";
565 let b = b"0123";
566
567 buf.push(a).unwrap();
568 assert_eq!(buf.bytes_unused(), 16 - a.len() - 8);
569
570 buf.push(b).unwrap_err();
571 assert_eq!(buf.bytes_unused(), 16 - a.len() - 8);
572
573 buf.push_force(b).unwrap();
574 assert_eq!(buf.bytes_unused(), 16 - b.len() - 8);
575 }
576
577 #[test]
578 fn push_all_data_lengths() {
579 for n in 0..(32 - 8) {
580 let mut buf = BytearrayRingbuffer::<32>::new();
581 let data = (0..n as u8).collect::<Vec<u8>>();
583
584 assert_eq!(buf.free(), 32 - 8);
585 buf.push(&data).unwrap();
586 assert_eq!(buf.free(), (32usize - 16).saturating_sub(n));
587 }
588 }
589
590 #[test]
591 fn push_sum_of_lengths_possible() {
592 let mut buf = BytearrayRingbuffer::<32>::new();
593 assert_eq!(buf.free(), 32 - 8);
595 buf.push(b"01234567").unwrap();
596 assert_eq!(buf.free(), 32 - 8 - 16);
597 buf.push(b"01234567").unwrap();
598 assert_eq!(buf.free(), 0);
599 }
600
601 #[test]
602 fn push_pop() {
603 const N: usize = 64;
604 for start_offset in 0..N {
605 eprintln!("--------------");
606 let mut buf = BytearrayRingbuffer::<N>::new();
607 buf.head = start_offset;
608 buf.tail = start_offset;
609
610 let data = b"01234567";
611 buf.push(data).unwrap();
612
613 let (a, b) = buf.pop_front().unwrap();
614 let mut out = Vec::new();
615 out.extend_from_slice(a);
616 out.extend_from_slice(b);
617
618 dbg!(out.as_slice());
619 assert!(data == out.as_slice());
620
621 assert_eq!(buf.head, buf.tail);
622 assert_eq!(buf.bytes_unused(), N);
623 }
624 }
625
626 #[test]
627 fn push_read_back() {
628 let data = [b"hello world" as &[u8], b"", b"test"];
629
630 const N: usize = 64;
631 for start_offset in 0..N {
632 let mut buf = BytearrayRingbuffer::<N>::new();
633 buf.head = start_offset;
634 buf.tail = start_offset;
635
636 for &d in &data {
637 buf.push(d).unwrap();
638 }
639
640 let mut it = buf.iter();
642 for &d in data.iter() {
643 let (a, b) = it.next().unwrap();
644 let mut ab = Vec::new();
645 ab.extend_from_slice(a);
646 ab.extend_from_slice(b);
647 let ab = ab.as_slice();
648 assert_eq!(d, ab);
649 }
650 assert_eq!(it.next(), None);
651
652 let mut it = buf.iter_backwards();
654 for &d in data.iter().rev() {
655 let (a, b) = it.next().unwrap();
656 let mut ab = Vec::new();
657 ab.extend_from_slice(a);
658 ab.extend_from_slice(b);
659 let ab = ab.as_slice();
660 assert_eq!(d, ab);
661 }
662 assert_eq!(it.next(), None);
663 }
664 }
665
666 #[test]
667 fn push_count() {
668 let mut buf = BytearrayRingbuffer::<64>::new();
669 buf.push(b"1234").unwrap();
670 assert_eq!(buf.count(), 1);
671 buf.push(b"1234").unwrap();
672 assert_eq!(buf.count(), 2);
673 buf.push(b"1234").unwrap();
674 assert_eq!(buf.count(), 3);
675 }
676
677 fn test_with_readback<const N: usize>(words: &[&'static str]) {
678 eprintln!("--------------------------");
679 let mut buf = BytearrayRingbuffer::<N>::new();
680 let mut current_words = VecDeque::new();
681 for &word in words {
682 eprintln!("adding {word:?}");
683 let word = word.to_owned();
684 let current_bytes: usize = current_words.iter().map(|w: &String| w.len() + 8).sum();
685 if current_bytes + 8 + word.len() > N {
686 current_words.pop_front();
687 }
688
689 buf.push_force(word.as_bytes()).unwrap();
690 current_words.push_back(word);
691
692 for (a, b) in buf.iter_backwards().zip(current_words.iter().rev()) {
693 eprintln!("read back {b:?}");
694 let mut st = String::new();
695 st.push_str(core::str::from_utf8(a.0).unwrap());
696 st.push_str(core::str::from_utf8(a.1).unwrap());
697 assert_eq!(st, *b);
698 }
699 }
700 }
701
702 #[test]
703 fn readback_various() {
704 test_with_readback::<32>(&["ab", "123", "hello", "world"]);
705 test_with_readback::<32>(&["", "", "a", "", "", ""]);
706 test_with_readback::<32>(&["", "", "ab", "", "", ""]);
707 test_with_readback::<32>(&["", "", "abc", "", "", ""]);
708 test_with_readback::<32>(&["", "", "abcd", "", "", ""]);
709 test_with_readback::<32>(&["", "", "abcde", "", "", ""]);
710
711 test_with_readback::<24>(&["0", "1", "a", "2", "3", "4"]);
712 test_with_readback::<24>(&["0", "1", "ab", "2", "3", "4"]);
713 test_with_readback::<24>(&["0", "1", "abc", "2", "3", "4"]);
714 test_with_readback::<24>(&["0", "1", "abcd", "2", "3", "4"]);
715 test_with_readback::<24>(&["0", "1", "abcde", "2", "3", "4"]);
716 test_with_readback::<24>(&["0", "1", "abcdef", "2", "3", "4"]);
717 test_with_readback::<24>(&["0", "1", "abcdefg", "2", "3", "4"]);
718 }
719
720 #[test]
721 fn nth_contiguous_out_of_range_returns_none() {
722 let mut buf = BytearrayRingbuffer::<64>::new();
723 buf.push(b"hello").unwrap();
724 assert_eq!(buf.count(), 1);
725
726 assert_eq!(buf.nth_contiguous(1), None);
727 }
728
729 #[test]
730 fn rotate_contiguous() {
731 const N: usize = 48;
732 let data: [&[u8]; _] = [b"012345", b"hello world", b"xyz"];
733
734 for offset in 0..N {
735 let mut buf = BytearrayRingbuffer::<N>::new();
736 buf.head = offset;
737 buf.tail = offset;
738
739 for &d in &data {
740 buf.push(d).unwrap();
741 }
742
743 let read = buf.nth_contiguous(1).unwrap();
744 assert_eq!(data[1], read);
745
746 for (&r, (a, b)) in data.iter().zip(buf.iter()) {
748 let mut out = Vec::new();
749 out.extend_from_slice(a);
750 out.extend_from_slice(b);
751 assert_eq!(out.as_slice(), r);
752 }
753 }
754 }
755
756 fn collect(a: &[u8], b: &[u8]) -> Vec<u8> {
759 let mut v = Vec::new();
760 v.extend_from_slice(a);
761 v.extend_from_slice(b);
762 v
763 }
764
765 #[test]
766 fn multipart_normal_fits() {
767 const N: usize = 64;
768 for offset in 0..N {
769 let mut buf = BytearrayRingbuffer::<N>::new();
770 buf.head = offset;
771 buf.tail = offset;
772
773 let mut mp = buf.push_multipart().unwrap();
774 mp.push(b"hello").unwrap();
775 mp.push(b" ").unwrap();
776 mp.push(b"world").unwrap();
777 drop(mp);
778
779 assert_eq!(buf.count(), 1);
780 let (a, b) = buf.pop_front().unwrap();
781 assert_eq!(collect(a, b), b"hello world");
782 assert_eq!(buf.count(), 0);
783 }
784 }
785
786 #[test]
787 fn multipart_empty_packet() {
788 let mut buf = BytearrayRingbuffer::<64>::new();
789 let mp = buf.push_multipart().unwrap();
790 drop(mp); assert_eq!(buf.count(), 1);
792 let (a, b) = buf.pop_front().unwrap();
793 assert_eq!(collect(a, b), b"");
794 }
795
796 #[test]
797 fn multipart_normal_overflow_returns_err() {
798 let mut buf = BytearrayRingbuffer::<24>::new();
803 buf.push(b"").unwrap(); let mut mp = buf.push_multipart().unwrap();
806 mp.push(b"abcd").unwrap(); let err = mp.push(b"12345"); assert!(err.is_err());
809
810 drop(mp);
812 assert_eq!(buf.count(), 2);
813 let (a, b) = buf.nth(1).unwrap();
815 assert_eq!(collect(a, b), b"abcd");
816 }
817
818 #[test]
819 fn multipart_cancel_normal_mode() {
820 let mut buf = BytearrayRingbuffer::<64>::new();
821 let original_unused = buf.bytes_unused();
822 let original_count = buf.count();
823
824 let mut mp = buf.push_multipart().unwrap();
825 mp.push(b"data that will be discarded").unwrap();
826 mp.cancel();
827
828 assert_eq!(buf.count(), original_count);
829 assert_eq!(buf.bytes_unused(), original_unused);
830 }
831
832 #[test]
833 fn multipart_normal_no_room_for_start() {
834 let mut buf = BytearrayRingbuffer::<24>::new();
836 buf.push(&[0u8; 16]).unwrap(); assert_eq!(buf.bytes_unused(), 0);
838 assert!(buf.push_multipart().is_err());
839 }
840
841 #[test]
842 fn multipart_force_drops_old_packets() {
843 let mut buf = BytearrayRingbuffer::<24>::new();
848 buf.push(b"AA").unwrap(); buf.push(b"BB").unwrap(); assert_eq!(buf.count(), 2);
851
852 let mut mp = buf.push_multipart_force();
853 mp.push(b"hello world").unwrap(); drop(mp);
855
856 assert_eq!(buf.count(), 1);
857 let (a, b) = buf.pop_front().unwrap();
858 assert_eq!(collect(a, b), b"hello world");
859 }
860
861 #[test]
862 fn multipart_force_cancel_drops_are_permanent() {
863 let mut buf = BytearrayRingbuffer::<24>::new();
865 buf.push(b"AA").unwrap();
866 buf.push(b"BB").unwrap();
867 let count_before = buf.count(); let mut mp = buf.push_multipart_force();
870 mp.push(b"hello world").unwrap(); mp.cancel(); assert!(buf.count() < count_before);
875 assert_eq!(buf.count(), 0);
876 }
877
878 #[test]
879 fn multipart_push_after_multipart() {
880 let mut buf = BytearrayRingbuffer::<64>::new();
881 {
882 let mut mp = buf.push_multipart().unwrap();
883 mp.push(b"first").unwrap();
884 }
885 buf.push(b"second").unwrap();
886
887 assert_eq!(buf.count(), 2);
888 let (a, b) = buf.nth(0).unwrap();
889 assert_eq!(collect(a, b), b"first");
890 let (a, b) = buf.nth(1).unwrap();
891 assert_eq!(collect(a, b), b"second");
892 }
893
894 #[test]
895 fn multipart_force_max_payload() {
896 const N: usize = 32;
898 let mut buf = BytearrayRingbuffer::<N>::new();
899 buf.push(b"old").unwrap(); let payload: Vec<u8> = (0..((N - 8) as u8)).collect();
902 let mut mp = buf.push_multipart_force();
903 mp.push(&payload).unwrap();
904 drop(mp);
905
906 assert_eq!(buf.count(), 1);
907 let (a, b) = buf.pop_front().unwrap();
908 assert_eq!(collect(a, b), payload);
909 }
910
911 #[test]
912 fn multipart_wraparound_all_offsets() {
913 const N: usize = 48;
914 for offset in 0..N {
915 let mut buf = BytearrayRingbuffer::<N>::new();
916 buf.head = offset;
917 buf.tail = offset;
918
919 buf.push(b"prefix").unwrap();
921
922 let mut mp = buf.push_multipart().unwrap();
924 mp.push(b"foo").unwrap();
925 mp.push(b"bar").unwrap();
926 drop(mp);
927
928 buf.push(b"suffix").unwrap();
930
931 assert_eq!(buf.count(), 3);
932 let (a, b) = buf.nth(0).unwrap();
933 assert_eq!(collect(a, b), b"prefix");
934 let (a, b) = buf.nth(1).unwrap();
935 assert_eq!(collect(a, b), b"foobar");
936 let (a, b) = buf.nth(2).unwrap();
937 assert_eq!(collect(a, b), b"suffix");
938 }
939 }
940}