Skip to main content

commonware_runtime/iobuf/
mod.rs

1//! Buffer types for I/O operations.
2//!
3//! Each buffer type is backed by one of three storage variants:
4//! - [`Bytes`]/[`BytesMut`]: standard heap allocation (from `From` conversions)
5//! - Aligned: untracked aligned allocation (from [`IoBufMut::with_alignment`],
6//!   pool bypass for small requests, or fallback)
7//! - Pooled: tracked aligned allocation returned to its originating [`BufferPool`]
8//!   on drop
9//!
10//! Public types:
11//! - [`IoBuf`]: Immutable byte buffer
12//! - [`IoBufMut`]: Mutable byte buffer
13//! - [`IoBufs`]: Container for one or more immutable buffers
14//! - [`IoBufsMut`]: Container for one or more mutable buffers
15//! - [`BufferPool`]: Pool of reusable, aligned buffers
16
17mod buffer;
18mod freelist;
19mod pool;
20
21pub(crate) use buffer::AlignedBuffer;
22use buffer::{AlignedBuf, AlignedBufMut, PooledBuf, PooledBufMut};
23use bytes::{Buf, BufMut, Bytes, BytesMut};
24use commonware_codec::{util::at_least, BufsMut, EncodeSize, Error, RangeCfg, Read, Write};
25use crossbeam_utils::CachePadded;
26pub use pool::{BufferPool, BufferPoolConfig, BufferPoolThreadCache, PoolError};
27use std::{collections::VecDeque, io::IoSlice, mem::align_of, num::NonZeroUsize, ops::RangeBounds};
28
29/// Returns the system page size.
30///
31/// On Unix systems, queries the actual page size via `sysconf`.
32/// On other systems (Windows), defaults to 4KB.
33#[allow(clippy::missing_const_for_fn)]
34pub fn page_size() -> usize {
35    #[cfg(unix)]
36    {
37        // SAFETY: sysconf is safe to call.
38        let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
39        if size <= 0 {
40            4096 // Safe fallback if sysconf fails
41        } else {
42            size as usize
43        }
44    }
45
46    #[cfg(not(unix))]
47    {
48        4096
49    }
50}
51
52/// Returns the cache line size for the current architecture.
53pub const fn cache_line_size() -> usize {
54    align_of::<CachePadded<u8>>()
55}
56
57#[cfg(feature = "bench")]
58pub mod bench {
59    pub use super::{buffer::PooledBuffer, freelist::Freelist};
60}
61
62/// Immutable byte buffer.
63///
64/// Backed by [`Bytes`], an untracked aligned allocation, or a pooled
65/// allocation.
66///
67/// Use this for immutable payloads. To build or mutate data, use
68/// [`IoBufMut`] and then [`IoBufMut::freeze`].
69///
70/// For pooled-backed values, the underlying buffer is returned to the pool
71/// when the final reference is dropped.
72///
73/// All `From<*> for IoBuf` implementations are guaranteed to be non-copy
74/// conversions. Use [`IoBuf::copy_from_slice`] when an explicit copy from
75/// borrowed data is required.
76///
77/// Cloning is cheap and does not copy underlying bytes.
78#[derive(Clone, Debug)]
79pub struct IoBuf {
80    inner: IoBufInner,
81}
82
83/// Internal storage variant for [`IoBuf`].
84///
85/// - `Bytes`: from `From<Bytes>`, `From<Vec<u8>>`, `From<&'static [u8]>`, etc.
86/// - `Aligned`: from untracked aligned allocation ([`IoBufMut::with_alignment`],
87///   pool bypass for small requests, or fallback)
88/// - `Pooled`: from [`BufferPool`] allocation (returned to pool on final drop)
89#[derive(Clone, Debug)]
90enum IoBufInner {
91    Bytes(Bytes),
92    Aligned(AlignedBuf),
93    Pooled(PooledBuf),
94}
95
96impl IoBuf {
97    /// Create a buffer by copying data from a slice.
98    ///
99    /// Use this when you have a non-static `&[u8]` that needs to be converted to an
100    /// [`IoBuf`]. For static slices, prefer [`IoBuf::from`] which is zero-copy.
101    pub fn copy_from_slice(data: &[u8]) -> Self {
102        Self {
103            inner: IoBufInner::Bytes(Bytes::copy_from_slice(data)),
104        }
105    }
106
107    /// Create a buffer from a pooled allocation.
108    #[inline]
109    const fn from_pooled(pooled: PooledBuf) -> Self {
110        Self {
111            inner: IoBufInner::Pooled(pooled),
112        }
113    }
114
115    /// Create a buffer from an untracked aligned allocation.
116    #[inline]
117    const fn from_aligned(aligned: AlignedBuf) -> Self {
118        Self {
119            inner: IoBufInner::Aligned(aligned),
120        }
121    }
122
123    /// Returns `true` if this buffer is tracked by a pool.
124    ///
125    /// Tracked buffers originate from [`BufferPool`] allocations and are
126    /// returned to the pool when the final reference is dropped.
127    ///
128    /// Buffers backed by [`Bytes`], and untracked aligned allocations (from
129    /// [`IoBufMut::with_alignment`], pool bypass for small requests, or
130    /// fallback), return `false`.
131    #[inline]
132    pub const fn is_pooled(&self) -> bool {
133        match &self.inner {
134            IoBufInner::Bytes(_) => false,
135            IoBufInner::Aligned(_) => false,
136            IoBufInner::Pooled(_) => true,
137        }
138    }
139
140    /// Number of bytes remaining in the buffer.
141    #[inline]
142    pub fn len(&self) -> usize {
143        self.remaining()
144    }
145
146    /// Whether the buffer is empty.
147    #[inline]
148    pub fn is_empty(&self) -> bool {
149        self.remaining() == 0
150    }
151
152    /// Get raw pointer to the buffer data.
153    #[inline]
154    pub fn as_ptr(&self) -> *const u8 {
155        match &self.inner {
156            IoBufInner::Bytes(b) => b.as_ptr(),
157            IoBufInner::Aligned(a) => a.as_ptr(),
158            IoBufInner::Pooled(p) => p.as_ptr(),
159        }
160    }
161
162    /// Returns a slice of self for the provided range (zero-copy).
163    ///
164    /// For pooled buffers, empty ranges return an empty detached buffer
165    /// ([`IoBuf::default`]) so the underlying pooled allocation is not retained.
166    #[inline]
167    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
168        match &self.inner {
169            IoBufInner::Bytes(b) => Self {
170                inner: IoBufInner::Bytes(b.slice(range)),
171            },
172            IoBufInner::Aligned(a) => a
173                .slice(range)
174                .map_or_else(Self::default, Self::from_aligned),
175            IoBufInner::Pooled(p) => p.slice(range).map_or_else(Self::default, Self::from_pooled),
176        }
177    }
178
179    /// Splits the buffer into two at the given index.
180    ///
181    /// Afterwards `self` contains bytes `[at, len)`, and the returned [`IoBuf`]
182    /// contains bytes `[0, at)`.
183    ///
184    /// This is an `O(1)` zero-copy operation.
185    ///
186    /// # Panics
187    ///
188    /// Panics if `at > len`.
189    pub fn split_to(&mut self, at: usize) -> Self {
190        if at == 0 {
191            return Self::default();
192        }
193
194        if at == self.remaining() {
195            return std::mem::take(self);
196        }
197
198        match &mut self.inner {
199            IoBufInner::Bytes(b) => Self {
200                inner: IoBufInner::Bytes(b.split_to(at)),
201            },
202            IoBufInner::Aligned(a) => Self::from_aligned(a.split_to(at)),
203            IoBufInner::Pooled(p) => Self::from_pooled(p.split_to(at)),
204        }
205    }
206
207    /// Try to convert this buffer into [`IoBufMut`] without copying.
208    ///
209    /// Succeeds when `self` holds exclusive ownership of the backing storage
210    /// and returns an [`IoBufMut`] with the same contents. Fails and returns
211    /// `self` unchanged when ownership is shared.
212    ///
213    /// For [`Bytes`]-backed buffers, this matches [`Bytes::try_into_mut`]
214    /// semantics: succeeds only for uniquely-owned full buffers, and always
215    /// fails for [`Bytes::from_owner`] and [`Bytes::from_static`] buffers. For
216    /// pooled buffers, this succeeds for any uniquely-owned view (including
217    /// slices) and fails when shared.
218    pub fn try_into_mut(self) -> Result<IoBufMut, Self> {
219        match self.inner {
220            IoBufInner::Bytes(bytes) => bytes
221                .try_into_mut()
222                .map(|mut_bytes| IoBufMut {
223                    inner: IoBufMutInner::Bytes(mut_bytes),
224                })
225                .map_err(|bytes| Self {
226                    inner: IoBufInner::Bytes(bytes),
227                }),
228            IoBufInner::Aligned(aligned) => aligned
229                .try_into_mut()
230                .map(|mut_aligned| IoBufMut {
231                    inner: IoBufMutInner::Aligned(mut_aligned),
232                })
233                .map_err(|aligned| Self {
234                    inner: IoBufInner::Aligned(aligned),
235                }),
236            IoBufInner::Pooled(pooled) => pooled
237                .try_into_mut()
238                .map(|mut_pooled| IoBufMut {
239                    inner: IoBufMutInner::Pooled(mut_pooled),
240                })
241                .map_err(|pooled| Self {
242                    inner: IoBufInner::Pooled(pooled),
243                }),
244        }
245    }
246
247    /// Convert this buffer into [`IoBufMut`], allocating from `pool` if needed.
248    ///
249    /// This is zero-copy when `self` has exclusive ownership of the backing
250    /// storage. If the buffer is shared, this allocates a new buffer from
251    /// `pool` and copies the readable bytes into it.
252    pub fn into_mut_with_pool(self, pool: &BufferPool) -> IoBufMut {
253        match self.try_into_mut() {
254            Ok(buf) => buf,
255            Err(buf) => {
256                let mut result = pool.alloc(buf.len());
257                result.put_slice(buf.as_ref());
258                result
259            }
260        }
261    }
262}
263
264impl AsRef<[u8]> for IoBuf {
265    #[inline]
266    fn as_ref(&self) -> &[u8] {
267        match &self.inner {
268            IoBufInner::Bytes(b) => b.as_ref(),
269            IoBufInner::Aligned(a) => a.as_ref(),
270            IoBufInner::Pooled(p) => p.as_ref(),
271        }
272    }
273}
274
275impl Default for IoBuf {
276    fn default() -> Self {
277        Self {
278            inner: IoBufInner::Bytes(Bytes::new()),
279        }
280    }
281}
282
283impl PartialEq for IoBuf {
284    fn eq(&self, other: &Self) -> bool {
285        self.as_ref() == other.as_ref()
286    }
287}
288
289impl Eq for IoBuf {}
290
291impl PartialEq<[u8]> for IoBuf {
292    #[inline]
293    fn eq(&self, other: &[u8]) -> bool {
294        self.as_ref() == other
295    }
296}
297
298impl PartialEq<&[u8]> for IoBuf {
299    #[inline]
300    fn eq(&self, other: &&[u8]) -> bool {
301        self.as_ref() == *other
302    }
303}
304
305impl<const N: usize> PartialEq<[u8; N]> for IoBuf {
306    #[inline]
307    fn eq(&self, other: &[u8; N]) -> bool {
308        self.as_ref() == other
309    }
310}
311
312impl<const N: usize> PartialEq<&[u8; N]> for IoBuf {
313    #[inline]
314    fn eq(&self, other: &&[u8; N]) -> bool {
315        self.as_ref() == *other
316    }
317}
318
319impl Buf for IoBuf {
320    #[inline]
321    fn remaining(&self) -> usize {
322        match &self.inner {
323            IoBufInner::Bytes(b) => b.remaining(),
324            IoBufInner::Aligned(a) => a.remaining(),
325            IoBufInner::Pooled(p) => p.remaining(),
326        }
327    }
328
329    #[inline]
330    fn chunk(&self) -> &[u8] {
331        match &self.inner {
332            IoBufInner::Bytes(b) => b.chunk(),
333            IoBufInner::Aligned(a) => a.chunk(),
334            IoBufInner::Pooled(p) => p.chunk(),
335        }
336    }
337
338    #[inline]
339    fn advance(&mut self, cnt: usize) {
340        match &mut self.inner {
341            IoBufInner::Bytes(b) => b.advance(cnt),
342            IoBufInner::Aligned(a) => a.advance(cnt),
343            IoBufInner::Pooled(p) => p.advance(cnt),
344        }
345    }
346
347    #[inline]
348    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
349        match &mut self.inner {
350            IoBufInner::Bytes(b) => b.copy_to_bytes(len),
351            IoBufInner::Aligned(a) => a.copy_to_bytes(len),
352            IoBufInner::Pooled(p) => {
353                // Full non-empty drain: transfer ownership so the drained source no
354                // longer retains the pooled allocation. Keep len == 0 on the normal
355                // path to avoid creating an empty Bytes that still pins pool memory.
356                if len != 0 && len == p.remaining() {
357                    let inner = std::mem::replace(&mut self.inner, IoBufInner::Bytes(Bytes::new()));
358                    match inner {
359                        IoBufInner::Pooled(p) => p.into_bytes(),
360                        _ => unreachable!(),
361                    }
362                } else {
363                    p.copy_to_bytes(len)
364                }
365            }
366        }
367    }
368}
369
370impl From<Bytes> for IoBuf {
371    fn from(bytes: Bytes) -> Self {
372        Self {
373            inner: IoBufInner::Bytes(bytes),
374        }
375    }
376}
377
378impl From<Vec<u8>> for IoBuf {
379    fn from(vec: Vec<u8>) -> Self {
380        Self {
381            inner: IoBufInner::Bytes(Bytes::from(vec)),
382        }
383    }
384}
385
386impl<const N: usize> From<&'static [u8; N]> for IoBuf {
387    fn from(array: &'static [u8; N]) -> Self {
388        Self {
389            inner: IoBufInner::Bytes(Bytes::from_static(array)),
390        }
391    }
392}
393
394impl From<&'static [u8]> for IoBuf {
395    fn from(slice: &'static [u8]) -> Self {
396        Self {
397            inner: IoBufInner::Bytes(Bytes::from_static(slice)),
398        }
399    }
400}
401
402/// Convert an [`IoBuf`] into a [`Vec<u8>`].
403///
404/// This conversion may copy:
405/// - [`Bytes`]-backed buffers may reuse allocation when possible
406/// - pooled buffers copy readable bytes into a new [`Vec<u8>`]
407impl From<IoBuf> for Vec<u8> {
408    fn from(buf: IoBuf) -> Self {
409        match buf.inner {
410            IoBufInner::Bytes(bytes) => Self::from(bytes),
411            IoBufInner::Aligned(aligned) => aligned.as_ref().to_vec(),
412            IoBufInner::Pooled(pooled) => pooled.as_ref().to_vec(),
413        }
414    }
415}
416
417/// Convert an [`IoBuf`] into [`Bytes`] without copying readable data.
418///
419/// For pooled buffers, this wraps the pooled owner using [`Bytes::from_owner`].
420impl From<IoBuf> for Bytes {
421    fn from(buf: IoBuf) -> Self {
422        match buf.inner {
423            IoBufInner::Bytes(bytes) => bytes,
424            IoBufInner::Aligned(aligned) => Self::from_owner(aligned),
425            IoBufInner::Pooled(pooled) => Self::from_owner(pooled),
426        }
427    }
428}
429
430impl Write for IoBuf {
431    #[inline]
432    fn write(&self, buf: &mut impl BufMut) {
433        self.len().write(buf);
434        buf.put_slice(self.as_ref());
435    }
436
437    #[inline]
438    fn write_bufs(&self, buf: &mut impl BufsMut) {
439        self.len().write(buf);
440        buf.push(self.clone());
441    }
442}
443
444impl EncodeSize for IoBuf {
445    #[inline]
446    fn encode_size(&self) -> usize {
447        self.len().encode_size() + self.len()
448    }
449
450    #[inline]
451    fn encode_inline_size(&self) -> usize {
452        self.len().encode_size()
453    }
454}
455
456impl Read for IoBuf {
457    type Cfg = RangeCfg<usize>;
458
459    #[inline]
460    fn read_cfg(buf: &mut impl Buf, range: &Self::Cfg) -> Result<Self, Error> {
461        let len = usize::read_cfg(buf, range)?;
462        at_least(buf, len)?;
463        Ok(Self::from(buf.copy_to_bytes(len)))
464    }
465}
466
467#[cfg(feature = "arbitrary")]
468impl arbitrary::Arbitrary<'_> for IoBuf {
469    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
470        let len = u.arbitrary_len::<u8>()?;
471        let data: Vec<u8> = u.arbitrary_iter()?.take(len).collect::<Result<_, _>>()?;
472        Ok(Self::from(data))
473    }
474}
475
476/// Mutable byte buffer.
477///
478/// Backed by [`BytesMut`], an untracked aligned allocation, or a pooled
479/// allocation.
480///
481/// Use this to build or mutate payloads before freezing into [`IoBuf`].
482///
483/// For pooled-backed values, dropping this buffer returns the underlying
484/// allocation to the pool. After [`IoBufMut::freeze`], the frozen `IoBuf`
485/// keeps the allocation alive until its final reference is dropped.
486#[derive(Debug)]
487pub struct IoBufMut {
488    inner: IoBufMutInner,
489}
490
491/// Internal storage variant for [`IoBufMut`]. See [`IoBufInner`] for variant
492/// semantics.
493#[derive(Debug)]
494enum IoBufMutInner {
495    Bytes(BytesMut),
496    Aligned(AlignedBufMut),
497    Pooled(PooledBufMut),
498}
499
500impl Default for IoBufMut {
501    fn default() -> Self {
502        Self {
503            inner: IoBufMutInner::Bytes(BytesMut::new()),
504        }
505    }
506}
507
508impl IoBufMut {
509    /// Create a buffer with the given capacity.
510    #[inline]
511    pub fn with_capacity(capacity: usize) -> Self {
512        Self {
513            inner: IoBufMutInner::Bytes(BytesMut::with_capacity(capacity)),
514        }
515    }
516
517    /// Create an untracked aligned buffer with the given capacity and alignment.
518    ///
519    /// This uses the aligned backing path directly rather than `BytesMut`.
520    /// The returned buffer is not tracked by a [`BufferPool`], so dropping it
521    /// deallocates the aligned allocation immediately.
522    ///
523    /// Use this when the caller needs a specific alignment but does not need
524    /// pooled reuse.
525    #[inline]
526    pub fn with_alignment(capacity: usize, alignment: NonZeroUsize) -> Self {
527        if capacity == 0 {
528            return Self::with_capacity(0);
529        }
530        let buffer = AlignedBuffer::new(capacity, alignment.get());
531        Self::from_aligned(AlignedBufMut::new(buffer))
532    }
533
534    /// Create a zero-initialized untracked aligned buffer with the given
535    /// length and alignment.
536    ///
537    /// Unlike [`Self::with_alignment`], this initializes the full readable
538    /// range to zero and sets `len == capacity == len`.
539    #[inline]
540    pub fn zeroed_with_alignment(len: usize, alignment: NonZeroUsize) -> Self {
541        if len == 0 {
542            return Self::zeroed(0);
543        }
544        let buffer = AlignedBuffer::new_zeroed(len, alignment.get());
545        let mut buffer = Self::from_aligned(AlignedBufMut::new(buffer));
546        // SAFETY: the aligned allocation was zero-initialized for `len` bytes.
547        unsafe { buffer.set_len(len) };
548        buffer
549    }
550
551    /// Create a buffer of `len` bytes, all initialized to zero.
552    ///
553    /// Unlike `with_capacity`, this sets both capacity and length to `len`,
554    /// making the entire buffer immediately usable for read operations
555    /// (e.g., `file.read_exact`).
556    #[inline]
557    pub fn zeroed(len: usize) -> Self {
558        Self {
559            inner: IoBufMutInner::Bytes(BytesMut::zeroed(len)),
560        }
561    }
562
563    /// Create a buffer from a pooled allocation.
564    #[inline]
565    const fn from_pooled(pooled: PooledBufMut) -> Self {
566        Self {
567            inner: IoBufMutInner::Pooled(pooled),
568        }
569    }
570
571    /// Create a buffer from an untracked aligned allocation.
572    #[inline]
573    const fn from_aligned(aligned: AlignedBufMut) -> Self {
574        Self {
575            inner: IoBufMutInner::Aligned(aligned),
576        }
577    }
578
579    /// Returns `true` if this buffer is tracked by a pool.
580    ///
581    /// Tracked buffers originate from [`BufferPool`] allocations and are
582    /// returned to the pool when dropped.
583    ///
584    /// Buffers backed by [`BytesMut`], and untracked aligned allocations (from
585    /// [`IoBufMut::with_alignment`], pool bypass for small requests, or
586    /// fallback), return `false`.
587    #[inline]
588    pub const fn is_pooled(&self) -> bool {
589        match &self.inner {
590            IoBufMutInner::Bytes(_) => false,
591            IoBufMutInner::Aligned(_) => false,
592            IoBufMutInner::Pooled(_) => true,
593        }
594    }
595
596    /// Sets the length of the buffer.
597    ///
598    /// This will explicitly set the size of the buffer without actually
599    /// modifying the data, so it is up to the caller to ensure that the data
600    /// has been initialized.
601    ///
602    /// # Safety
603    ///
604    /// Caller must ensure all bytes in `0..len` are initialized before any
605    /// read operations.
606    ///
607    /// # Panics
608    ///
609    /// Panics if `len > capacity()`.
610    #[inline]
611    pub unsafe fn set_len(&mut self, len: usize) {
612        assert!(
613            len <= self.capacity(),
614            "set_len({len}) exceeds capacity({})",
615            self.capacity()
616        );
617        match &mut self.inner {
618            IoBufMutInner::Bytes(b) => b.set_len(len),
619            IoBufMutInner::Aligned(b) => b.set_len(len),
620            IoBufMutInner::Pooled(b) => b.set_len(len),
621        }
622    }
623
624    /// Number of bytes remaining in the buffer.
625    #[inline]
626    pub fn len(&self) -> usize {
627        self.remaining()
628    }
629
630    /// Whether the buffer is empty.
631    #[inline]
632    pub fn is_empty(&self) -> bool {
633        match &self.inner {
634            IoBufMutInner::Bytes(b) => b.is_empty(),
635            IoBufMutInner::Aligned(b) => b.is_empty(),
636            IoBufMutInner::Pooled(b) => b.is_empty(),
637        }
638    }
639
640    /// Freeze into immutable [`IoBuf`].
641    #[inline]
642    pub fn freeze(self) -> IoBuf {
643        match self.inner {
644            IoBufMutInner::Bytes(b) => b.freeze().into(),
645            IoBufMutInner::Aligned(b) => b.freeze(),
646            IoBufMutInner::Pooled(b) => b.freeze(),
647        }
648    }
649
650    /// Returns the number of bytes the buffer can hold without reallocating.
651    #[inline]
652    pub fn capacity(&self) -> usize {
653        match &self.inner {
654            IoBufMutInner::Bytes(b) => b.capacity(),
655            IoBufMutInner::Aligned(b) => b.capacity(),
656            IoBufMutInner::Pooled(b) => b.capacity(),
657        }
658    }
659
660    /// Returns an unsafe mutable pointer to the buffer's data.
661    #[inline]
662    pub fn as_mut_ptr(&mut self) -> *mut u8 {
663        match &mut self.inner {
664            IoBufMutInner::Bytes(b) => b.as_mut_ptr(),
665            IoBufMutInner::Aligned(b) => b.as_mut_ptr(),
666            IoBufMutInner::Pooled(b) => b.as_mut_ptr(),
667        }
668    }
669
670    /// Truncates the buffer to `len` readable bytes.
671    ///
672    /// If `len` is greater than the current length, this has no effect.
673    #[inline]
674    pub fn truncate(&mut self, len: usize) {
675        match &mut self.inner {
676            IoBufMutInner::Bytes(b) => b.truncate(len),
677            IoBufMutInner::Aligned(b) => b.truncate(len),
678            IoBufMutInner::Pooled(b) => b.truncate(len),
679        }
680    }
681
682    /// Clears the buffer, removing all data. Existing capacity is preserved.
683    #[inline]
684    pub fn clear(&mut self) {
685        match &mut self.inner {
686            IoBufMutInner::Bytes(b) => b.clear(),
687            IoBufMutInner::Aligned(b) => b.clear(),
688            IoBufMutInner::Pooled(b) => b.clear(),
689        }
690    }
691}
692
693impl AsRef<[u8]> for IoBufMut {
694    #[inline]
695    fn as_ref(&self) -> &[u8] {
696        match &self.inner {
697            IoBufMutInner::Bytes(b) => b.as_ref(),
698            IoBufMutInner::Aligned(b) => b.as_ref(),
699            IoBufMutInner::Pooled(b) => b.as_ref(),
700        }
701    }
702}
703
704impl AsMut<[u8]> for IoBufMut {
705    #[inline]
706    fn as_mut(&mut self) -> &mut [u8] {
707        match &mut self.inner {
708            IoBufMutInner::Bytes(b) => b.as_mut(),
709            IoBufMutInner::Aligned(b) => b.as_mut(),
710            IoBufMutInner::Pooled(b) => b.as_mut(),
711        }
712    }
713}
714
715impl PartialEq<[u8]> for IoBufMut {
716    #[inline]
717    fn eq(&self, other: &[u8]) -> bool {
718        self.as_ref() == other
719    }
720}
721
722impl PartialEq<&[u8]> for IoBufMut {
723    #[inline]
724    fn eq(&self, other: &&[u8]) -> bool {
725        self.as_ref() == *other
726    }
727}
728
729impl<const N: usize> PartialEq<[u8; N]> for IoBufMut {
730    #[inline]
731    fn eq(&self, other: &[u8; N]) -> bool {
732        self.as_ref() == other
733    }
734}
735
736impl<const N: usize> PartialEq<&[u8; N]> for IoBufMut {
737    #[inline]
738    fn eq(&self, other: &&[u8; N]) -> bool {
739        self.as_ref() == *other
740    }
741}
742
743impl Buf for IoBufMut {
744    #[inline]
745    fn remaining(&self) -> usize {
746        match &self.inner {
747            IoBufMutInner::Bytes(b) => b.remaining(),
748            IoBufMutInner::Aligned(b) => b.remaining(),
749            IoBufMutInner::Pooled(b) => b.remaining(),
750        }
751    }
752
753    #[inline]
754    fn chunk(&self) -> &[u8] {
755        match &self.inner {
756            IoBufMutInner::Bytes(b) => b.chunk(),
757            IoBufMutInner::Aligned(b) => b.chunk(),
758            IoBufMutInner::Pooled(b) => b.chunk(),
759        }
760    }
761
762    #[inline]
763    fn advance(&mut self, cnt: usize) {
764        match &mut self.inner {
765            IoBufMutInner::Bytes(b) => b.advance(cnt),
766            IoBufMutInner::Aligned(b) => b.advance(cnt),
767            IoBufMutInner::Pooled(b) => b.advance(cnt),
768        }
769    }
770
771    #[inline]
772    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
773        match &mut self.inner {
774            IoBufMutInner::Bytes(b) => b.copy_to_bytes(len),
775            IoBufMutInner::Aligned(a) => a.copy_to_bytes(len),
776            IoBufMutInner::Pooled(p) => {
777                // Full non-empty drain: transfer ownership so the drained source no
778                // longer retains the pooled allocation. Keep len == 0 on the normal
779                // path to avoid creating an empty Bytes that still pins pool memory.
780                if len != 0 && len == p.remaining() {
781                    let inner =
782                        std::mem::replace(&mut self.inner, IoBufMutInner::Bytes(BytesMut::new()));
783                    match inner {
784                        IoBufMutInner::Pooled(p) => p.into_bytes(),
785                        _ => unreachable!(),
786                    }
787                } else {
788                    p.copy_to_bytes(len)
789                }
790            }
791        }
792    }
793}
794
795// SAFETY: Delegates to BytesMut or PooledBufMut which implement BufMut safely.
796unsafe impl BufMut for IoBufMut {
797    #[inline]
798    fn remaining_mut(&self) -> usize {
799        match &self.inner {
800            IoBufMutInner::Bytes(b) => b.remaining_mut(),
801            IoBufMutInner::Aligned(b) => b.remaining_mut(),
802            IoBufMutInner::Pooled(b) => b.remaining_mut(),
803        }
804    }
805
806    #[inline]
807    unsafe fn advance_mut(&mut self, cnt: usize) {
808        match &mut self.inner {
809            IoBufMutInner::Bytes(b) => b.advance_mut(cnt),
810            IoBufMutInner::Aligned(b) => b.advance_mut(cnt),
811            IoBufMutInner::Pooled(b) => b.advance_mut(cnt),
812        }
813    }
814
815    #[inline]
816    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
817        match &mut self.inner {
818            IoBufMutInner::Bytes(b) => b.chunk_mut(),
819            IoBufMutInner::Aligned(b) => b.chunk_mut(),
820            IoBufMutInner::Pooled(b) => b.chunk_mut(),
821        }
822    }
823}
824
825impl From<Vec<u8>> for IoBufMut {
826    fn from(vec: Vec<u8>) -> Self {
827        Self::from(Bytes::from(vec))
828    }
829}
830
831impl From<&[u8]> for IoBufMut {
832    fn from(slice: &[u8]) -> Self {
833        Self {
834            inner: IoBufMutInner::Bytes(BytesMut::from(slice)),
835        }
836    }
837}
838
839impl<const N: usize> From<[u8; N]> for IoBufMut {
840    fn from(array: [u8; N]) -> Self {
841        Self::from(array.as_ref())
842    }
843}
844
845impl<const N: usize> From<&[u8; N]> for IoBufMut {
846    fn from(array: &[u8; N]) -> Self {
847        Self::from(array.as_ref())
848    }
849}
850
851impl From<BytesMut> for IoBufMut {
852    fn from(bytes: BytesMut) -> Self {
853        Self {
854            inner: IoBufMutInner::Bytes(bytes),
855        }
856    }
857}
858
859impl From<Bytes> for IoBufMut {
860    /// Zero-copy if `bytes` is unique for the entire original buffer (refcount is 1),
861    /// copies otherwise. Always copies if the [`Bytes`] was constructed via
862    /// [`Bytes::from_owner`] or [`Bytes::from_static`].
863    fn from(bytes: Bytes) -> Self {
864        Self {
865            inner: IoBufMutInner::Bytes(BytesMut::from(bytes)),
866        }
867    }
868}
869
870impl From<IoBuf> for IoBufMut {
871    /// Zero-copy when exclusive ownership can be recovered, copies otherwise.
872    fn from(buf: IoBuf) -> Self {
873        match buf.try_into_mut() {
874            Ok(buf) => buf,
875            Err(buf) => Self::from(buf.as_ref()),
876        }
877    }
878}
879
880/// Container for one or more immutable buffers.
881#[derive(Clone, Debug)]
882pub struct IoBufs {
883    inner: IoBufsInner,
884}
885
886/// Internal immutable representation.
887///
888/// - Representation is canonical and minimal for readable data:
889///   - `Single` is the only representation for empty data and one-chunk data.
890///   - `Chunked` is used only when four or more readable chunks remain.
891/// - `Pair`, `Triple`, and `Chunked` never store empty chunks.
892#[derive(Clone, Debug)]
893enum IoBufsInner {
894    /// Single buffer (fast path).
895    Single(IoBuf),
896    /// Two buffers (fast path).
897    Pair([IoBuf; 2]),
898    /// Three buffers (fast path).
899    Triple([IoBuf; 3]),
900    /// Four or more buffers.
901    Chunked(VecDeque<IoBuf>),
902}
903
904impl Default for IoBufs {
905    fn default() -> Self {
906        Self {
907            inner: IoBufsInner::Single(IoBuf::default()),
908        }
909    }
910}
911
912impl IoBufs {
913    /// Build canonical immutable chunk storage from readable chunks.
914    ///
915    /// Empty chunks are removed before representation selection.
916    fn from_chunks_iter(chunks: impl IntoIterator<Item = IoBuf>) -> Self {
917        let mut iter = chunks.into_iter().filter(|buf| !buf.is_empty());
918        let first = match iter.next() {
919            Some(first) => first,
920            None => return Self::default(),
921        };
922        let second = match iter.next() {
923            Some(second) => second,
924            None => {
925                return Self {
926                    inner: IoBufsInner::Single(first),
927                };
928            }
929        };
930        let third = match iter.next() {
931            Some(third) => third,
932            None => {
933                return Self {
934                    inner: IoBufsInner::Pair([first, second]),
935                };
936            }
937        };
938        let fourth = match iter.next() {
939            Some(fourth) => fourth,
940            None => {
941                return Self {
942                    inner: IoBufsInner::Triple([first, second, third]),
943                };
944            }
945        };
946
947        let mut bufs = VecDeque::with_capacity(4);
948        bufs.push_back(first);
949        bufs.push_back(second);
950        bufs.push_back(third);
951        bufs.push_back(fourth);
952        bufs.extend(iter);
953
954        Self {
955            inner: IoBufsInner::Chunked(bufs),
956        }
957    }
958
959    /// Re-establish canonical immutable representation invariants.
960    fn canonicalize(&mut self) {
961        let inner = std::mem::replace(&mut self.inner, IoBufsInner::Single(IoBuf::default()));
962        self.inner = match inner {
963            IoBufsInner::Single(buf) => {
964                if buf.is_empty() {
965                    IoBufsInner::Single(IoBuf::default())
966                } else {
967                    IoBufsInner::Single(buf)
968                }
969            }
970            IoBufsInner::Pair([a, b]) => Self::from_chunks_iter([a, b]).inner,
971            IoBufsInner::Triple([a, b, c]) => Self::from_chunks_iter([a, b, c]).inner,
972            IoBufsInner::Chunked(bufs) => Self::from_chunks_iter(bufs).inner,
973        };
974    }
975
976    /// Returns a reference to the single contiguous buffer, if present.
977    ///
978    /// Returns `Some` only when all remaining data is in one contiguous buffer.
979    pub const fn as_single(&self) -> Option<&IoBuf> {
980        match &self.inner {
981            IoBufsInner::Single(buf) => Some(buf),
982            _ => None,
983        }
984    }
985
986    /// Consume this container and return the single buffer if present.
987    ///
988    /// Returns `Ok(IoBuf)` only when all remaining data is already contained in
989    /// a single chunk. Returns `Err(Self)` with the original container
990    /// otherwise.
991    pub fn try_into_single(self) -> Result<IoBuf, Self> {
992        match self.inner {
993            IoBufsInner::Single(buf) => Ok(buf),
994            inner => Err(Self { inner }),
995        }
996    }
997
998    /// Number of bytes remaining across all buffers.
999    #[inline]
1000    pub fn len(&self) -> usize {
1001        self.remaining()
1002    }
1003
1004    /// Number of non-empty readable chunks.
1005    #[inline]
1006    pub fn chunk_count(&self) -> usize {
1007        // This assumes canonical form.
1008        match &self.inner {
1009            IoBufsInner::Single(buf) => {
1010                if buf.is_empty() {
1011                    0
1012                } else {
1013                    1
1014                }
1015            }
1016            IoBufsInner::Pair(_) => 2,
1017            IoBufsInner::Triple(_) => 3,
1018            IoBufsInner::Chunked(bufs) => bufs.len(),
1019        }
1020    }
1021
1022    /// Whether all buffers are empty.
1023    #[inline]
1024    pub fn is_empty(&self) -> bool {
1025        self.remaining() == 0
1026    }
1027
1028    /// Whether this contains a single contiguous buffer.
1029    ///
1030    /// When true, `chunk()` returns all remaining bytes.
1031    #[inline]
1032    pub const fn is_single(&self) -> bool {
1033        matches!(self.inner, IoBufsInner::Single(_))
1034    }
1035
1036    /// Visit each readable chunk in order without coalescing.
1037    #[inline]
1038    pub fn for_each_chunk(&self, mut f: impl FnMut(&[u8])) {
1039        match &self.inner {
1040            IoBufsInner::Single(buf) => {
1041                let chunk = buf.as_ref();
1042                if !chunk.is_empty() {
1043                    f(chunk);
1044                }
1045            }
1046            IoBufsInner::Pair(pair) => {
1047                for buf in pair {
1048                    let chunk = buf.as_ref();
1049                    if !chunk.is_empty() {
1050                        f(chunk);
1051                    }
1052                }
1053            }
1054            IoBufsInner::Triple(triple) => {
1055                for buf in triple {
1056                    let chunk = buf.as_ref();
1057                    if !chunk.is_empty() {
1058                        f(chunk);
1059                    }
1060                }
1061            }
1062            IoBufsInner::Chunked(bufs) => {
1063                for buf in bufs {
1064                    let chunk = buf.as_ref();
1065                    if !chunk.is_empty() {
1066                        f(chunk);
1067                    }
1068                }
1069            }
1070        }
1071    }
1072
1073    /// Prepend a buffer to the front.
1074    ///
1075    /// Empty input buffers are ignored.
1076    pub fn prepend(&mut self, buf: IoBuf) {
1077        if buf.is_empty() {
1078            return;
1079        }
1080        let inner = std::mem::replace(&mut self.inner, IoBufsInner::Single(IoBuf::default()));
1081        self.inner = match inner {
1082            IoBufsInner::Single(existing) if existing.is_empty() => IoBufsInner::Single(buf),
1083            IoBufsInner::Single(existing) => IoBufsInner::Pair([buf, existing]),
1084            IoBufsInner::Pair([a, b]) => IoBufsInner::Triple([buf, a, b]),
1085            IoBufsInner::Triple([a, b, c]) => {
1086                let mut bufs = VecDeque::with_capacity(4);
1087                bufs.push_back(buf);
1088                bufs.push_back(a);
1089                bufs.push_back(b);
1090                bufs.push_back(c);
1091                IoBufsInner::Chunked(bufs)
1092            }
1093            IoBufsInner::Chunked(mut bufs) => {
1094                bufs.push_front(buf);
1095                IoBufsInner::Chunked(bufs)
1096            }
1097        };
1098    }
1099
1100    /// Append a buffer to the back.
1101    ///
1102    /// Empty input buffers are ignored.
1103    pub fn append(&mut self, buf: IoBuf) {
1104        if buf.is_empty() {
1105            return;
1106        }
1107        let inner = std::mem::replace(&mut self.inner, IoBufsInner::Single(IoBuf::default()));
1108        self.inner = match inner {
1109            IoBufsInner::Single(existing) if existing.is_empty() => IoBufsInner::Single(buf),
1110            IoBufsInner::Single(existing) => IoBufsInner::Pair([existing, buf]),
1111            IoBufsInner::Pair([a, b]) => IoBufsInner::Triple([a, b, buf]),
1112            IoBufsInner::Triple([a, b, c]) => {
1113                let mut bufs = VecDeque::with_capacity(4);
1114                bufs.push_back(a);
1115                bufs.push_back(b);
1116                bufs.push_back(c);
1117                bufs.push_back(buf);
1118                IoBufsInner::Chunked(bufs)
1119            }
1120            IoBufsInner::Chunked(mut bufs) => {
1121                bufs.push_back(buf);
1122                IoBufsInner::Chunked(bufs)
1123            }
1124        };
1125    }
1126
1127    /// Splits the buffer(s) into two at the given index.
1128    ///
1129    /// Afterwards `self` contains bytes `[at, len)`, and the returned
1130    /// [`IoBufs`] contains bytes `[0, at)`.
1131    ///
1132    /// Whole chunks are moved without copying. If the split point lands inside
1133    /// a chunk, the chunk is split zero-copy via [`IoBuf::split_to`].
1134    ///
1135    /// # Panics
1136    ///
1137    /// Panics if `at > len`.
1138    pub fn split_to(&mut self, at: usize) -> Self {
1139        if at == 0 {
1140            return Self::default();
1141        }
1142
1143        let remaining = self.remaining();
1144        assert!(
1145            at <= remaining,
1146            "split_to out of bounds: {:?} <= {:?}",
1147            at,
1148            remaining,
1149        );
1150
1151        if at == remaining {
1152            return std::mem::take(self);
1153        }
1154
1155        let inner = std::mem::replace(&mut self.inner, IoBufsInner::Single(IoBuf::default()));
1156        match inner {
1157            IoBufsInner::Single(mut buf) => {
1158                // Delegate directly and keep remainder as single
1159                let prefix = buf.split_to(at);
1160                self.inner = IoBufsInner::Single(buf);
1161                Self::from(prefix)
1162            }
1163            IoBufsInner::Pair([mut a, mut b]) => {
1164                let a_len = a.remaining();
1165                if at < a_len {
1166                    // Split stays entirely in chunk `a`.
1167                    let prefix = a.split_to(at);
1168                    self.inner = IoBufsInner::Pair([a, b]);
1169                    return Self::from(prefix);
1170                }
1171                if at == a_len {
1172                    // Exact chunk boundary: move `a` out, keep `b`.
1173                    self.inner = IoBufsInner::Single(b);
1174                    return Self::from(a);
1175                }
1176
1177                // Split crosses from `a` into `b`.
1178                let b_prefix_len = at - a_len;
1179                let b_prefix = b.split_to(b_prefix_len);
1180                self.inner = IoBufsInner::Single(b);
1181                Self {
1182                    inner: IoBufsInner::Pair([a, b_prefix]),
1183                }
1184            }
1185            IoBufsInner::Triple([mut a, mut b, mut c]) => {
1186                let a_len = a.remaining();
1187                if at < a_len {
1188                    // Split stays entirely in chunk `a`.
1189                    let prefix = a.split_to(at);
1190                    self.inner = IoBufsInner::Triple([a, b, c]);
1191                    return Self::from(prefix);
1192                }
1193                if at == a_len {
1194                    // Exact boundary after `a`.
1195                    self.inner = IoBufsInner::Pair([b, c]);
1196                    return Self::from(a);
1197                }
1198
1199                let mut remaining = at - a_len;
1200                let b_len = b.remaining();
1201                if remaining < b_len {
1202                    // Split lands inside `b`.
1203                    let b_prefix = b.split_to(remaining);
1204                    self.inner = IoBufsInner::Pair([b, c]);
1205                    return Self {
1206                        inner: IoBufsInner::Pair([a, b_prefix]),
1207                    };
1208                }
1209                if remaining == b_len {
1210                    // Exact boundary after `b`.
1211                    self.inner = IoBufsInner::Single(c);
1212                    return Self {
1213                        inner: IoBufsInner::Pair([a, b]),
1214                    };
1215                }
1216
1217                // Split reaches into `c`.
1218                remaining -= b_len;
1219                let c_prefix = c.split_to(remaining);
1220                self.inner = IoBufsInner::Single(c);
1221                Self {
1222                    inner: IoBufsInner::Triple([a, b, c_prefix]),
1223                }
1224            }
1225            IoBufsInner::Chunked(mut bufs) => {
1226                let mut remaining = at;
1227                let mut out = VecDeque::new();
1228
1229                while remaining > 0 {
1230                    let mut front = bufs.pop_front().expect("split_to out of bounds");
1231                    let avail = front.remaining();
1232                    if avail == 0 {
1233                        // Canonical chunked state should not contain empties.
1234                        continue;
1235                    }
1236                    if remaining < avail {
1237                        // Split inside this chunk: keep suffix in `self`, move prefix to output.
1238                        let prefix = front.split_to(remaining);
1239                        out.push_back(prefix);
1240                        bufs.push_front(front);
1241                        break;
1242                    }
1243
1244                    // Consume this full chunk into the output prefix.
1245                    out.push_back(front);
1246                    remaining -= avail;
1247                }
1248
1249                self.inner = if bufs.len() >= 4 {
1250                    IoBufsInner::Chunked(bufs)
1251                } else {
1252                    Self::from_chunks_iter(bufs).inner
1253                };
1254
1255                if out.len() >= 4 {
1256                    Self {
1257                        inner: IoBufsInner::Chunked(out),
1258                    }
1259                } else {
1260                    Self::from_chunks_iter(out)
1261                }
1262            }
1263        }
1264    }
1265
1266    /// Coalesce all remaining bytes into a single contiguous [`IoBuf`].
1267    ///
1268    /// Zero-copy if only one buffer. Copies if multiple buffers.
1269    #[inline]
1270    pub fn coalesce(mut self) -> IoBuf {
1271        match self.inner {
1272            IoBufsInner::Single(buf) => buf,
1273            _ => self.copy_to_bytes(self.remaining()).into(),
1274        }
1275    }
1276
1277    /// Coalesce all remaining bytes into a single contiguous [`IoBuf`], using the pool
1278    /// for allocation if multiple buffers need to be merged.
1279    ///
1280    /// Zero-copy if only one buffer. Uses pool allocation if multiple buffers.
1281    pub fn coalesce_with_pool(self, pool: &BufferPool) -> IoBuf {
1282        match self.inner {
1283            IoBufsInner::Single(buf) => buf,
1284            IoBufsInner::Pair([a, b]) => {
1285                let total_len = a.remaining().saturating_add(b.remaining());
1286                let mut result = pool.alloc(total_len);
1287                result.put_slice(a.as_ref());
1288                result.put_slice(b.as_ref());
1289                result.freeze()
1290            }
1291            IoBufsInner::Triple([a, b, c]) => {
1292                let total_len = a
1293                    .remaining()
1294                    .saturating_add(b.remaining())
1295                    .saturating_add(c.remaining());
1296                let mut result = pool.alloc(total_len);
1297                result.put_slice(a.as_ref());
1298                result.put_slice(b.as_ref());
1299                result.put_slice(c.as_ref());
1300                result.freeze()
1301            }
1302            IoBufsInner::Chunked(bufs) => {
1303                let total_len: usize = bufs
1304                    .iter()
1305                    .map(|b| b.remaining())
1306                    .fold(0, usize::saturating_add);
1307                let mut result = pool.alloc(total_len);
1308                for buf in bufs {
1309                    result.put_slice(buf.as_ref());
1310                }
1311                result.freeze()
1312            }
1313        }
1314    }
1315}
1316
1317impl Buf for IoBufs {
1318    fn remaining(&self) -> usize {
1319        match &self.inner {
1320            IoBufsInner::Single(buf) => buf.remaining(),
1321            IoBufsInner::Pair([a, b]) => a.remaining().saturating_add(b.remaining()),
1322            IoBufsInner::Triple([a, b, c]) => a
1323                .remaining()
1324                .saturating_add(b.remaining())
1325                .saturating_add(c.remaining()),
1326            IoBufsInner::Chunked(bufs) => bufs
1327                .iter()
1328                .map(|b| b.remaining())
1329                .fold(0, usize::saturating_add),
1330        }
1331    }
1332
1333    fn chunk(&self) -> &[u8] {
1334        match &self.inner {
1335            IoBufsInner::Single(buf) => buf.chunk(),
1336            IoBufsInner::Pair([a, b]) => {
1337                if a.remaining() > 0 {
1338                    a.chunk()
1339                } else if b.remaining() > 0 {
1340                    b.chunk()
1341                } else {
1342                    &[]
1343                }
1344            }
1345            IoBufsInner::Triple([a, b, c]) => {
1346                if a.remaining() > 0 {
1347                    a.chunk()
1348                } else if b.remaining() > 0 {
1349                    b.chunk()
1350                } else if c.remaining() > 0 {
1351                    c.chunk()
1352                } else {
1353                    &[]
1354                }
1355            }
1356            IoBufsInner::Chunked(bufs) => {
1357                for buf in bufs.iter() {
1358                    if buf.remaining() > 0 {
1359                        return buf.chunk();
1360                    }
1361                }
1362                &[]
1363            }
1364        }
1365    }
1366
1367    fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
1368        if dst.is_empty() {
1369            return 0;
1370        }
1371
1372        match &self.inner {
1373            IoBufsInner::Single(buf) => {
1374                let chunk = buf.chunk();
1375                if !chunk.is_empty() {
1376                    dst[0] = IoSlice::new(chunk);
1377                    return 1;
1378                }
1379                0
1380            }
1381            IoBufsInner::Pair([a, b]) => fill_vectored_from_chunks(dst, [a.chunk(), b.chunk()]),
1382            IoBufsInner::Triple([a, b, c]) => {
1383                fill_vectored_from_chunks(dst, [a.chunk(), b.chunk(), c.chunk()])
1384            }
1385            IoBufsInner::Chunked(bufs) => {
1386                fill_vectored_from_chunks(dst, bufs.iter().map(|buf| buf.chunk()))
1387            }
1388        }
1389    }
1390
1391    fn advance(&mut self, cnt: usize) {
1392        let should_canonicalize = match &mut self.inner {
1393            IoBufsInner::Single(buf) => {
1394                buf.advance(cnt);
1395                false
1396            }
1397            IoBufsInner::Pair(pair) => advance_small_chunks(pair.as_mut_slice(), cnt),
1398            IoBufsInner::Triple(triple) => advance_small_chunks(triple.as_mut_slice(), cnt),
1399            IoBufsInner::Chunked(bufs) => {
1400                advance_chunked_front(bufs, cnt);
1401                bufs.len() <= 3
1402            }
1403        };
1404
1405        if should_canonicalize {
1406            self.canonicalize();
1407        }
1408    }
1409
1410    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
1411        let (result, needs_canonicalize) = match &mut self.inner {
1412            IoBufsInner::Single(buf) => return buf.copy_to_bytes(len),
1413            IoBufsInner::Pair(pair) => {
1414                copy_to_bytes_small_chunks(pair, len, "IoBufs::copy_to_bytes: not enough data")
1415            }
1416            IoBufsInner::Triple(triple) => {
1417                copy_to_bytes_small_chunks(triple, len, "IoBufs::copy_to_bytes: not enough data")
1418            }
1419            IoBufsInner::Chunked(bufs) => {
1420                copy_to_bytes_chunked(bufs, len, "IoBufs::copy_to_bytes: not enough data")
1421            }
1422        };
1423
1424        if needs_canonicalize {
1425            self.canonicalize();
1426        }
1427
1428        result
1429    }
1430}
1431
1432impl From<IoBuf> for IoBufs {
1433    fn from(buf: IoBuf) -> Self {
1434        Self {
1435            inner: IoBufsInner::Single(buf),
1436        }
1437    }
1438}
1439
1440impl From<IoBufMut> for IoBufs {
1441    fn from(buf: IoBufMut) -> Self {
1442        Self {
1443            inner: IoBufsInner::Single(buf.freeze()),
1444        }
1445    }
1446}
1447
1448impl From<Bytes> for IoBufs {
1449    fn from(bytes: Bytes) -> Self {
1450        Self::from(IoBuf::from(bytes))
1451    }
1452}
1453
1454impl From<BytesMut> for IoBufs {
1455    fn from(bytes: BytesMut) -> Self {
1456        Self::from(IoBuf::from(bytes.freeze()))
1457    }
1458}
1459
1460impl From<Vec<u8>> for IoBufs {
1461    fn from(vec: Vec<u8>) -> Self {
1462        Self::from(IoBuf::from(vec))
1463    }
1464}
1465
1466impl From<Vec<IoBuf>> for IoBufs {
1467    fn from(bufs: Vec<IoBuf>) -> Self {
1468        Self::from_chunks_iter(bufs)
1469    }
1470}
1471
1472impl<const N: usize> From<&'static [u8; N]> for IoBufs {
1473    fn from(array: &'static [u8; N]) -> Self {
1474        Self::from(IoBuf::from(array))
1475    }
1476}
1477
1478impl From<&'static [u8]> for IoBufs {
1479    fn from(slice: &'static [u8]) -> Self {
1480        Self::from(IoBuf::from(slice))
1481    }
1482}
1483
1484/// Container for one or more mutable buffers.
1485#[derive(Debug)]
1486pub struct IoBufsMut {
1487    inner: IoBufsMutInner,
1488}
1489
1490/// Internal mutable representation.
1491///
1492/// - Construction from caller-provided writable chunks keeps chunks with
1493///   non-zero capacity, even when `remaining() == 0`.
1494/// - Read-canonicalization paths remove drained chunks (`remaining() == 0`)
1495///   and collapse shape as readable chunk count shrinks.
1496#[derive(Debug)]
1497enum IoBufsMutInner {
1498    /// Single buffer (common case, no allocation).
1499    Single(IoBufMut),
1500    /// Two buffers (fast path, no VecDeque allocation).
1501    Pair([IoBufMut; 2]),
1502    /// Three buffers (fast path, no VecDeque allocation).
1503    Triple([IoBufMut; 3]),
1504    /// Four or more buffers.
1505    Chunked(VecDeque<IoBufMut>),
1506}
1507
1508impl Default for IoBufsMut {
1509    fn default() -> Self {
1510        Self {
1511            inner: IoBufsMutInner::Single(IoBufMut::default()),
1512        }
1513    }
1514}
1515
1516impl IoBufsMut {
1517    /// Build mutable chunk storage from already-filtered chunks.
1518    ///
1519    /// This helper intentionally does not filter.
1520    /// Callers choose filter policy first:
1521    /// - [`Self::from_writable_chunks_iter`] for construction from writable chunks (`capacity() > 0`)
1522    /// - [`Self::from_readable_chunks_iter`] for read-canonicalization (`remaining() > 0`)
1523    fn from_chunks_iter(chunks: impl IntoIterator<Item = IoBufMut>) -> Self {
1524        let mut iter = chunks.into_iter();
1525        let first = match iter.next() {
1526            Some(first) => first,
1527            None => return Self::default(),
1528        };
1529        let second = match iter.next() {
1530            Some(second) => second,
1531            None => {
1532                return Self {
1533                    inner: IoBufsMutInner::Single(first),
1534                };
1535            }
1536        };
1537        let third = match iter.next() {
1538            Some(third) => third,
1539            None => {
1540                return Self {
1541                    inner: IoBufsMutInner::Pair([first, second]),
1542                };
1543            }
1544        };
1545        let fourth = match iter.next() {
1546            Some(fourth) => fourth,
1547            None => {
1548                return Self {
1549                    inner: IoBufsMutInner::Triple([first, second, third]),
1550                };
1551            }
1552        };
1553
1554        let mut bufs = VecDeque::with_capacity(4);
1555        bufs.push_back(first);
1556        bufs.push_back(second);
1557        bufs.push_back(third);
1558        bufs.push_back(fourth);
1559        bufs.extend(iter);
1560        Self {
1561            inner: IoBufsMutInner::Chunked(bufs),
1562        }
1563    }
1564
1565    /// Build canonical mutable chunk storage from writable chunks.
1566    ///
1567    /// Chunks with zero capacity are removed.
1568    fn from_writable_chunks_iter(chunks: impl IntoIterator<Item = IoBufMut>) -> Self {
1569        // Keep chunks that can hold data (including len == 0 writable buffers).
1570        Self::from_chunks_iter(chunks.into_iter().filter(|buf| buf.capacity() > 0))
1571    }
1572
1573    /// Build canonical mutable chunk storage from readable chunks.
1574    ///
1575    /// Chunks with no remaining readable bytes are removed.
1576    fn from_readable_chunks_iter(chunks: impl IntoIterator<Item = IoBufMut>) -> Self {
1577        Self::from_chunks_iter(chunks.into_iter().filter(|buf| buf.remaining() > 0))
1578    }
1579
1580    /// Re-establish canonical mutable representation invariants.
1581    fn canonicalize(&mut self) {
1582        let inner = std::mem::replace(&mut self.inner, IoBufsMutInner::Single(IoBufMut::default()));
1583        self.inner = match inner {
1584            IoBufsMutInner::Single(buf) => IoBufsMutInner::Single(buf),
1585            IoBufsMutInner::Pair([a, b]) => Self::from_readable_chunks_iter([a, b]).inner,
1586            IoBufsMutInner::Triple([a, b, c]) => Self::from_readable_chunks_iter([a, b, c]).inner,
1587            IoBufsMutInner::Chunked(bufs) => Self::from_readable_chunks_iter(bufs).inner,
1588        };
1589    }
1590
1591    #[inline]
1592    fn for_each_chunk_mut(&mut self, mut f: impl FnMut(&mut IoBufMut)) {
1593        match &mut self.inner {
1594            IoBufsMutInner::Single(buf) => f(buf),
1595            IoBufsMutInner::Pair(pair) => {
1596                for buf in pair.iter_mut() {
1597                    f(buf);
1598                }
1599            }
1600            IoBufsMutInner::Triple(triple) => {
1601                for buf in triple.iter_mut() {
1602                    f(buf);
1603                }
1604            }
1605            IoBufsMutInner::Chunked(bufs) => {
1606                for buf in bufs.iter_mut() {
1607                    f(buf);
1608                }
1609            }
1610        }
1611    }
1612
1613    /// Returns a reference to the single contiguous buffer, if present.
1614    ///
1615    /// Returns `Some` only when this is currently represented as one chunk.
1616    pub const fn as_single(&self) -> Option<&IoBufMut> {
1617        match &self.inner {
1618            IoBufsMutInner::Single(buf) => Some(buf),
1619            _ => None,
1620        }
1621    }
1622
1623    /// Returns a mutable reference to the single contiguous buffer, if present.
1624    ///
1625    /// Returns `Some` only when this is currently represented as one chunk.
1626    pub const fn as_single_mut(&mut self) -> Option<&mut IoBufMut> {
1627        match &mut self.inner {
1628            IoBufsMutInner::Single(buf) => Some(buf),
1629            _ => None,
1630        }
1631    }
1632
1633    /// Consume this container and return the single buffer if present.
1634    ///
1635    /// Returns `Ok(IoBufMut)` only when readable data is represented as one
1636    /// chunk. Returns `Err(Self)` with the original container otherwise.
1637    #[allow(clippy::result_large_err)]
1638    pub fn try_into_single(self) -> Result<IoBufMut, Self> {
1639        match self.inner {
1640            IoBufsMutInner::Single(buf) => Ok(buf),
1641            inner => Err(Self { inner }),
1642        }
1643    }
1644
1645    /// Number of bytes remaining across all buffers.
1646    #[inline]
1647    pub fn len(&self) -> usize {
1648        self.remaining()
1649    }
1650
1651    /// Whether all buffers are empty.
1652    #[inline]
1653    pub fn is_empty(&self) -> bool {
1654        self.remaining() == 0
1655    }
1656
1657    /// Whether this contains a single contiguous buffer.
1658    ///
1659    /// When true, `chunk()` returns all remaining bytes.
1660    #[inline]
1661    pub const fn is_single(&self) -> bool {
1662        matches!(self.inner, IoBufsMutInner::Single(_))
1663    }
1664
1665    /// Freeze into immutable [`IoBufs`].
1666    pub fn freeze(self) -> IoBufs {
1667        match self.inner {
1668            IoBufsMutInner::Single(buf) => IoBufs::from(buf.freeze()),
1669            IoBufsMutInner::Pair([a, b]) => IoBufs::from_chunks_iter([a.freeze(), b.freeze()]),
1670            IoBufsMutInner::Triple([a, b, c]) => {
1671                IoBufs::from_chunks_iter([a.freeze(), b.freeze(), c.freeze()])
1672            }
1673            IoBufsMutInner::Chunked(bufs) => {
1674                IoBufs::from_chunks_iter(bufs.into_iter().map(IoBufMut::freeze))
1675            }
1676        }
1677    }
1678
1679    fn coalesce_with<F>(self, allocate: F) -> IoBufMut
1680    where
1681        F: FnOnce(usize) -> IoBufMut,
1682    {
1683        match self.inner {
1684            IoBufsMutInner::Single(buf) => buf,
1685            IoBufsMutInner::Pair([a, b]) => {
1686                let total_len = a.len().saturating_add(b.len());
1687                let mut result = allocate(total_len);
1688                result.put_slice(a.as_ref());
1689                result.put_slice(b.as_ref());
1690                result
1691            }
1692            IoBufsMutInner::Triple([a, b, c]) => {
1693                let total_len = a.len().saturating_add(b.len()).saturating_add(c.len());
1694                let mut result = allocate(total_len);
1695                result.put_slice(a.as_ref());
1696                result.put_slice(b.as_ref());
1697                result.put_slice(c.as_ref());
1698                result
1699            }
1700            IoBufsMutInner::Chunked(bufs) => {
1701                let total_len: usize = bufs.iter().map(|b| b.len()).fold(0, usize::saturating_add);
1702                let mut result = allocate(total_len);
1703                for buf in bufs {
1704                    result.put_slice(buf.as_ref());
1705                }
1706                result
1707            }
1708        }
1709    }
1710
1711    /// Coalesce all buffers into a single contiguous [`IoBufMut`].
1712    ///
1713    /// Zero-copy if only one buffer. Copies if multiple buffers.
1714    pub fn coalesce(self) -> IoBufMut {
1715        self.coalesce_with(IoBufMut::with_capacity)
1716    }
1717
1718    /// Coalesce all buffers into a single contiguous [`IoBufMut`], using the pool
1719    /// for allocation if multiple buffers need to be merged.
1720    ///
1721    /// Zero-copy if only one buffer. Uses pool allocation if multiple buffers.
1722    pub fn coalesce_with_pool(self, pool: &BufferPool) -> IoBufMut {
1723        self.coalesce_with(|len| pool.alloc(len))
1724    }
1725
1726    /// Coalesce all buffers into a single contiguous [`IoBufMut`] with extra
1727    /// capacity, using the pool for allocation.
1728    ///
1729    /// Zero-copy if single buffer with sufficient spare capacity.
1730    pub fn coalesce_with_pool_extra(self, pool: &BufferPool, extra: usize) -> IoBufMut {
1731        match self.inner {
1732            IoBufsMutInner::Single(buf) if buf.capacity() - buf.len() >= extra => buf,
1733            IoBufsMutInner::Single(buf) => {
1734                let mut result = pool.alloc(buf.len() + extra);
1735                result.put_slice(buf.as_ref());
1736                result
1737            }
1738            IoBufsMutInner::Pair([a, b]) => {
1739                let total = a.len().saturating_add(b.len());
1740                let mut result = pool.alloc(total + extra);
1741                result.put_slice(a.as_ref());
1742                result.put_slice(b.as_ref());
1743                result
1744            }
1745            IoBufsMutInner::Triple([a, b, c]) => {
1746                let total = a.len().saturating_add(b.len()).saturating_add(c.len());
1747                let mut result = pool.alloc(total + extra);
1748                result.put_slice(a.as_ref());
1749                result.put_slice(b.as_ref());
1750                result.put_slice(c.as_ref());
1751                result
1752            }
1753            IoBufsMutInner::Chunked(bufs) => {
1754                let total: usize = bufs.iter().map(|b| b.len()).fold(0, usize::saturating_add);
1755                let mut result = pool.alloc(total + extra);
1756                for buf in bufs {
1757                    result.put_slice(buf.as_ref());
1758                }
1759                result
1760            }
1761        }
1762    }
1763
1764    /// Returns the total capacity across all buffers.
1765    pub fn capacity(&self) -> usize {
1766        match &self.inner {
1767            IoBufsMutInner::Single(buf) => buf.capacity(),
1768            IoBufsMutInner::Pair([a, b]) => a.capacity().saturating_add(b.capacity()),
1769            IoBufsMutInner::Triple([a, b, c]) => a
1770                .capacity()
1771                .saturating_add(b.capacity())
1772                .saturating_add(c.capacity()),
1773            IoBufsMutInner::Chunked(bufs) => bufs
1774                .iter()
1775                .map(|b| b.capacity())
1776                .fold(0, usize::saturating_add),
1777        }
1778    }
1779
1780    /// Sets the length of the buffer(s) to `len`, distributing across chunks
1781    /// while preserving the current chunk layout.
1782    ///
1783    /// This is useful for APIs that must fill caller-provided buffer structure
1784    /// in place (for example [`Blob::read_at_buf`](crate::Blob::read_at_buf)).
1785    ///
1786    /// # Safety
1787    ///
1788    /// Caller must initialize all `len` bytes before the buffer is read.
1789    ///
1790    /// # Panics
1791    ///
1792    /// Panics if `len` exceeds total capacity.
1793    pub(crate) unsafe fn set_len(&mut self, len: usize) {
1794        let capacity = self.capacity();
1795        assert!(
1796            len <= capacity,
1797            "set_len({len}) exceeds capacity({capacity})"
1798        );
1799        let mut remaining = len;
1800        self.for_each_chunk_mut(|buf| {
1801            let cap = buf.capacity();
1802            let to_set = remaining.min(cap);
1803            buf.set_len(to_set);
1804            remaining -= to_set;
1805        });
1806    }
1807
1808    /// Copy data from a slice into the buffers.
1809    ///
1810    /// Panics if the slice length doesn't match the total buffer length.
1811    pub fn copy_from_slice(&mut self, src: &[u8]) {
1812        assert_eq!(
1813            src.len(),
1814            self.len(),
1815            "source slice length must match buffer length"
1816        );
1817        let mut offset = 0;
1818        self.for_each_chunk_mut(|buf| {
1819            let len = buf.len();
1820            buf.as_mut().copy_from_slice(&src[offset..offset + len]);
1821            offset += len;
1822        });
1823    }
1824}
1825
1826impl Buf for IoBufsMut {
1827    fn remaining(&self) -> usize {
1828        match &self.inner {
1829            IoBufsMutInner::Single(buf) => buf.remaining(),
1830            IoBufsMutInner::Pair([a, b]) => a.remaining().saturating_add(b.remaining()),
1831            IoBufsMutInner::Triple([a, b, c]) => a
1832                .remaining()
1833                .saturating_add(b.remaining())
1834                .saturating_add(c.remaining()),
1835            IoBufsMutInner::Chunked(bufs) => bufs
1836                .iter()
1837                .map(|b| b.remaining())
1838                .fold(0, usize::saturating_add),
1839        }
1840    }
1841
1842    fn chunk(&self) -> &[u8] {
1843        match &self.inner {
1844            IoBufsMutInner::Single(buf) => buf.chunk(),
1845            IoBufsMutInner::Pair([a, b]) => {
1846                if a.remaining() > 0 {
1847                    a.chunk()
1848                } else if b.remaining() > 0 {
1849                    b.chunk()
1850                } else {
1851                    &[]
1852                }
1853            }
1854            IoBufsMutInner::Triple([a, b, c]) => {
1855                if a.remaining() > 0 {
1856                    a.chunk()
1857                } else if b.remaining() > 0 {
1858                    b.chunk()
1859                } else if c.remaining() > 0 {
1860                    c.chunk()
1861                } else {
1862                    &[]
1863                }
1864            }
1865            IoBufsMutInner::Chunked(bufs) => {
1866                for buf in bufs.iter() {
1867                    if buf.remaining() > 0 {
1868                        return buf.chunk();
1869                    }
1870                }
1871                &[]
1872            }
1873        }
1874    }
1875
1876    fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
1877        if dst.is_empty() {
1878            return 0;
1879        }
1880
1881        match &self.inner {
1882            IoBufsMutInner::Single(buf) => {
1883                let chunk = buf.chunk();
1884                if !chunk.is_empty() {
1885                    dst[0] = IoSlice::new(chunk);
1886                    return 1;
1887                }
1888                0
1889            }
1890            IoBufsMutInner::Pair([a, b]) => fill_vectored_from_chunks(dst, [a.chunk(), b.chunk()]),
1891            IoBufsMutInner::Triple([a, b, c]) => {
1892                fill_vectored_from_chunks(dst, [a.chunk(), b.chunk(), c.chunk()])
1893            }
1894            IoBufsMutInner::Chunked(bufs) => {
1895                fill_vectored_from_chunks(dst, bufs.iter().map(|buf| buf.chunk()))
1896            }
1897        }
1898    }
1899
1900    fn advance(&mut self, cnt: usize) {
1901        let should_canonicalize = match &mut self.inner {
1902            IoBufsMutInner::Single(buf) => {
1903                buf.advance(cnt);
1904                false
1905            }
1906            IoBufsMutInner::Pair(pair) => advance_small_chunks(pair.as_mut_slice(), cnt),
1907            IoBufsMutInner::Triple(triple) => advance_small_chunks(triple.as_mut_slice(), cnt),
1908            IoBufsMutInner::Chunked(bufs) => {
1909                advance_chunked_front(bufs, cnt);
1910                bufs.len() <= 3
1911            }
1912        };
1913
1914        if should_canonicalize {
1915            self.canonicalize();
1916        }
1917    }
1918
1919    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
1920        let (result, needs_canonicalize) = match &mut self.inner {
1921            IoBufsMutInner::Single(buf) => return buf.copy_to_bytes(len),
1922            IoBufsMutInner::Pair(pair) => {
1923                copy_to_bytes_small_chunks(pair, len, "IoBufsMut::copy_to_bytes: not enough data")
1924            }
1925            IoBufsMutInner::Triple(triple) => {
1926                copy_to_bytes_small_chunks(triple, len, "IoBufsMut::copy_to_bytes: not enough data")
1927            }
1928            IoBufsMutInner::Chunked(bufs) => {
1929                copy_to_bytes_chunked(bufs, len, "IoBufsMut::copy_to_bytes: not enough data")
1930            }
1931        };
1932
1933        if needs_canonicalize {
1934            self.canonicalize();
1935        }
1936
1937        result
1938    }
1939}
1940
1941// SAFETY: Delegates to IoBufMut which implements BufMut safely.
1942unsafe impl BufMut for IoBufsMut {
1943    #[inline]
1944    fn remaining_mut(&self) -> usize {
1945        match &self.inner {
1946            IoBufsMutInner::Single(buf) => buf.remaining_mut(),
1947            IoBufsMutInner::Pair([a, b]) => a.remaining_mut().saturating_add(b.remaining_mut()),
1948            IoBufsMutInner::Triple([a, b, c]) => a
1949                .remaining_mut()
1950                .saturating_add(b.remaining_mut())
1951                .saturating_add(c.remaining_mut()),
1952            IoBufsMutInner::Chunked(bufs) => bufs
1953                .iter()
1954                .map(|b| b.remaining_mut())
1955                .fold(0, usize::saturating_add),
1956        }
1957    }
1958
1959    #[inline]
1960    unsafe fn advance_mut(&mut self, cnt: usize) {
1961        match &mut self.inner {
1962            IoBufsMutInner::Single(buf) => buf.advance_mut(cnt),
1963            IoBufsMutInner::Pair(pair) => {
1964                let mut remaining = cnt;
1965                if advance_mut_in_chunks(pair, &mut remaining) {
1966                    return;
1967                }
1968                panic!("cannot advance past end of buffer");
1969            }
1970            IoBufsMutInner::Triple(triple) => {
1971                let mut remaining = cnt;
1972                if advance_mut_in_chunks(triple, &mut remaining) {
1973                    return;
1974                }
1975                panic!("cannot advance past end of buffer");
1976            }
1977            IoBufsMutInner::Chunked(bufs) => {
1978                let mut remaining = cnt;
1979                let (first, second) = bufs.as_mut_slices();
1980                if advance_mut_in_chunks(first, &mut remaining)
1981                    || advance_mut_in_chunks(second, &mut remaining)
1982                {
1983                    return;
1984                }
1985                panic!("cannot advance past end of buffer");
1986            }
1987        }
1988    }
1989
1990    #[inline]
1991    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
1992        match &mut self.inner {
1993            IoBufsMutInner::Single(buf) => buf.chunk_mut(),
1994            IoBufsMutInner::Pair(pair) => {
1995                if pair[0].remaining_mut() > 0 {
1996                    pair[0].chunk_mut()
1997                } else if pair[1].remaining_mut() > 0 {
1998                    pair[1].chunk_mut()
1999                } else {
2000                    bytes::buf::UninitSlice::new(&mut [])
2001                }
2002            }
2003            IoBufsMutInner::Triple(triple) => {
2004                if triple[0].remaining_mut() > 0 {
2005                    triple[0].chunk_mut()
2006                } else if triple[1].remaining_mut() > 0 {
2007                    triple[1].chunk_mut()
2008                } else if triple[2].remaining_mut() > 0 {
2009                    triple[2].chunk_mut()
2010                } else {
2011                    bytes::buf::UninitSlice::new(&mut [])
2012                }
2013            }
2014            IoBufsMutInner::Chunked(bufs) => {
2015                for buf in bufs.iter_mut() {
2016                    if buf.remaining_mut() > 0 {
2017                        return buf.chunk_mut();
2018                    }
2019                }
2020                bytes::buf::UninitSlice::new(&mut [])
2021            }
2022        }
2023    }
2024}
2025
2026impl From<IoBufMut> for IoBufsMut {
2027    fn from(buf: IoBufMut) -> Self {
2028        Self {
2029            inner: IoBufsMutInner::Single(buf),
2030        }
2031    }
2032}
2033
2034impl From<Vec<u8>> for IoBufsMut {
2035    fn from(vec: Vec<u8>) -> Self {
2036        Self {
2037            inner: IoBufsMutInner::Single(IoBufMut::from(vec)),
2038        }
2039    }
2040}
2041
2042impl From<BytesMut> for IoBufsMut {
2043    fn from(bytes: BytesMut) -> Self {
2044        Self {
2045            inner: IoBufsMutInner::Single(IoBufMut::from(bytes)),
2046        }
2047    }
2048}
2049
2050impl From<Vec<IoBufMut>> for IoBufsMut {
2051    fn from(bufs: Vec<IoBufMut>) -> Self {
2052        Self::from_writable_chunks_iter(bufs)
2053    }
2054}
2055
2056impl<const N: usize> From<[u8; N]> for IoBufsMut {
2057    fn from(array: [u8; N]) -> Self {
2058        Self {
2059            inner: IoBufsMutInner::Single(IoBufMut::from(array)),
2060        }
2061    }
2062}
2063
2064/// Drain `len` readable bytes from a small fixed chunk array (`Pair`/`Triple`).
2065///
2066/// Returns drained bytes plus whether the caller should canonicalize afterward.
2067#[inline]
2068fn copy_to_bytes_small_chunks<B: Buf, const N: usize>(
2069    chunks: &mut [B; N],
2070    len: usize,
2071    not_enough_data_msg: &str,
2072) -> (Bytes, bool) {
2073    let total = chunks
2074        .iter()
2075        .map(|buf| buf.remaining())
2076        .fold(0, usize::saturating_add);
2077    assert!(total >= len, "{not_enough_data_msg}");
2078
2079    if chunks[0].remaining() >= len {
2080        let bytes = chunks[0].copy_to_bytes(len);
2081        return (bytes, chunks[0].remaining() == 0);
2082    }
2083
2084    let mut out = BytesMut::with_capacity(len);
2085    let mut remaining = len;
2086    for buf in chunks.iter_mut() {
2087        if remaining == 0 {
2088            break;
2089        }
2090        let to_copy = remaining.min(buf.remaining());
2091        out.extend_from_slice(&buf.chunk()[..to_copy]);
2092        buf.advance(to_copy);
2093        remaining -= to_copy;
2094    }
2095
2096    // Slow path always consumes past chunk 0, so canonicalization is required.
2097    (out.freeze(), true)
2098}
2099
2100/// Drain `len` readable bytes from a deque-backed chunk representation.
2101///
2102/// Returns drained bytes plus whether the caller should canonicalize afterward.
2103#[inline]
2104fn copy_to_bytes_chunked<B: Buf>(
2105    bufs: &mut VecDeque<B>,
2106    len: usize,
2107    not_enough_data_msg: &str,
2108) -> (Bytes, bool) {
2109    while bufs.front().is_some_and(|buf| buf.remaining() == 0) {
2110        bufs.pop_front();
2111    }
2112
2113    if bufs.front().is_none() {
2114        assert_eq!(len, 0, "{not_enough_data_msg}");
2115        return (Bytes::new(), false);
2116    }
2117
2118    if bufs.front().is_some_and(|front| front.remaining() >= len) {
2119        let front = bufs.front_mut().expect("front checked above");
2120        let bytes = front.copy_to_bytes(len);
2121        if front.remaining() == 0 {
2122            bufs.pop_front();
2123        }
2124        return (bytes, bufs.len() <= 3);
2125    }
2126
2127    let total = bufs
2128        .iter()
2129        .map(|buf| buf.remaining())
2130        .fold(0, usize::saturating_add);
2131    assert!(total >= len, "{not_enough_data_msg}");
2132
2133    let mut out = BytesMut::with_capacity(len);
2134    let mut remaining = len;
2135    while remaining > 0 {
2136        let front = bufs
2137            .front_mut()
2138            .expect("remaining > 0 implies non-empty bufs");
2139        let to_copy = remaining.min(front.remaining());
2140        out.extend_from_slice(&front.chunk()[..to_copy]);
2141        front.advance(to_copy);
2142        if front.remaining() == 0 {
2143            bufs.pop_front();
2144        }
2145        remaining -= to_copy;
2146    }
2147
2148    (out.freeze(), bufs.len() <= 3)
2149}
2150
2151/// Advance across a [`VecDeque`] of chunks by consuming from the front.
2152#[inline]
2153fn advance_chunked_front<B: Buf>(bufs: &mut VecDeque<B>, mut cnt: usize) {
2154    while cnt > 0 {
2155        let front = bufs.front_mut().expect("cannot advance past end of buffer");
2156        let avail = front.remaining();
2157        if avail == 0 {
2158            bufs.pop_front();
2159            continue;
2160        }
2161        if cnt < avail {
2162            front.advance(cnt);
2163            break;
2164        }
2165        front.advance(avail);
2166        bufs.pop_front();
2167        cnt -= avail;
2168    }
2169}
2170
2171/// Advance across a small fixed set of chunks (`Pair`/`Triple`).
2172///
2173/// Returns `true` when one or more chunks became (or were) empty, so callers
2174/// can canonicalize once after the operation.
2175#[inline]
2176fn advance_small_chunks<B: Buf>(chunks: &mut [B], mut cnt: usize) -> bool {
2177    let mut idx = 0;
2178    let mut needs_canonicalize = false;
2179
2180    while cnt > 0 {
2181        let chunk = chunks
2182            .get_mut(idx)
2183            .expect("cannot advance past end of buffer");
2184        let avail = chunk.remaining();
2185        if avail == 0 {
2186            idx += 1;
2187            needs_canonicalize = true;
2188            continue;
2189        }
2190        if cnt < avail {
2191            chunk.advance(cnt);
2192            return needs_canonicalize;
2193        }
2194        chunk.advance(avail);
2195        cnt -= avail;
2196        idx += 1;
2197        needs_canonicalize = true;
2198    }
2199
2200    needs_canonicalize
2201}
2202
2203/// Advance writable cursors across `chunks` by up to `*remaining` bytes.
2204///
2205/// Returns `true` when the full request has been satisfied.
2206///
2207/// # Safety
2208///
2209/// Forwards to [`BufMut::advance_mut`], so callers must ensure the advanced
2210/// region has been initialized according to [`BufMut`]'s contract.
2211#[inline]
2212unsafe fn advance_mut_in_chunks<B: BufMut>(chunks: &mut [B], remaining: &mut usize) -> bool {
2213    if *remaining == 0 {
2214        return true;
2215    }
2216
2217    for buf in chunks.iter_mut() {
2218        let avail = buf.chunk_mut().len();
2219        if avail == 0 {
2220            continue;
2221        }
2222        if *remaining <= avail {
2223            // SAFETY: Upheld by this function's safety contract.
2224            unsafe { buf.advance_mut(*remaining) };
2225            *remaining = 0;
2226            return true;
2227        }
2228        // SAFETY: Upheld by this function's safety contract.
2229        unsafe { buf.advance_mut(avail) };
2230        *remaining -= avail;
2231    }
2232    false
2233}
2234
2235/// Fill `dst` with `IoSlice`s built from `chunks`.
2236///
2237/// Empty chunks are skipped. At most `dst.len()` slices are written.
2238/// Returns the number of slices written.
2239#[inline]
2240fn fill_vectored_from_chunks<'a, I>(dst: &mut [IoSlice<'a>], chunks: I) -> usize
2241where
2242    I: IntoIterator<Item = &'a [u8]>,
2243{
2244    let mut written = 0;
2245    for chunk in chunks
2246        .into_iter()
2247        .filter(|chunk| !chunk.is_empty())
2248        .take(dst.len())
2249    {
2250        dst[written] = IoSlice::new(chunk);
2251        written += 1;
2252    }
2253    written
2254}
2255
2256/// Assembles [`IoBufs`] from a mix of inline writes and zero-copy pieces.
2257///
2258/// All inline writes go into a single pool-backed buffer. [`BufsMut::push`]
2259/// records boundaries without flushing. [`Builder::finish`] freezes the buffer
2260/// once and uses [`IoBuf::slice`] to carve it into pieces at the recorded
2261/// boundaries, interleaved with the pushed [`Bytes`].
2262///
2263/// The inline buffer has a fixed capacity set at construction and will not
2264/// grow. Callers must ensure the capacity accounts for all inline
2265/// (non-pushed) bytes that will be written. Exceeding it will panic.
2266///
2267/// ```text
2268/// builder.put_u16(99);                        // inline
2269/// builder.push(shard_payload.clone());        // zero-copy (Arc clone)
2270/// builder.put_u32(checksum);                  // inline
2271/// let output = builder.finish();
2272///
2273/// // output: [ 99 | --- 1 MB shard --- | checksum ]
2274/// //           pool    Arc clone          pool
2275/// //            \________________________/
2276/// //             slices of one allocation
2277/// ```
2278pub struct Builder {
2279    // Single working buffer for all inline writes.
2280    buf: IoBufMut,
2281    // Each entry is (offset_in_buf, pushed_bytes) recording where a push
2282    // interrupts the inline byte stream.
2283    pushes: Vec<(usize, Bytes)>,
2284}
2285
2286impl Builder {
2287    /// Creates a new builder with a fixed-capacity inline buffer.
2288    ///
2289    /// `capacity` is the minimum number of inline bytes the buffer can hold.
2290    /// The pool may round up to a larger size class. Writing more inline
2291    /// bytes than the allocated capacity will panic.
2292    pub fn new(pool: &BufferPool, capacity: NonZeroUsize) -> Self {
2293        Self {
2294            buf: pool.alloc(capacity.get()),
2295            pushes: Vec::new(),
2296        }
2297    }
2298
2299    /// Freezes the inline buffer and assembles [`IoBufs`] by slicing at
2300    /// the recorded push boundaries.
2301    pub fn finish(self) -> IoBufs {
2302        if self.pushes.is_empty() {
2303            return IoBufs::from(self.buf.freeze());
2304        }
2305
2306        let frozen = self.buf.freeze();
2307        let mut result = IoBufs::default();
2308        let mut pos = 0;
2309
2310        for (offset, pushed) in self.pushes {
2311            if offset > pos {
2312                result.append(frozen.slice(pos..offset));
2313            }
2314            result.append(IoBuf::from(pushed));
2315            pos = offset;
2316        }
2317
2318        if pos < frozen.len() {
2319            result.append(frozen.slice(pos..));
2320        }
2321
2322        result
2323    }
2324}
2325
2326// SAFETY: All methods delegate directly to `self.buf`, a pool-backed
2327// `IoBufMut` with a sound `BufMut` implementation. The inline buffer has
2328// fixed capacity; writes that exceed it will panic via the underlying
2329// `IoBufMut` implementation.
2330unsafe impl BufMut for Builder {
2331    #[inline]
2332    fn remaining_mut(&self) -> usize {
2333        self.buf.remaining_mut()
2334    }
2335
2336    #[inline]
2337    unsafe fn advance_mut(&mut self, cnt: usize) {
2338        self.buf.advance_mut(cnt);
2339    }
2340
2341    #[inline]
2342    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
2343        self.buf.chunk_mut()
2344    }
2345}
2346
2347impl BufsMut for Builder {
2348    fn push(&mut self, bytes: impl Into<Bytes>) {
2349        let bytes = bytes.into();
2350        if !bytes.is_empty() {
2351            self.pushes.push((self.buf.len(), bytes));
2352        }
2353    }
2354}
2355
2356/// Extension trait for encoding values into pooled I/O buffers.
2357///
2358/// This is useful for hot paths that need to avoid frequent heap allocations
2359/// when serializing values that implement [`Write`] and [`EncodeSize`].
2360pub trait EncodeExt: EncodeSize + Write {
2361    /// Encode this value into an [`IoBufMut`] allocated from `pool`.
2362    ///
2363    /// # Panics
2364    ///
2365    /// Panics if [`EncodeSize::encode_size`] does not match the number of
2366    /// bytes written by [`Write::write`].
2367    fn encode_with_pool_mut(&self, pool: &BufferPool) -> IoBufMut {
2368        let len = self.encode_size();
2369        let mut buf = pool.alloc(len);
2370        self.write(&mut buf);
2371        assert_eq!(
2372            buf.len(),
2373            len,
2374            "write() did not write expected bytes into pooled buffer"
2375        );
2376        buf
2377    }
2378
2379    /// Encode into [`IoBufs`] using pool allocation.
2380    ///
2381    /// Override [`Write::write_bufs`] to avoid copying large [`Bytes`] fields.
2382    ///
2383    /// # Panics
2384    ///
2385    /// Panics if [`EncodeSize::encode_inline_size`] underreports the number
2386    /// of inline bytes written by [`Write::write_bufs`], or if
2387    /// [`EncodeSize::encode_size`] does not match the total bytes written.
2388    fn encode_with_pool(&self, pool: &BufferPool) -> IoBufs {
2389        let len = self.encode_size();
2390        let capacity = NonZeroUsize::new(self.encode_inline_size()).unwrap_or(NonZeroUsize::MIN);
2391        let mut builder = Builder::new(pool, capacity);
2392        self.write_bufs(&mut builder);
2393        let bufs = builder.finish();
2394        assert_eq!(
2395            bufs.remaining(),
2396            len,
2397            "write_bufs() did not write expected bytes"
2398        );
2399        bufs
2400    }
2401}
2402
2403impl<T: EncodeSize + Write> EncodeExt for T {}
2404
2405#[cfg(test)]
2406mod tests {
2407    use super::*;
2408    use bytes::{Bytes, BytesMut};
2409    use commonware_codec::{types::lazy::Lazy, Decode, Encode, RangeCfg};
2410    use core::ops::{Range, RangeFrom, RangeInclusive, RangeToInclusive};
2411    use std::collections::{BTreeMap, HashMap};
2412
2413    fn test_pool() -> BufferPool {
2414        cfg_if::cfg_if! {
2415            if #[cfg(miri)] {
2416                // Reduce max_per_class to avoid slow atomics under miri.
2417                let pool_config = BufferPoolConfig {
2418                    pool_min_size: 0,
2419                    max_per_class: commonware_utils::NZU32!(32),
2420                    ..BufferPoolConfig::for_network()
2421                };
2422            } else {
2423                let pool_config = BufferPoolConfig::for_network().with_pool_min_size(0);
2424            }
2425        }
2426        let mut registry = crate::telemetry::metrics::Registry::default();
2427        BufferPool::new(pool_config, &mut registry)
2428    }
2429
2430    fn assert_encode_with_pool_matches_encode<T: Encode + EncodeExt>(value: &T) {
2431        let pool = test_pool();
2432        let mut pooled = value.encode_with_pool(&pool);
2433        let baseline = value.encode();
2434        let mut pooled_bytes = vec![0u8; pooled.remaining()];
2435        pooled.copy_to_slice(&mut pooled_bytes);
2436        assert_eq!(pooled_bytes, baseline.as_ref());
2437    }
2438
2439    #[test]
2440    fn test_iobuf_core_behaviors() {
2441        // Clone stays zero-copy for immutable buffers.
2442        let buf1 = IoBuf::from(vec![1u8; 1000]);
2443        let buf2 = buf1.clone();
2444        assert_eq!(buf1.as_ref().as_ptr(), buf2.as_ref().as_ptr());
2445
2446        // copy_from_slice creates an owned immutable buffer.
2447        let data = vec![1u8, 2, 3, 4, 5];
2448        let copied = IoBuf::copy_from_slice(&data);
2449        assert_eq!(copied, [1, 2, 3, 4, 5]);
2450        assert_eq!(copied.len(), 5);
2451        let empty = IoBuf::copy_from_slice(&[]);
2452        assert!(empty.is_empty());
2453
2454        // Equality works against both arrays and slices.
2455        let eq = IoBuf::from(b"hello");
2456        assert_eq!(eq, *b"hello");
2457        assert_eq!(eq, b"hello");
2458        assert_ne!(eq, *b"world");
2459        assert_ne!(eq, b"world");
2460        assert_eq!(IoBuf::from(b"hello"), IoBuf::from(b"hello"));
2461        assert_ne!(IoBuf::from(b"hello"), IoBuf::from(b"world"));
2462        let bytes: Bytes = IoBuf::from(b"bytes").into();
2463        assert_eq!(bytes.as_ref(), b"bytes");
2464
2465        // Buf trait operations keep `len()` and `remaining()` in sync.
2466        let mut buf = IoBuf::from(b"hello world");
2467        assert_eq!(buf.len(), buf.remaining());
2468        assert_eq!(buf.as_ref(), buf.chunk());
2469        assert_eq!(buf.remaining(), 11);
2470        buf.advance(6);
2471        assert_eq!(buf.chunk(), b"world");
2472        assert_eq!(buf.len(), buf.remaining());
2473
2474        // copy_to_bytes drains in-order and advances the source.
2475        let first = buf.copy_to_bytes(2);
2476        assert_eq!(&first[..], b"wo");
2477        let rest = buf.copy_to_bytes(3);
2478        assert_eq!(&rest[..], b"rld");
2479        assert_eq!(buf.remaining(), 0);
2480
2481        // Slicing remains zero-copy and supports all common range forms.
2482        let src = IoBuf::from(b"hello world");
2483        assert_eq!(src.slice(..5), b"hello");
2484        assert_eq!(src.slice(6..), b"world");
2485        assert_eq!(src.slice(3..8), b"lo wo");
2486        assert!(src.slice(5..5).is_empty());
2487    }
2488
2489    #[test]
2490    fn test_iobuf_codec_roundtrip() {
2491        let cfg: RangeCfg<usize> = (0..=1024).into();
2492
2493        let original = IoBuf::from(b"hello world");
2494        let encoded = original.encode();
2495        let decoded = IoBuf::decode_cfg(encoded, &cfg).unwrap();
2496        assert_eq!(original, decoded);
2497
2498        let empty = IoBuf::default();
2499        let encoded = empty.encode();
2500        let decoded = IoBuf::decode_cfg(encoded, &cfg).unwrap();
2501        assert_eq!(empty, decoded);
2502
2503        let large_cfg: RangeCfg<usize> = (0..=20000).into();
2504        let large = IoBuf::from(vec![42u8; 10000]);
2505        let encoded = large.encode();
2506        let decoded = IoBuf::decode_cfg(encoded, &large_cfg).unwrap();
2507        assert_eq!(large, decoded);
2508
2509        let mut truncated = BytesMut::new();
2510        4usize.write(&mut truncated);
2511        truncated.extend_from_slice(b"xy");
2512        let mut truncated = truncated.freeze();
2513        assert!(IoBuf::read_cfg(&mut truncated, &cfg).is_err());
2514
2515        // Directly exercise the successful `read_cfg` path, not just decode helpers.
2516        let mut direct = BytesMut::new();
2517        4usize.write(&mut direct);
2518        direct.extend_from_slice(b"wxyz");
2519        let mut direct = direct.freeze();
2520        let decoded = IoBuf::read_cfg(&mut direct, &cfg).unwrap();
2521        assert_eq!(decoded, b"wxyz");
2522    }
2523
2524    #[test]
2525    #[should_panic(expected = "cannot advance")]
2526    fn test_iobuf_advance_past_end() {
2527        let mut buf = IoBuf::from(b"hello");
2528        buf.advance(10);
2529    }
2530
2531    #[test]
2532    fn test_iobuf_split_to_consistent_across_backings() {
2533        // split_to on pooled and Bytes-backed IoBufs should produce identical results.
2534        let pool = test_pool();
2535        let mut pooled = pool.try_alloc(256).expect("pooled allocation");
2536        pooled.put_slice(b"hello world");
2537        let mut pooled_buf = pooled.freeze();
2538        let mut bytes_buf = IoBuf::from(b"hello world");
2539
2540        assert!(pooled_buf.is_pooled());
2541        assert!(!bytes_buf.is_pooled());
2542
2543        let pooled_empty = pooled_buf.split_to(0);
2544        let bytes_empty = bytes_buf.split_to(0);
2545        assert_eq!(pooled_empty, bytes_empty);
2546        assert_eq!(pooled_buf, bytes_buf);
2547        assert!(!pooled_empty.is_pooled());
2548
2549        let pooled_prefix = pooled_buf.split_to(5);
2550        let bytes_prefix = bytes_buf.split_to(5);
2551        assert_eq!(pooled_prefix, bytes_prefix);
2552        assert_eq!(pooled_buf, bytes_buf);
2553        assert!(pooled_prefix.is_pooled());
2554
2555        let pooled_rest = pooled_buf.split_to(pooled_buf.len());
2556        let bytes_rest = bytes_buf.split_to(bytes_buf.len());
2557        assert_eq!(pooled_rest, bytes_rest);
2558        assert_eq!(pooled_buf, bytes_buf);
2559        assert!(pooled_buf.is_empty());
2560        assert!(bytes_buf.is_empty());
2561        assert!(!pooled_buf.is_pooled());
2562    }
2563
2564    #[test]
2565    #[should_panic(expected = "split_to out of bounds")]
2566    fn test_iobuf_split_to_out_of_bounds() {
2567        let mut buf = IoBuf::from(b"abc");
2568        let _ = buf.split_to(4);
2569    }
2570
2571    #[test]
2572    fn test_iobufmut_core_behaviors() {
2573        // Build mutable buffers incrementally and freeze to immutable.
2574        let mut buf = IoBufMut::with_capacity(100);
2575        assert!(buf.capacity() >= 100);
2576        assert_eq!(buf.len(), 0);
2577        buf.put_slice(b"hello");
2578        buf.put_slice(b" world");
2579        assert_eq!(buf, b"hello world");
2580        assert_eq!(buf, &b"hello world"[..]);
2581        assert_eq!(buf.freeze(), b"hello world");
2582
2583        // `zeroed` creates readable initialized bytes; `set_len` can shrink safely.
2584        let mut zeroed = IoBufMut::zeroed(10);
2585        assert_eq!(zeroed, &[0u8; 10]);
2586        // SAFETY: shrinking readable length to initialized region.
2587        unsafe { zeroed.set_len(5) };
2588        assert_eq!(zeroed, &[0u8; 5]);
2589        zeroed.as_mut()[..5].copy_from_slice(b"hello");
2590        assert_eq!(&zeroed.as_ref()[..5], b"hello");
2591        let frozen = zeroed.freeze();
2592        let vec: Vec<u8> = frozen.into();
2593        assert_eq!(&vec[..5], b"hello");
2594
2595        // Exercise pooled branch behavior for `is_empty`.
2596        let pool = test_pool();
2597        let mut pooled = pool.alloc(8);
2598        assert!(pooled.is_empty());
2599        pooled.put_slice(b"x");
2600        assert!(!pooled.is_empty());
2601    }
2602
2603    #[test]
2604    fn test_iobufs_shapes_and_read_paths() {
2605        // Empty construction normalizes to an empty single chunk.
2606        let empty = IoBufs::from(Vec::<u8>::new());
2607        assert!(empty.is_empty());
2608        assert!(empty.is_single());
2609        assert!(empty.as_single().is_some());
2610
2611        // Single-buffer read path.
2612        let mut single = IoBufs::from(b"hello world");
2613        assert!(single.is_single());
2614        assert_eq!(single.chunk(), b"hello world");
2615        single.advance(6);
2616        assert_eq!(single.chunk(), b"world");
2617        assert_eq!(single.copy_to_bytes(5).as_ref(), b"world");
2618        assert_eq!(single.remaining(), 0);
2619
2620        // Fast-path shapes (Pair/Triple/Chunked).
2621        let mut pair = IoBufs::from(IoBuf::from(b"a"));
2622        pair.append(IoBuf::from(b"b"));
2623        assert!(matches!(pair.inner, IoBufsInner::Pair(_)));
2624        assert!(pair.as_single().is_none());
2625
2626        let mut triple = IoBufs::from(IoBuf::from(b"a"));
2627        triple.append(IoBuf::from(b"b"));
2628        triple.append(IoBuf::from(b"c"));
2629        assert!(matches!(triple.inner, IoBufsInner::Triple(_)));
2630
2631        let mut chunked = IoBufs::from(IoBuf::from(b"a"));
2632        chunked.append(IoBuf::from(b"b"));
2633        chunked.append(IoBuf::from(b"c"));
2634        chunked.append(IoBuf::from(b"d"));
2635        assert!(matches!(chunked.inner, IoBufsInner::Chunked(_)));
2636
2637        // prepend + append preserve ordering.
2638        let mut joined = IoBufs::from(b"middle");
2639        joined.prepend(IoBuf::from(b"start "));
2640        joined.append(IoBuf::from(b" end"));
2641        assert_eq!(joined.coalesce(), b"start middle end");
2642
2643        // prepending empty is a no-op, and prepending into pair upgrades to triple.
2644        let mut prepend_noop = IoBufs::from(b"x");
2645        prepend_noop.prepend(IoBuf::default());
2646        assert_eq!(prepend_noop.coalesce(), b"x");
2647
2648        // Prepending into an empty aggregate should stay on the single-buffer fast path.
2649        let mut prepend_into_empty = IoBufs::default();
2650        prepend_into_empty.prepend(IoBuf::from(b"z"));
2651        assert!(prepend_into_empty.is_single());
2652        assert_eq!(prepend_into_empty.coalesce(), b"z");
2653
2654        let mut prepend_pair = IoBufs::from(vec![IoBuf::from(b"b"), IoBuf::from(b"c")]);
2655        prepend_pair.prepend(IoBuf::from(b"a"));
2656        assert!(matches!(prepend_pair.inner, IoBufsInner::Triple(_)));
2657        assert_eq!(prepend_pair.coalesce(), b"abc");
2658
2659        // canonicalizing a non-empty single should keep the same representation.
2660        let mut canonical_single = IoBufs::from(b"q");
2661        canonical_single.canonicalize();
2662        assert!(canonical_single.is_single());
2663        assert_eq!(canonical_single.coalesce(), b"q");
2664    }
2665
2666    #[test]
2667    fn test_iobufs_split_to_cases() {
2668        // Zero and full split on a single chunk.
2669        let mut bufs = IoBufs::from(b"hello");
2670
2671        let empty = bufs.split_to(0);
2672        assert!(empty.is_empty());
2673        assert_eq!(bufs.coalesce(), b"hello");
2674
2675        let mut bufs = IoBufs::from(b"hello");
2676        let all = bufs.split_to(5);
2677        assert_eq!(all.coalesce(), b"hello");
2678        assert!(bufs.is_single());
2679        assert!(bufs.is_empty());
2680
2681        // Single split in the middle.
2682        let mut single_mid = IoBufs::from(b"hello");
2683        let single_prefix = single_mid.split_to(2);
2684        assert!(single_prefix.is_single());
2685        assert_eq!(single_prefix.coalesce(), b"he");
2686        assert_eq!(single_mid.coalesce(), b"llo");
2687
2688        // Pair split paths: in-first, boundary-after-first, crossing-into-second.
2689        let mut pair = IoBufs::from(vec![IoBuf::from(b"ab"), IoBuf::from(b"cd")]);
2690        let pair_prefix = pair.split_to(1);
2691        assert!(pair_prefix.is_single());
2692        assert_eq!(pair_prefix.coalesce(), b"a");
2693        assert!(matches!(pair.inner, IoBufsInner::Pair(_)));
2694        assert_eq!(pair.coalesce(), b"bcd");
2695
2696        let mut pair = IoBufs::from(vec![IoBuf::from(b"ab"), IoBuf::from(b"cd")]);
2697        let pair_prefix = pair.split_to(2);
2698        assert!(pair_prefix.is_single());
2699        assert_eq!(pair_prefix.coalesce(), b"ab");
2700        assert!(pair.is_single());
2701        assert_eq!(pair.coalesce(), b"cd");
2702
2703        let mut pair = IoBufs::from(vec![IoBuf::from(b"ab"), IoBuf::from(b"cd")]);
2704        let pair_prefix = pair.split_to(3);
2705        assert!(matches!(pair_prefix.inner, IoBufsInner::Pair(_)));
2706        assert_eq!(pair_prefix.coalesce(), b"abc");
2707        assert!(pair.is_single());
2708        assert_eq!(pair.coalesce(), b"d");
2709
2710        // Triple split paths: in-first, boundary-after-first, in-second, boundary-after-second,
2711        // and reaching into third.
2712        let mut triple = IoBufs::from(vec![
2713            IoBuf::from(b"ab"),
2714            IoBuf::from(b"cd"),
2715            IoBuf::from(b"ef"),
2716        ]);
2717        let triple_prefix = triple.split_to(1);
2718        assert!(triple_prefix.is_single());
2719        assert_eq!(triple_prefix.coalesce(), b"a");
2720        assert!(matches!(triple.inner, IoBufsInner::Triple(_)));
2721        assert_eq!(triple.coalesce(), b"bcdef");
2722
2723        let mut triple = IoBufs::from(vec![
2724            IoBuf::from(b"ab"),
2725            IoBuf::from(b"cd"),
2726            IoBuf::from(b"ef"),
2727        ]);
2728        let triple_prefix = triple.split_to(2);
2729        assert!(triple_prefix.is_single());
2730        assert_eq!(triple_prefix.coalesce(), b"ab");
2731        assert!(matches!(triple.inner, IoBufsInner::Pair(_)));
2732        assert_eq!(triple.coalesce(), b"cdef");
2733
2734        let mut triple = IoBufs::from(vec![
2735            IoBuf::from(b"ab"),
2736            IoBuf::from(b"cd"),
2737            IoBuf::from(b"ef"),
2738        ]);
2739        let triple_prefix = triple.split_to(3);
2740        assert!(matches!(triple_prefix.inner, IoBufsInner::Pair(_)));
2741        assert_eq!(triple_prefix.coalesce(), b"abc");
2742        assert!(matches!(triple.inner, IoBufsInner::Pair(_)));
2743        assert_eq!(triple.coalesce(), b"def");
2744
2745        let mut triple = IoBufs::from(vec![
2746            IoBuf::from(b"ab"),
2747            IoBuf::from(b"cd"),
2748            IoBuf::from(b"ef"),
2749        ]);
2750        let triple_prefix = triple.split_to(4);
2751        assert!(matches!(triple_prefix.inner, IoBufsInner::Pair(_)));
2752        assert_eq!(triple_prefix.coalesce(), b"abcd");
2753        assert!(triple.is_single());
2754        assert_eq!(triple.coalesce(), b"ef");
2755
2756        let mut triple = IoBufs::from(vec![
2757            IoBuf::from(b"ab"),
2758            IoBuf::from(b"cd"),
2759            IoBuf::from(b"ef"),
2760        ]);
2761        let triple_prefix = triple.split_to(5);
2762        assert!(matches!(triple_prefix.inner, IoBufsInner::Triple(_)));
2763        assert_eq!(triple_prefix.coalesce(), b"abcde");
2764        assert!(triple.is_single());
2765        assert_eq!(triple.coalesce(), b"f");
2766
2767        // Chunked split can canonicalize remainder/prefix shapes.
2768        let mut bufs = IoBufs::from(vec![
2769            IoBuf::from(b"ab"),
2770            IoBuf::from(b"cd"),
2771            IoBuf::from(b"ef"),
2772            IoBuf::from(b"gh"),
2773        ]);
2774        let prefix = bufs.split_to(4);
2775        assert!(matches!(prefix.inner, IoBufsInner::Pair(_)));
2776        assert_eq!(prefix.coalesce(), b"abcd");
2777        assert!(matches!(bufs.inner, IoBufsInner::Pair(_)));
2778        assert_eq!(bufs.coalesce(), b"efgh");
2779
2780        // Chunked split inside a chunk.
2781        let mut bufs = IoBufs::from(vec![
2782            IoBuf::from(b"ab"),
2783            IoBuf::from(b"cd"),
2784            IoBuf::from(b"ef"),
2785            IoBuf::from(b"gh"),
2786        ]);
2787        let prefix = bufs.split_to(5);
2788        assert!(matches!(prefix.inner, IoBufsInner::Triple(_)));
2789        assert_eq!(prefix.coalesce(), b"abcde");
2790        assert!(matches!(bufs.inner, IoBufsInner::Pair(_)));
2791        assert_eq!(bufs.coalesce(), b"fgh");
2792
2793        // Chunked split can remain chunked on both sides when both have >= 4 chunks.
2794        let mut bufs = IoBufs::from(vec![
2795            IoBuf::from(b"a"),
2796            IoBuf::from(b"b"),
2797            IoBuf::from(b"c"),
2798            IoBuf::from(b"d"),
2799            IoBuf::from(b"e"),
2800            IoBuf::from(b"f"),
2801            IoBuf::from(b"g"),
2802            IoBuf::from(b"h"),
2803        ]);
2804        let prefix = bufs.split_to(4);
2805        assert!(matches!(prefix.inner, IoBufsInner::Chunked(_)));
2806        assert_eq!(prefix.coalesce(), b"abcd");
2807        assert!(matches!(bufs.inner, IoBufsInner::Chunked(_)));
2808        assert_eq!(bufs.coalesce(), b"efgh");
2809
2810        // Defensive path: tolerate accidental empty chunks in non-canonical chunked input.
2811        let mut bufs = IoBufs {
2812            inner: IoBufsInner::Chunked(VecDeque::from([
2813                IoBuf::default(),
2814                IoBuf::from(b"ab"),
2815                IoBuf::from(b"cd"),
2816                IoBuf::from(b"ef"),
2817                IoBuf::from(b"gh"),
2818            ])),
2819        };
2820        let prefix = bufs.split_to(3);
2821        assert_eq!(prefix.coalesce(), b"abc");
2822        assert_eq!(bufs.coalesce(), b"defgh");
2823    }
2824
2825    #[test]
2826    #[should_panic(expected = "split_to out of bounds")]
2827    fn test_iobufs_split_to_out_of_bounds() {
2828        let mut bufs = IoBufs::from(b"abc");
2829        let _ = bufs.split_to(4);
2830    }
2831
2832    #[test]
2833    fn test_iobufs_chunk_count() {
2834        assert_eq!(IoBufs::default().chunk_count(), 0);
2835        assert_eq!(IoBufs::from(IoBuf::from(b"a")).chunk_count(), 1);
2836        assert_eq!(
2837            IoBufs::from(vec![IoBuf::from(b"b"), IoBuf::from(b"c")]).chunk_count(),
2838            2
2839        );
2840        assert_eq!(
2841            IoBufs::from(vec![
2842                IoBuf::from(b"a"),
2843                IoBuf::from(b"b"),
2844                IoBuf::from(b"c")
2845            ])
2846            .chunk_count(),
2847            3
2848        );
2849        assert_eq!(
2850            IoBufs::from(vec![
2851                IoBuf::from(b"a"),
2852                IoBuf::from(b"b"),
2853                IoBuf::from(b"c"),
2854                IoBuf::from(b"d")
2855            ])
2856            .chunk_count(),
2857            4
2858        );
2859    }
2860
2861    #[test]
2862    fn test_iobufs_coalesce_after_advance() {
2863        let mut bufs = IoBufs::from(IoBuf::from(b"hello"));
2864        bufs.append(IoBuf::from(b" world"));
2865
2866        assert_eq!(bufs.len(), 11);
2867
2868        bufs.advance(3);
2869        assert_eq!(bufs.len(), 8);
2870
2871        assert_eq!(bufs.coalesce(), b"lo world");
2872    }
2873
2874    #[test]
2875    fn test_iobufs_coalesce_with_pool() {
2876        let pool = test_pool();
2877
2878        // Single buffer: zero-copy (same pointer)
2879        let buf = IoBuf::from(vec![1u8, 2, 3, 4, 5]);
2880        let original_ptr = buf.as_ptr();
2881        let bufs = IoBufs::from(buf);
2882        let coalesced = bufs.coalesce_with_pool(&pool);
2883        assert_eq!(coalesced, [1, 2, 3, 4, 5]);
2884        assert_eq!(coalesced.as_ptr(), original_ptr);
2885
2886        // Multiple buffers: merged using pool
2887        let mut bufs = IoBufs::from(IoBuf::from(b"hello"));
2888        bufs.append(IoBuf::from(b" world"));
2889        let coalesced = bufs.coalesce_with_pool(&pool);
2890        assert_eq!(coalesced, b"hello world");
2891
2892        // Multiple buffers after advance: only remaining data coalesced
2893        let mut bufs = IoBufs::from(IoBuf::from(b"hello"));
2894        bufs.append(IoBuf::from(b" world"));
2895        bufs.advance(3);
2896        let coalesced = bufs.coalesce_with_pool(&pool);
2897        assert_eq!(coalesced, b"lo world");
2898
2899        // Empty buffers in the middle
2900        let mut bufs = IoBufs::from(IoBuf::from(b"hello"));
2901        bufs.append(IoBuf::default());
2902        bufs.append(IoBuf::from(b" world"));
2903        let coalesced = bufs.coalesce_with_pool(&pool);
2904        assert_eq!(coalesced, b"hello world");
2905
2906        // Empty IoBufs
2907        let bufs = IoBufs::default();
2908        let coalesced = bufs.coalesce_with_pool(&pool);
2909        assert!(coalesced.is_empty());
2910
2911        // 4+ buffers: exercise chunked coalesce-with-pool path.
2912        let bufs = IoBufs::from(vec![
2913            IoBuf::from(b"ab"),
2914            IoBuf::from(b"cd"),
2915            IoBuf::from(b"ef"),
2916            IoBuf::from(b"gh"),
2917        ]);
2918        let coalesced = bufs.coalesce_with_pool(&pool);
2919        assert_eq!(coalesced, b"abcdefgh");
2920        assert!(coalesced.is_pooled());
2921    }
2922
2923    #[test]
2924    fn test_iobufs_empty_chunks_and_copy_to_bytes_paths() {
2925        // Empty chunks are skipped while reading across multiple chunks.
2926        let mut bufs = IoBufs::default();
2927        bufs.append(IoBuf::from(b"hello"));
2928        bufs.append(IoBuf::default());
2929        bufs.append(IoBuf::from(b" "));
2930        bufs.append(IoBuf::default());
2931        bufs.append(IoBuf::from(b"world"));
2932        assert_eq!(bufs.len(), 11);
2933        assert_eq!(bufs.chunk(), b"hello");
2934        bufs.advance(5);
2935        assert_eq!(bufs.chunk(), b" ");
2936        bufs.advance(1);
2937        assert_eq!(bufs.chunk(), b"world");
2938
2939        // Single-buffer copy_to_bytes path.
2940        let mut single = IoBufs::from(b"hello world");
2941        assert_eq!(single.copy_to_bytes(5).as_ref(), b"hello");
2942        assert_eq!(single.remaining(), 6);
2943
2944        // Multi-buffer copy_to_bytes path across boundaries.
2945        let mut multi = IoBufs::from(b"hello");
2946        multi.prepend(IoBuf::from(b"say "));
2947        assert_eq!(multi.copy_to_bytes(7).as_ref(), b"say hel");
2948        assert_eq!(multi.copy_to_bytes(2).as_ref(), b"lo");
2949    }
2950
2951    #[test]
2952    fn test_iobufs_copy_to_bytes_pair_and_triple() {
2953        // Pair: crossing one boundary should collapse to the trailing single chunk.
2954        let mut pair = IoBufs::from(IoBuf::from(b"ab"));
2955        pair.append(IoBuf::from(b"cd"));
2956        let first = pair.copy_to_bytes(3);
2957        assert_eq!(&first[..], b"abc");
2958        assert!(pair.is_single());
2959        assert_eq!(pair.chunk(), b"d");
2960
2961        // Triple: draining across two chunks leaves the final chunk readable.
2962        let mut triple = IoBufs::from(IoBuf::from(b"ab"));
2963        triple.append(IoBuf::from(b"cd"));
2964        triple.append(IoBuf::from(b"ef"));
2965        let first = triple.copy_to_bytes(5);
2966        assert_eq!(&first[..], b"abcde");
2967        assert!(triple.is_single());
2968        assert_eq!(triple.chunk(), b"f");
2969    }
2970
2971    #[test]
2972    fn test_iobufs_copy_to_bytes_chunked_four_plus() {
2973        let mut bufs = IoBufs::from(vec![
2974            IoBuf::from(b"ab"),
2975            IoBuf::from(b"cd"),
2976            IoBuf::from(b"ef"),
2977            IoBuf::from(b"gh"),
2978        ]);
2979
2980        // Chunked fast-path: first chunk alone satisfies request.
2981        let first = bufs.copy_to_bytes(1);
2982        assert_eq!(&first[..], b"a");
2983
2984        // Chunked slow-path: request crosses chunk boundaries.
2985        let second = bufs.copy_to_bytes(4);
2986        assert_eq!(&second[..], b"bcde");
2987
2988        let rest = bufs.copy_to_bytes(3);
2989        assert_eq!(&rest[..], b"fgh");
2990        assert_eq!(bufs.remaining(), 0);
2991    }
2992
2993    #[test]
2994    fn test_iobufs_copy_to_bytes_edge_cases() {
2995        // Leading empty chunk should not affect copied payload.
2996        let mut iobufs = IoBufs::from(IoBuf::from(b""));
2997        iobufs.append(IoBuf::from(b"hello"));
2998        assert_eq!(iobufs.copy_to_bytes(5).as_ref(), b"hello");
2999
3000        // Boundary-aligned reads should return exact chunk payloads in-order.
3001        let mut boundary = IoBufs::from(IoBuf::from(b"hello"));
3002        boundary.append(IoBuf::from(b"world"));
3003        assert_eq!(boundary.copy_to_bytes(5).as_ref(), b"hello");
3004        assert_eq!(boundary.copy_to_bytes(5).as_ref(), b"world");
3005        assert_eq!(boundary.remaining(), 0);
3006    }
3007
3008    #[test]
3009    #[should_panic(expected = "cannot advance past end of buffer")]
3010    fn test_iobufs_advance_past_end() {
3011        let mut bufs = IoBufs::from(b"hel");
3012        bufs.append(IoBuf::from(b"lo"));
3013        bufs.advance(10);
3014    }
3015
3016    #[test]
3017    #[should_panic(expected = "not enough data")]
3018    fn test_iobufs_copy_to_bytes_past_end() {
3019        let mut bufs = IoBufs::from(b"hel");
3020        bufs.append(IoBuf::from(b"lo"));
3021        bufs.copy_to_bytes(10);
3022    }
3023
3024    #[test]
3025    fn test_iobufs_matches_bytes_chain() {
3026        let b1 = Bytes::from_static(b"hello");
3027        let b2 = Bytes::from_static(b" ");
3028        let b3 = Bytes::from_static(b"world");
3029
3030        // Buf parity for remaining/chunk/advance should match `Bytes::chain`.
3031        let mut chain = b1.clone().chain(b2.clone()).chain(b3.clone());
3032        let mut iobufs = IoBufs::from(IoBuf::from(b1.clone()));
3033        iobufs.append(IoBuf::from(b2.clone()));
3034        iobufs.append(IoBuf::from(b3.clone()));
3035
3036        assert_eq!(chain.remaining(), iobufs.remaining());
3037        assert_eq!(chain.chunk(), iobufs.chunk());
3038
3039        chain.advance(3);
3040        iobufs.advance(3);
3041        assert_eq!(chain.remaining(), iobufs.remaining());
3042        assert_eq!(chain.chunk(), iobufs.chunk());
3043
3044        chain.advance(3);
3045        iobufs.advance(3);
3046        assert_eq!(chain.remaining(), iobufs.remaining());
3047        assert_eq!(chain.chunk(), iobufs.chunk());
3048
3049        // Test copy_to_bytes
3050        let mut chain = b1.clone().chain(b2.clone()).chain(b3.clone());
3051        let mut iobufs = IoBufs::from(IoBuf::from(b1));
3052        iobufs.append(IoBuf::from(b2));
3053        iobufs.append(IoBuf::from(b3));
3054
3055        assert_eq!(chain.copy_to_bytes(3), iobufs.copy_to_bytes(3));
3056        assert_eq!(chain.copy_to_bytes(4), iobufs.copy_to_bytes(4));
3057        assert_eq!(
3058            chain.copy_to_bytes(chain.remaining()),
3059            iobufs.copy_to_bytes(iobufs.remaining())
3060        );
3061        assert_eq!(chain.remaining(), 0);
3062        assert_eq!(iobufs.remaining(), 0);
3063    }
3064
3065    #[test]
3066    fn test_iobufs_try_into_single() {
3067        let single = IoBufs::from(IoBuf::from(b"hello"));
3068        let single = single.try_into_single().expect("single expected");
3069        assert_eq!(single, b"hello");
3070
3071        let multi = IoBufs::from(vec![IoBuf::from(b"ab"), IoBuf::from(b"cd")]);
3072        let multi = multi.try_into_single().expect_err("multi expected");
3073        assert_eq!(multi.coalesce(), b"abcd");
3074    }
3075
3076    #[test]
3077    fn test_iobufs_chunks_vectored_multiple_slices() {
3078        // Single non-empty buffers should export exactly one slice.
3079        let single = IoBufs::from(IoBuf::from(b"xy"));
3080        let mut single_dst = [IoSlice::new(&[]); 2];
3081        let count = single.chunks_vectored(&mut single_dst);
3082        assert_eq!(count, 1);
3083        assert_eq!(&single_dst[0][..], b"xy");
3084
3085        // Single empty buffers should export no slices.
3086        let empty_single = IoBufs::default();
3087        let mut empty_single_dst = [IoSlice::new(&[]); 1];
3088        assert_eq!(empty_single.chunks_vectored(&mut empty_single_dst), 0);
3089
3090        let bufs = IoBufs::from(vec![
3091            IoBuf::from(b"ab"),
3092            IoBuf::from(b"cd"),
3093            IoBuf::from(b"ef"),
3094            IoBuf::from(b"gh"),
3095        ]);
3096
3097        // Destination capacity should cap how many chunks we export.
3098        let mut small = [IoSlice::new(&[]); 2];
3099        let count = bufs.chunks_vectored(&mut small);
3100        assert_eq!(count, 2);
3101        assert_eq!(&small[0][..], b"ab");
3102        assert_eq!(&small[1][..], b"cd");
3103
3104        // Larger destination should include every readable chunk.
3105        let mut large = [IoSlice::new(&[]); 8];
3106        let count = bufs.chunks_vectored(&mut large);
3107        assert_eq!(count, 4);
3108        assert_eq!(&large[0][..], b"ab");
3109        assert_eq!(&large[1][..], b"cd");
3110        assert_eq!(&large[2][..], b"ef");
3111        assert_eq!(&large[3][..], b"gh");
3112
3113        // Empty destination cannot accept any slices.
3114        let mut empty_dst: [IoSlice<'_>; 0] = [];
3115        assert_eq!(bufs.chunks_vectored(&mut empty_dst), 0);
3116
3117        // Non-canonical shapes should skip empty leading chunks.
3118        let sparse = IoBufs {
3119            inner: IoBufsInner::Pair([IoBuf::default(), IoBuf::from(b"x")]),
3120        };
3121        let mut dst = [IoSlice::new(&[]); 2];
3122        let count = sparse.chunks_vectored(&mut dst);
3123        assert_eq!(count, 1);
3124        assert_eq!(&dst[0][..], b"x");
3125
3126        // Triple should skip empty chunks and preserve readable order.
3127        let sparse_triple = IoBufs {
3128            inner: IoBufsInner::Triple([IoBuf::default(), IoBuf::from(b"y"), IoBuf::from(b"z")]),
3129        };
3130        let mut dst = [IoSlice::new(&[]); 3];
3131        let count = sparse_triple.chunks_vectored(&mut dst);
3132        assert_eq!(count, 2);
3133        assert_eq!(&dst[0][..], b"y");
3134        assert_eq!(&dst[1][..], b"z");
3135
3136        // Chunked shapes with only empty buffers should export no slices.
3137        let empty_chunked = IoBufs {
3138            inner: IoBufsInner::Chunked(VecDeque::from([IoBuf::default(), IoBuf::default()])),
3139        };
3140        let mut dst = [IoSlice::new(&[]); 2];
3141        assert_eq!(empty_chunked.chunks_vectored(&mut dst), 0);
3142    }
3143
3144    #[test]
3145    fn test_iobufsmut_freeze_chunked() {
3146        // Multiple non-empty buffers stay multi-chunk.
3147        let buf1 = IoBufMut::from(b"hello".as_ref());
3148        let buf2 = IoBufMut::from(b" world".as_ref());
3149        let bufs = IoBufsMut::from(vec![buf1, buf2]);
3150        let mut frozen = bufs.freeze();
3151        assert!(!frozen.is_single());
3152        assert_eq!(frozen.chunk(), b"hello");
3153        frozen.advance(5);
3154        assert_eq!(frozen.chunk(), b" world");
3155        frozen.advance(6);
3156        assert_eq!(frozen.remaining(), 0);
3157
3158        // Empty buffers are filtered out.
3159        let buf1 = IoBufMut::from(b"hello".as_ref());
3160        let empty = IoBufMut::default();
3161        let buf2 = IoBufMut::from(b" world".as_ref());
3162        let bufs = IoBufsMut::from(vec![buf1, empty, buf2]);
3163        let mut frozen = bufs.freeze();
3164        assert!(!frozen.is_single());
3165        assert_eq!(frozen.chunk(), b"hello");
3166        frozen.advance(5);
3167        assert_eq!(frozen.chunk(), b" world");
3168        frozen.advance(6);
3169        assert_eq!(frozen.remaining(), 0);
3170
3171        // Collapses to Single when one non-empty buffer remains
3172        let empty1 = IoBufMut::default();
3173        let buf = IoBufMut::from(b"only one".as_ref());
3174        let empty2 = IoBufMut::default();
3175        let bufs = IoBufsMut::from(vec![empty1, buf, empty2]);
3176        let frozen = bufs.freeze();
3177        assert!(frozen.is_single());
3178        assert_eq!(frozen.coalesce(), b"only one");
3179
3180        // All empty buffers -> Single with empty buffer
3181        let empty1 = IoBufMut::default();
3182        let empty2 = IoBufMut::default();
3183        let bufs = IoBufsMut::from(vec![empty1, empty2]);
3184        let frozen = bufs.freeze();
3185        assert!(frozen.is_single());
3186        assert!(frozen.is_empty());
3187    }
3188
3189    #[test]
3190    fn test_iobufsmut_coalesce() {
3191        let buf1 = IoBufMut::from(b"hello");
3192        let buf2 = IoBufMut::from(b" world");
3193        let bufs = IoBufsMut::from(vec![buf1, buf2]);
3194        let coalesced = bufs.coalesce();
3195        assert_eq!(coalesced, b"hello world");
3196    }
3197
3198    #[test]
3199    fn test_iobufsmut_from_vec() {
3200        // Empty Vec becomes Single with empty buffer
3201        let bufs = IoBufsMut::from(Vec::<IoBufMut>::new());
3202        assert!(bufs.is_single());
3203        assert!(bufs.is_empty());
3204
3205        // Vec with one element becomes Single
3206        let buf = IoBufMut::from(b"test");
3207        let bufs = IoBufsMut::from(vec![buf]);
3208        assert!(bufs.is_single());
3209        assert_eq!(bufs.chunk(), b"test");
3210
3211        // Vec with multiple elements becomes multi-chunk.
3212        let buf1 = IoBufMut::from(b"hello");
3213        let buf2 = IoBufMut::from(b" world");
3214        let bufs = IoBufsMut::from(vec![buf1, buf2]);
3215        assert!(!bufs.is_single());
3216    }
3217
3218    #[test]
3219    fn test_iobufsmut_from_vec_filters_empty_chunks() {
3220        let mut bufs = IoBufsMut::from(vec![
3221            IoBufMut::default(),
3222            IoBufMut::from(b"hello"),
3223            IoBufMut::default(),
3224            IoBufMut::from(b" world"),
3225            IoBufMut::default(),
3226        ]);
3227        assert_eq!(bufs.chunk(), b"hello");
3228        bufs.advance(5);
3229        assert_eq!(bufs.chunk(), b" world");
3230        bufs.advance(6);
3231        assert_eq!(bufs.remaining(), 0);
3232    }
3233
3234    #[test]
3235    fn test_iobufsmut_fast_path_shapes() {
3236        let pair = IoBufsMut::from(vec![IoBufMut::from(b"a"), IoBufMut::from(b"b")]);
3237        assert!(matches!(pair.inner, IoBufsMutInner::Pair(_)));
3238
3239        let triple = IoBufsMut::from(vec![
3240            IoBufMut::from(b"a"),
3241            IoBufMut::from(b"b"),
3242            IoBufMut::from(b"c"),
3243        ]);
3244        assert!(matches!(triple.inner, IoBufsMutInner::Triple(_)));
3245
3246        let chunked = IoBufsMut::from(vec![
3247            IoBufMut::from(b"a"),
3248            IoBufMut::from(b"b"),
3249            IoBufMut::from(b"c"),
3250            IoBufMut::from(b"d"),
3251        ]);
3252        assert!(matches!(chunked.inner, IoBufsMutInner::Chunked(_)));
3253    }
3254
3255    #[test]
3256    fn test_iobufsmut_default() {
3257        // Default IoBufsMut should be a single empty chunk.
3258        let bufs = IoBufsMut::default();
3259        assert!(bufs.is_single());
3260        assert!(bufs.is_empty());
3261        assert_eq!(bufs.len(), 0);
3262    }
3263
3264    #[test]
3265    fn test_iobufsmut_from_array() {
3266        // From<[u8; N]> should create a single-chunk container with the array data.
3267        let bufs = IoBufsMut::from([1u8, 2, 3, 4, 5]);
3268        assert!(bufs.is_single());
3269        assert_eq!(bufs.len(), 5);
3270        assert_eq!(bufs.chunk(), &[1, 2, 3, 4, 5]);
3271    }
3272
3273    #[test]
3274    fn test_iobufmut_buf_trait() {
3275        // Buf trait on IoBufMut: remaining/chunk/advance should work like BytesMut.
3276        let mut buf = IoBufMut::from(b"hello world");
3277        assert_eq!(buf.remaining(), 11);
3278        assert_eq!(buf.chunk(), b"hello world");
3279
3280        buf.advance(6);
3281        assert_eq!(buf.remaining(), 5);
3282        assert_eq!(buf.chunk(), b"world");
3283
3284        buf.advance(5);
3285        assert_eq!(buf.remaining(), 0);
3286        assert!(buf.chunk().is_empty());
3287    }
3288
3289    #[test]
3290    #[should_panic(expected = "cannot advance")]
3291    fn test_iobufmut_advance_past_end() {
3292        let mut buf = IoBufMut::from(b"hello");
3293        buf.advance(10);
3294    }
3295
3296    #[test]
3297    fn test_iobufsmut_buf_trait_chunked() {
3298        let buf1 = IoBufMut::from(b"hello");
3299        let buf2 = IoBufMut::from(b" ");
3300        let buf3 = IoBufMut::from(b"world");
3301        let mut bufs = IoBufsMut::from(vec![buf1, buf2, buf3]);
3302
3303        assert_eq!(bufs.remaining(), 11);
3304        assert_eq!(bufs.chunk(), b"hello");
3305
3306        // Advance within first buffer
3307        bufs.advance(3);
3308        assert_eq!(bufs.remaining(), 8);
3309        assert_eq!(bufs.chunk(), b"lo");
3310
3311        // Advance past first buffer (should pop_front)
3312        bufs.advance(2);
3313        assert_eq!(bufs.remaining(), 6);
3314        assert_eq!(bufs.chunk(), b" ");
3315
3316        // Advance exactly one buffer
3317        bufs.advance(1);
3318        assert_eq!(bufs.remaining(), 5);
3319        assert_eq!(bufs.chunk(), b"world");
3320
3321        // Advance to end
3322        bufs.advance(5);
3323        assert_eq!(bufs.remaining(), 0);
3324    }
3325
3326    #[test]
3327    #[should_panic(expected = "cannot advance past end of buffer")]
3328    fn test_iobufsmut_advance_past_end() {
3329        let buf1 = IoBufMut::from(b"hello");
3330        let buf2 = IoBufMut::from(b" world");
3331        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3332        bufs.advance(20);
3333    }
3334
3335    #[test]
3336    fn test_iobufsmut_bufmut_trait_single() {
3337        let mut bufs = IoBufsMut::from(IoBufMut::with_capacity(20));
3338        // BytesMut can grow, so remaining_mut is very large
3339        assert!(bufs.remaining_mut() > 1000);
3340
3341        bufs.put_slice(b"hello");
3342        assert_eq!(bufs.chunk(), b"hello");
3343        assert_eq!(bufs.len(), 5);
3344
3345        bufs.put_slice(b" world");
3346        assert_eq!(bufs.coalesce(), b"hello world");
3347    }
3348
3349    #[test]
3350    fn test_iobufsmut_zeroed_write() {
3351        // Use zeroed buffers which have a fixed length
3352        let bufs = IoBufsMut::from(IoBufMut::zeroed(20));
3353        assert_eq!(bufs.len(), 20);
3354
3355        // Can write using as_mut on coalesced buffer
3356        let mut coalesced = bufs.coalesce();
3357        coalesced.as_mut()[..5].copy_from_slice(b"hello");
3358        assert_eq!(&coalesced.as_ref()[..5], b"hello");
3359    }
3360
3361    #[test]
3362    fn test_iobufsmut_bufmut_put_slice() {
3363        // Test writing across multiple buffers
3364        let buf1 = IoBufMut::with_capacity(5);
3365        let buf2 = IoBufMut::with_capacity(6);
3366        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3367
3368        // Write data
3369        bufs.put_slice(b"hello");
3370        bufs.put_slice(b" world");
3371        assert_eq!(bufs.coalesce(), b"hello world");
3372    }
3373
3374    #[test]
3375    fn test_iobufs_advance_drains_buffers() {
3376        let mut bufs = IoBufs::from(IoBuf::from(b"hello"));
3377        bufs.append(IoBuf::from(b" "));
3378        bufs.append(IoBuf::from(b"world"));
3379
3380        // Advance exactly past first buffer
3381        bufs.advance(5);
3382        assert_eq!(bufs.remaining(), 6);
3383        assert_eq!(bufs.chunk(), b" ");
3384
3385        // Advance across multiple buffers
3386        bufs.advance(4);
3387        assert_eq!(bufs.remaining(), 2);
3388        assert_eq!(bufs.chunk(), b"ld");
3389    }
3390
3391    #[test]
3392    fn test_iobufs_advance_exactly_to_boundary() {
3393        let mut bufs = IoBufs::from(IoBuf::from(b"abc"));
3394        bufs.append(IoBuf::from(b"def"));
3395
3396        // Advance exactly to first buffer boundary
3397        bufs.advance(3);
3398        assert_eq!(bufs.remaining(), 3);
3399        assert_eq!(bufs.chunk(), b"def");
3400
3401        // Advance exactly to end
3402        bufs.advance(3);
3403        assert_eq!(bufs.remaining(), 0);
3404    }
3405
3406    #[test]
3407    fn test_iobufs_advance_canonicalizes_pair_to_single() {
3408        let mut bufs = IoBufs::from(IoBuf::from(b"ab"));
3409        bufs.append(IoBuf::from(b"cd"));
3410        bufs.advance(2);
3411        assert!(bufs.is_single());
3412        assert_eq!(bufs.chunk(), b"cd");
3413    }
3414
3415    #[test]
3416    fn test_iobufsmut_with_empty_buffers() {
3417        let buf1 = IoBufMut::from(b"hello");
3418        let buf2 = IoBufMut::default();
3419        let buf3 = IoBufMut::from(b" world");
3420        let mut bufs = IoBufsMut::from(vec![buf1, buf2, buf3]);
3421
3422        assert_eq!(bufs.remaining(), 11);
3423        assert_eq!(bufs.chunk(), b"hello");
3424
3425        // Advance past first buffer
3426        bufs.advance(5);
3427        // Empty buffer should be skipped
3428        assert_eq!(bufs.chunk(), b" world");
3429        assert_eq!(bufs.remaining(), 6);
3430    }
3431
3432    #[test]
3433    fn test_iobufsmut_advance_skips_leading_writable_empty_chunk() {
3434        // A leading chunk with capacity but no readable bytes (len == 0) should
3435        // be skipped during advance, reaching the next readable chunk.
3436        let empty_writable = IoBufMut::with_capacity(4);
3437        let payload = IoBufMut::from(b"xy");
3438        let mut bufs = IoBufsMut::from(vec![empty_writable, payload]);
3439
3440        bufs.advance(1);
3441        assert_eq!(bufs.chunk(), b"y");
3442        assert_eq!(bufs.remaining(), 1);
3443    }
3444
3445    #[test]
3446    fn test_iobufsmut_coalesce_after_advance() {
3447        // Advance mid-chunk: advance 3 of 11 bytes
3448        let buf1 = IoBufMut::from(b"hello");
3449        let buf2 = IoBufMut::from(b" world");
3450        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3451
3452        bufs.advance(3);
3453        assert_eq!(bufs.coalesce(), b"lo world");
3454
3455        // Advance to exact chunk boundary: advance 5 of 11 bytes
3456        let buf1 = IoBufMut::from(b"hello");
3457        let buf2 = IoBufMut::from(b" world");
3458        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3459
3460        bufs.advance(5);
3461        assert_eq!(bufs.coalesce(), b" world");
3462    }
3463
3464    #[test]
3465    fn test_iobufsmut_copy_to_bytes() {
3466        let buf1 = IoBufMut::from(b"hello");
3467        let buf2 = IoBufMut::from(b" world");
3468        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3469
3470        // First read spans chunks and leaves unread suffix.
3471        let first = bufs.copy_to_bytes(7);
3472        assert_eq!(&first[..], b"hello w");
3473        assert_eq!(bufs.remaining(), 4);
3474
3475        // Second read drains the remainder.
3476        let rest = bufs.copy_to_bytes(4);
3477        assert_eq!(&rest[..], b"orld");
3478        assert_eq!(bufs.remaining(), 0);
3479    }
3480
3481    #[test]
3482    fn test_iobufsmut_copy_to_bytes_chunked_four_plus() {
3483        let mut bufs = IoBufsMut::from(vec![
3484            IoBufMut::from(b"ab"),
3485            IoBufMut::from(b"cd"),
3486            IoBufMut::from(b"ef"),
3487            IoBufMut::from(b"gh"),
3488        ]);
3489
3490        // Exercise chunked advance path before copy_to_bytes.
3491        bufs.advance(1);
3492        assert_eq!(bufs.chunk(), b"b");
3493        bufs.advance(1);
3494        assert_eq!(bufs.chunk(), b"cd");
3495
3496        // Chunked fast-path: first chunk alone satisfies request.
3497        let first = bufs.copy_to_bytes(1);
3498        assert_eq!(&first[..], b"c");
3499
3500        // Chunked slow-path: request crosses chunk boundaries.
3501        let second = bufs.copy_to_bytes(4);
3502        assert_eq!(&second[..], b"defg");
3503
3504        let rest = bufs.copy_to_bytes(1);
3505        assert_eq!(&rest[..], b"h");
3506        assert_eq!(bufs.remaining(), 0);
3507
3508        // Enter copy_to_bytes while still in chunked representation.
3509        let mut bufs = IoBufsMut::from(vec![
3510            IoBufMut::from(b"a"),
3511            IoBufMut::from(b"b"),
3512            IoBufMut::from(b"c"),
3513            IoBufMut::from(b"d"),
3514            IoBufMut::from(b"e"),
3515        ]);
3516        assert!(matches!(bufs.inner, IoBufsMutInner::Chunked(_)));
3517        let first = bufs.copy_to_bytes(1);
3518        assert_eq!(&first[..], b"a");
3519        // Stay chunked while consuming across multiple tiny chunks.
3520        let next = bufs.copy_to_bytes(3);
3521        assert_eq!(&next[..], b"bcd");
3522        assert_eq!(bufs.chunk(), b"e");
3523        assert_eq!(bufs.remaining(), 1);
3524    }
3525
3526    #[test]
3527    fn test_iobufsmut_copy_to_bytes_canonicalizes_pair() {
3528        let mut bufs = IoBufsMut::from(vec![IoBufMut::from(b"ab"), IoBufMut::from(b"cd")]);
3529        assert!(matches!(bufs.inner, IoBufsMutInner::Pair(_)));
3530
3531        let first = bufs.copy_to_bytes(2);
3532        assert_eq!(&first[..], b"ab");
3533
3534        assert!(bufs.is_single());
3535        assert_eq!(bufs.chunk(), b"cd");
3536        assert_eq!(bufs.remaining(), 2);
3537    }
3538
3539    #[test]
3540    fn test_iobufsmut_copy_from_slice_single() {
3541        let mut bufs = IoBufsMut::from(IoBufMut::zeroed(11));
3542        bufs.copy_from_slice(b"hello world");
3543        assert_eq!(bufs.coalesce(), b"hello world");
3544    }
3545
3546    #[test]
3547    fn test_iobufsmut_copy_from_slice_chunked() {
3548        let buf1 = IoBufMut::zeroed(5);
3549        let buf2 = IoBufMut::zeroed(6);
3550        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3551
3552        bufs.copy_from_slice(b"hello world");
3553
3554        // Verify each chunk was filled correctly.
3555        assert_eq!(bufs.chunk(), b"hello");
3556        bufs.advance(5);
3557        assert_eq!(bufs.chunk(), b" world");
3558        bufs.advance(6);
3559        assert_eq!(bufs.remaining(), 0);
3560    }
3561
3562    #[test]
3563    #[should_panic(expected = "source slice length must match buffer length")]
3564    fn test_iobufsmut_copy_from_slice_wrong_length() {
3565        let mut bufs = IoBufsMut::from(IoBufMut::zeroed(5));
3566        bufs.copy_from_slice(b"hello world"); // 11 bytes into 5-byte buffer
3567    }
3568
3569    #[test]
3570    fn test_iobufsmut_matches_bytesmut_chain() {
3571        // Create three BytesMut with capacity
3572        let mut bm1 = BytesMut::with_capacity(5);
3573        let mut bm2 = BytesMut::with_capacity(6);
3574        let mut bm3 = BytesMut::with_capacity(7);
3575
3576        // Create matching IoBufsMut
3577        let mut iobufs = IoBufsMut::from(vec![
3578            IoBufMut::with_capacity(5),
3579            IoBufMut::with_capacity(6),
3580            IoBufMut::with_capacity(7),
3581        ]);
3582
3583        // Test initial chunk_mut length matches (spare capacity)
3584        let chain_len = (&mut bm1)
3585            .chain_mut(&mut bm2)
3586            .chain_mut(&mut bm3)
3587            .chunk_mut()
3588            .len();
3589        let iobufs_len = iobufs.chunk_mut().len();
3590        assert_eq!(chain_len, iobufs_len);
3591
3592        // Write some data
3593        (&mut bm1)
3594            .chain_mut(&mut bm2)
3595            .chain_mut(&mut bm3)
3596            .put_slice(b"hel");
3597        iobufs.put_slice(b"hel");
3598
3599        // Verify chunk_mut matches after partial write
3600        let chain_len = (&mut bm1)
3601            .chain_mut(&mut bm2)
3602            .chain_mut(&mut bm3)
3603            .chunk_mut()
3604            .len();
3605        let iobufs_len = iobufs.chunk_mut().len();
3606        assert_eq!(chain_len, iobufs_len);
3607
3608        // Write more data
3609        (&mut bm1)
3610            .chain_mut(&mut bm2)
3611            .chain_mut(&mut bm3)
3612            .put_slice(b"lo world!");
3613        iobufs.put_slice(b"lo world!");
3614
3615        // Verify chunk_mut matches after more writes
3616        let chain_len = (&mut bm1)
3617            .chain_mut(&mut bm2)
3618            .chain_mut(&mut bm3)
3619            .chunk_mut()
3620            .len();
3621        let iobufs_len = iobufs.chunk_mut().len();
3622        assert_eq!(chain_len, iobufs_len);
3623
3624        // Verify final content matches
3625        let frozen = iobufs.freeze().coalesce();
3626        let mut chain_content = bm1.to_vec();
3627        chain_content.extend_from_slice(&bm2);
3628        chain_content.extend_from_slice(&bm3);
3629        assert_eq!(frozen, chain_content.as_slice());
3630        assert_eq!(frozen, b"hello world!");
3631    }
3632
3633    #[test]
3634    fn test_iobufsmut_buf_matches_bytes_chain() {
3635        // Create pre-filled Bytes buffers
3636        let mut b1 = Bytes::from_static(b"hello");
3637        let mut b2 = Bytes::from_static(b" world");
3638        let b3 = Bytes::from_static(b"!");
3639
3640        // Create matching IoBufsMut
3641        let mut iobufs = IoBufsMut::from(vec![
3642            IoBufMut::from(b"hello"),
3643            IoBufMut::from(b" world"),
3644            IoBufMut::from(b"!"),
3645        ]);
3646
3647        // Test Buf::remaining matches
3648        let chain_remaining = b1.clone().chain(b2.clone()).chain(b3.clone()).remaining();
3649        assert_eq!(chain_remaining, iobufs.remaining());
3650
3651        // Test Buf::chunk matches
3652        let chain_chunk = b1
3653            .clone()
3654            .chain(b2.clone())
3655            .chain(b3.clone())
3656            .chunk()
3657            .to_vec();
3658        assert_eq!(chain_chunk, iobufs.chunk().to_vec());
3659
3660        // Advance and test again
3661        b1.advance(3);
3662        iobufs.advance(3);
3663
3664        let chain_remaining = b1.clone().chain(b2.clone()).chain(b3.clone()).remaining();
3665        assert_eq!(chain_remaining, iobufs.remaining());
3666
3667        let chain_chunk = b1
3668            .clone()
3669            .chain(b2.clone())
3670            .chain(b3.clone())
3671            .chunk()
3672            .to_vec();
3673        assert_eq!(chain_chunk, iobufs.chunk().to_vec());
3674
3675        // Advance past first buffer boundary into second
3676        b1.advance(2);
3677        iobufs.advance(2);
3678
3679        let chain_remaining = b1.clone().chain(b2.clone()).chain(b3.clone()).remaining();
3680        assert_eq!(chain_remaining, iobufs.remaining());
3681
3682        // Now we should be in the second buffer
3683        let chain_chunk = b1
3684            .clone()
3685            .chain(b2.clone())
3686            .chain(b3.clone())
3687            .chunk()
3688            .to_vec();
3689        assert_eq!(chain_chunk, iobufs.chunk().to_vec());
3690
3691        // Advance past second buffer boundary into third
3692        b2.advance(6);
3693        iobufs.advance(6);
3694
3695        let chain_remaining = b1.clone().chain(b2.clone()).chain(b3.clone()).remaining();
3696        assert_eq!(chain_remaining, iobufs.remaining());
3697
3698        // Now we should be in the third buffer
3699        let chain_chunk = b1.chain(b2).chain(b3).chunk().to_vec();
3700        assert_eq!(chain_chunk, iobufs.chunk().to_vec());
3701
3702        // Test copy_to_bytes
3703        let b1 = Bytes::from_static(b"hello");
3704        let b2 = Bytes::from_static(b" world");
3705        let b3 = Bytes::from_static(b"!");
3706        let mut iobufs = IoBufsMut::from(vec![
3707            IoBufMut::from(b"hello"),
3708            IoBufMut::from(b" world"),
3709            IoBufMut::from(b"!"),
3710        ]);
3711
3712        let chain_bytes = b1.chain(b2).chain(b3).copy_to_bytes(8);
3713        let iobufs_bytes = iobufs.copy_to_bytes(8);
3714        assert_eq!(chain_bytes, iobufs_bytes);
3715        assert_eq!(chain_bytes.as_ref(), b"hello wo");
3716    }
3717
3718    #[test]
3719    fn test_iobufsmut_chunks_vectored_multiple_slices() {
3720        // Single non-empty buffers should export exactly one slice.
3721        let single = IoBufsMut::from(IoBufMut::from(b"xy"));
3722        let mut single_dst = [IoSlice::new(&[]); 2];
3723        let count = single.chunks_vectored(&mut single_dst);
3724        assert_eq!(count, 1);
3725        assert_eq!(&single_dst[0][..], b"xy");
3726
3727        // Single empty buffers should export no slices.
3728        let empty_single = IoBufsMut::default();
3729        let mut empty_single_dst = [IoSlice::new(&[]); 1];
3730        assert_eq!(empty_single.chunks_vectored(&mut empty_single_dst), 0);
3731
3732        let bufs = IoBufsMut::from(vec![
3733            IoBufMut::from(b"ab"),
3734            IoBufMut::from(b"cd"),
3735            IoBufMut::from(b"ef"),
3736            IoBufMut::from(b"gh"),
3737        ]);
3738
3739        // Destination capacity should cap how many chunks we export.
3740        let mut small = [IoSlice::new(&[]); 2];
3741        let count = bufs.chunks_vectored(&mut small);
3742        assert_eq!(count, 2);
3743        assert_eq!(&small[0][..], b"ab");
3744        assert_eq!(&small[1][..], b"cd");
3745
3746        // Larger destination should include every readable chunk.
3747        let mut large = [IoSlice::new(&[]); 8];
3748        let count = bufs.chunks_vectored(&mut large);
3749        assert_eq!(count, 4);
3750        assert_eq!(&large[0][..], b"ab");
3751        assert_eq!(&large[1][..], b"cd");
3752        assert_eq!(&large[2][..], b"ef");
3753        assert_eq!(&large[3][..], b"gh");
3754
3755        // Empty destination cannot accept any slices.
3756        let mut empty_dst: [IoSlice<'_>; 0] = [];
3757        assert_eq!(bufs.chunks_vectored(&mut empty_dst), 0);
3758
3759        // Non-canonical shapes should skip empty leading chunks.
3760        let sparse = IoBufsMut {
3761            inner: IoBufsMutInner::Pair([IoBufMut::default(), IoBufMut::from(b"y")]),
3762        };
3763        let mut dst = [IoSlice::new(&[]); 2];
3764        let count = sparse.chunks_vectored(&mut dst);
3765        assert_eq!(count, 1);
3766        assert_eq!(&dst[0][..], b"y");
3767
3768        // Triple should skip empty chunks and preserve readable order.
3769        let sparse_triple = IoBufsMut {
3770            inner: IoBufsMutInner::Triple([
3771                IoBufMut::default(),
3772                IoBufMut::from(b"z"),
3773                IoBufMut::from(b"w"),
3774            ]),
3775        };
3776        let mut dst = [IoSlice::new(&[]); 3];
3777        let count = sparse_triple.chunks_vectored(&mut dst);
3778        assert_eq!(count, 2);
3779        assert_eq!(&dst[0][..], b"z");
3780        assert_eq!(&dst[1][..], b"w");
3781
3782        // Chunked shapes with only empty buffers should export no slices.
3783        let empty_chunked = IoBufsMut {
3784            inner: IoBufsMutInner::Chunked(VecDeque::from([
3785                IoBufMut::default(),
3786                IoBufMut::default(),
3787            ])),
3788        };
3789        let mut dst = [IoSlice::new(&[]); 2];
3790        assert_eq!(empty_chunked.chunks_vectored(&mut dst), 0);
3791    }
3792
3793    #[test]
3794    fn test_iobufsmut_try_into_single() {
3795        let single = IoBufsMut::from(IoBufMut::from(b"hello"));
3796        let single = single.try_into_single().expect("single expected");
3797        assert_eq!(single, b"hello");
3798
3799        let multi = IoBufsMut::from(vec![IoBufMut::from(b"ab"), IoBufMut::from(b"cd")]);
3800        let multi = multi.try_into_single().expect_err("multi expected");
3801        assert_eq!(multi.coalesce(), b"abcd");
3802    }
3803
3804    #[test]
3805    fn test_iobufsmut_freeze_after_advance() {
3806        // Partial advance: advance 3 of 11 bytes
3807        let buf1 = IoBufMut::from(b"hello");
3808        let buf2 = IoBufMut::from(b" world");
3809        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3810
3811        bufs.advance(3);
3812        assert_eq!(bufs.len(), 8);
3813
3814        let frozen = bufs.freeze();
3815        assert_eq!(frozen.len(), 8);
3816        assert_eq!(frozen.coalesce(), b"lo world");
3817
3818        // Exact boundary advance: advance 5 of 11 bytes (first buf is 5 bytes)
3819        let buf1 = IoBufMut::from(b"hello");
3820        let buf2 = IoBufMut::from(b" world");
3821        let mut bufs = IoBufsMut::from(vec![buf1, buf2]);
3822
3823        bufs.advance(5);
3824        assert_eq!(bufs.len(), 6);
3825
3826        // First buffer should be fully consumed (empty after advance)
3827        // freeze() filters empty buffers, so result should be Single
3828        let frozen = bufs.freeze();
3829        assert!(frozen.is_single());
3830        assert_eq!(frozen.coalesce(), b" world");
3831    }
3832
3833    #[test]
3834    fn test_iobufsmut_coalesce_with_pool() {
3835        let pool = test_pool();
3836
3837        // Single buffer: zero-copy (same pointer)
3838        let mut buf = IoBufMut::from(b"hello");
3839        let original_ptr = buf.as_mut_ptr();
3840        let bufs = IoBufsMut::from(buf);
3841        let coalesced = bufs.coalesce_with_pool(&pool);
3842        assert_eq!(coalesced, b"hello");
3843        assert_eq!(coalesced.as_ref().as_ptr(), original_ptr);
3844
3845        // Multiple buffers: merged using pool
3846        let bufs = IoBufsMut::from(vec![IoBufMut::from(b"hello"), IoBufMut::from(b" world")]);
3847        let coalesced = bufs.coalesce_with_pool(&pool);
3848        assert_eq!(coalesced, b"hello world");
3849        assert!(coalesced.is_pooled());
3850
3851        // Four chunks force the deque-backed coalesce path instead of pair/triple fast paths.
3852        let bufs = IoBufsMut::from(vec![
3853            IoBufMut::from(b"a"),
3854            IoBufMut::from(b"b"),
3855            IoBufMut::from(b"c"),
3856            IoBufMut::from(b"d"),
3857        ]);
3858        let coalesced = bufs.coalesce_with_pool(&pool);
3859        assert_eq!(coalesced, b"abcd");
3860        assert!(coalesced.is_pooled());
3861
3862        // With extra capacity: zero-copy if sufficient spare capacity
3863        let mut buf = IoBufMut::with_capacity(100);
3864        buf.put_slice(b"hello");
3865        let original_ptr = buf.as_mut_ptr();
3866        let bufs = IoBufsMut::from(buf);
3867        let coalesced = bufs.coalesce_with_pool_extra(&pool, 10);
3868        assert_eq!(coalesced, b"hello");
3869        assert_eq!(coalesced.as_ref().as_ptr(), original_ptr);
3870
3871        // With extra capacity: reallocates if insufficient
3872        let mut buf = IoBufMut::with_capacity(5);
3873        buf.put_slice(b"hello");
3874        let bufs = IoBufsMut::from(buf);
3875        let coalesced = bufs.coalesce_with_pool_extra(&pool, 100);
3876        assert_eq!(coalesced, b"hello");
3877        assert!(coalesced.capacity() >= 105);
3878    }
3879
3880    #[test]
3881    fn test_iobuf_additional_conversion_and_trait_paths() {
3882        let pool = test_pool();
3883
3884        let mut pooled_mut = pool.alloc(4);
3885        pooled_mut.put_slice(b"data");
3886        let pooled = pooled_mut.freeze();
3887        assert!(!pooled.as_ptr().is_null());
3888
3889        let unique = IoBuf::from(Bytes::from(vec![1u8, 2, 3]));
3890        let unique_mut = unique.try_into_mut().expect("unique bytes should convert");
3891        assert_eq!(unique_mut.as_ref(), &[1u8, 2, 3]);
3892
3893        let shared = IoBuf::from(Bytes::from(vec![4u8, 5, 6]));
3894        let _shared_clone = shared.clone();
3895        assert!(shared.try_into_mut().is_err());
3896
3897        let expected: &[u8] = &[9u8, 8];
3898        let eq_buf = IoBuf::from(vec![9u8, 8]);
3899        assert!(PartialEq::<[u8]>::eq(&eq_buf, expected));
3900
3901        let static_slice: &'static [u8] = b"static";
3902        assert_eq!(IoBuf::from(static_slice), b"static");
3903
3904        let mut pooled_mut = pool.alloc(3);
3905        pooled_mut.put_slice(b"xyz");
3906        let pooled = pooled_mut.freeze();
3907        let vec_out: Vec<u8> = pooled.clone().into();
3908        let bytes_out: Bytes = pooled.into();
3909        assert_eq!(vec_out, b"xyz");
3910        assert_eq!(bytes_out.as_ref(), b"xyz");
3911    }
3912
3913    #[test]
3914    fn test_iobuf_into_mut_with_pool() {
3915        let pool = test_pool();
3916
3917        // Unique buffers recover mutability without copying.
3918        let mut unique = pool.alloc(4);
3919        unique.put_slice(b"data");
3920        let unique_ptr = unique.as_mut_ptr();
3921        let mut recovered = unique.freeze().into_mut_with_pool(&pool);
3922        assert_eq!(recovered.as_ref(), b"data");
3923        assert_eq!(recovered.as_mut_ptr(), unique_ptr);
3924
3925        // Shared buffers allocate from the pool and copy readable bytes.
3926        let mut shared = pool.alloc(4);
3927        shared.put_slice(b"copy");
3928        let shared = shared.freeze();
3929        let shared_ptr = shared.as_ptr();
3930        let _clone = shared.clone();
3931        let mut copied = shared.into_mut_with_pool(&pool);
3932        assert_eq!(copied.as_ref(), b"copy");
3933        assert_ne!(copied.as_mut_ptr() as *const u8, shared_ptr);
3934        assert!(copied.is_pooled());
3935
3936        // Recovery after transient slices are dropped is zero-copy and preserves
3937        // the full readable length and capacity.
3938        let mut mirror = pool.alloc(8);
3939        mirror.put_slice(b"abcdefgh");
3940        let mirror_cap = mirror.capacity();
3941        let mirror_ptr = mirror.as_mut_ptr();
3942        let frozen = mirror.freeze();
3943        let head = frozen.slice(0..3);
3944        let tail = frozen.slice(5..8);
3945        drop(head);
3946        drop(tail);
3947        let mut recovered = frozen.into_mut_with_pool(&pool);
3948        assert_eq!(recovered.as_ref(), b"abcdefgh");
3949        assert_eq!(recovered.as_mut_ptr(), mirror_ptr);
3950        assert_eq!(recovered.capacity(), mirror_cap);
3951    }
3952
3953    #[test]
3954    fn test_iobufmut_additional_conversion_and_trait_paths() {
3955        // Basic mutable operations should keep readable bytes consistent.
3956        let mut buf = IoBufMut::from(vec![1u8, 2, 3, 4]);
3957        assert!(!buf.is_empty());
3958        buf.truncate(2);
3959        assert_eq!(buf.as_ref(), &[1u8, 2]);
3960        buf.clear();
3961        assert!(buf.is_empty());
3962        buf.put_slice(b"xyz");
3963
3964        // Equality should work across slice, array, and byte-string forms.
3965        let expected: &[u8] = b"xyz";
3966        assert!(PartialEq::<[u8]>::eq(&buf, expected));
3967        assert!(buf == b"xyz"[..]);
3968        assert!(buf == *b"xyz");
3969        assert!(buf == b"xyz");
3970
3971        // Conversions from common owned/shared containers preserve contents.
3972        let from_vec = IoBufMut::from(vec![7u8, 8]);
3973        assert_eq!(from_vec.as_ref(), &[7u8, 8]);
3974
3975        let from_bytesmut = IoBufMut::from(BytesMut::from(&b"hi"[..]));
3976        assert_eq!(from_bytesmut.as_ref(), b"hi");
3977
3978        let from_bytes = IoBufMut::from(Bytes::from_static(b"ok"));
3979        assert_eq!(from_bytes.as_ref(), b"ok");
3980
3981        // `Bytes::from_static` cannot be converted to mutable without copy.
3982        let from_iobuf = IoBufMut::from(IoBuf::from(Bytes::from_static(b"io")));
3983        assert_eq!(from_iobuf.as_ref(), b"io");
3984    }
3985
3986    #[test]
3987    fn test_iobuf_aligned_public_paths() {
3988        // Exercise the public IoBuf/IoBufMut API through the untracked aligned
3989        // backing: write, advance, copy_to_bytes, freeze, slice, split_to,
3990        // try_into_mut, and From/Into conversions.
3991        static ARRAY: &[u8; 4] = b"wxyz";
3992
3993        let alignment = NonZeroUsize::new(64).expect("non-zero alignment");
3994
3995        // Start from a non-zero untracked aligned buffer to cover the public mutable API.
3996        let mut aligned_mut = IoBufMut::with_alignment(8, alignment);
3997        assert!(!aligned_mut.is_pooled());
3998        assert!(aligned_mut.is_empty());
3999        assert_eq!(aligned_mut.capacity(), 8);
4000        assert!((aligned_mut.as_mut_ptr() as usize).is_multiple_of(64));
4001
4002        aligned_mut.put_slice(b"abcdefgh");
4003        assert_eq!(aligned_mut.as_mut(), b"abcdefgh");
4004        assert_eq!(aligned_mut.chunk(), b"abcdefgh");
4005        aligned_mut.advance(2);
4006        assert_eq!(aligned_mut.chunk(), b"cdefgh");
4007
4008        let partial = aligned_mut.copy_to_bytes(2);
4009        assert_eq!(partial.as_ref(), b"cd");
4010        assert_eq!(aligned_mut.as_ref(), b"efgh");
4011        let empty = aligned_mut.copy_to_bytes(0);
4012        assert!(empty.is_empty());
4013        assert_eq!(aligned_mut.as_ref(), b"efgh");
4014
4015        aligned_mut.clear();
4016        assert!(aligned_mut.is_empty());
4017        aligned_mut.put_slice(ARRAY);
4018        assert!(aligned_mut == ARRAY);
4019
4020        // Full aligned drains should use the owner-transfer path, including len == 0 first.
4021        let mut fully_drained = IoBufMut::with_alignment(4, alignment);
4022        fully_drained.put_slice(b"lmno");
4023        let empty = fully_drained.copy_to_bytes(0);
4024        assert!(empty.is_empty());
4025        assert_eq!(fully_drained.as_ref(), b"lmno");
4026        let drained = fully_drained.copy_to_bytes(4);
4027        assert_eq!(drained.as_ref(), b"lmno");
4028        assert!(fully_drained.is_empty());
4029
4030        // Freeze to an immutable aligned `IoBuf` and exercise its view/Buf dispatch.
4031        let aligned = aligned_mut.freeze();
4032        assert!(!aligned.is_pooled());
4033        assert_eq!(aligned.as_ref(), &ARRAY[..]);
4034        assert!(aligned == ARRAY);
4035        assert!(!aligned.as_ptr().is_null());
4036        assert_eq!(aligned.slice(..2), b"wx");
4037        assert_eq!(aligned.slice(1..), b"xyz");
4038        assert_eq!(aligned.slice(1..=2), b"xy");
4039        assert_eq!(aligned.chunk(), b"wxyz");
4040
4041        let mut split = aligned.clone();
4042        let prefix = split.split_to(2);
4043        assert_eq!(prefix, b"wx");
4044        assert_eq!(split, b"yz");
4045
4046        let mut advanced = aligned.clone();
4047        advanced.advance(2);
4048        assert_eq!(advanced.chunk(), b"yz");
4049
4050        // Partial and full immutable drains should preserve the aligned backing behavior.
4051        let mut drained = aligned.clone();
4052        let empty = drained.copy_to_bytes(0);
4053        assert!(empty.is_empty());
4054        assert_eq!(drained.as_ref(), &ARRAY[..]);
4055        let first = drained.copy_to_bytes(1);
4056        assert_eq!(first.as_ref(), b"w");
4057        let rest = drained.copy_to_bytes(3);
4058        assert_eq!(rest.as_ref(), b"xyz");
4059        assert_eq!(drained.remaining(), 0);
4060
4061        // Unique aligned immutable buffers can become mutable again.
4062        let mut unique_source = IoBufMut::zeroed_with_alignment(4, alignment);
4063        unique_source.as_mut().copy_from_slice(b"pqrs");
4064        let unique = unique_source.freeze();
4065        let recovered = unique
4066            .try_into_mut()
4067            .expect("unique aligned iobuf should recover mutability");
4068        assert_eq!(recovered.as_ref(), b"pqrs");
4069
4070        // Shared aligned immutable buffers must reject the mutable conversion.
4071        let mut shared_source = IoBufMut::zeroed_with_alignment(4, alignment);
4072        shared_source.as_mut().copy_from_slice(b"tuvw");
4073        let shared = shared_source.freeze();
4074        let _shared_clone = shared.clone();
4075        assert!(shared.try_into_mut().is_err());
4076
4077        // Owned/container conversions should preserve bytes for aligned backings.
4078        let vec_out: Vec<u8> = aligned.clone().into();
4079        let bytes_out: Bytes = aligned.into();
4080        assert_eq!(vec_out, ARRAY.to_vec());
4081        assert_eq!(bytes_out.as_ref(), &ARRAY[..]);
4082
4083        let from_array = IoBuf::from(ARRAY);
4084        assert_eq!(from_array, b"wxyz");
4085
4086        let iobufs = IoBufs::from(ARRAY);
4087        assert_eq!(iobufs.chunk(), b"wxyz");
4088    }
4089
4090    #[test]
4091    fn test_iobufmut_aligned_zero_length_constructors() {
4092        let alignment = NonZeroUsize::new(64).expect("non-zero alignment");
4093
4094        let with_alignment = IoBufMut::with_alignment(0, alignment);
4095        assert!(with_alignment.is_empty());
4096        assert_eq!(with_alignment.len(), 0);
4097        assert_eq!(with_alignment.capacity(), 0);
4098
4099        let zeroed = IoBufMut::zeroed_with_alignment(0, alignment);
4100        assert!(zeroed.is_empty());
4101        assert_eq!(zeroed.len(), 0);
4102        assert_eq!(zeroed.capacity(), 0);
4103    }
4104
4105    #[test]
4106    fn test_iobufs_additional_shape_and_conversion_paths() {
4107        let pool = test_pool();
4108
4109        // Constructor coverage for mutable/immutable/slice-backed inputs.
4110        let from_mut = IoBufs::from(IoBufMut::from(b"m"));
4111        assert_eq!(from_mut.chunk(), b"m");
4112        let from_bytes = IoBufs::from(Bytes::from_static(b"b"));
4113        assert_eq!(from_bytes.chunk(), b"b");
4114        let from_bytesmut = IoBufs::from(BytesMut::from(&b"bm"[..]));
4115        assert_eq!(from_bytesmut.chunk(), b"bm");
4116        let from_vec = IoBufs::from(vec![1u8, 2u8]);
4117        assert_eq!(from_vec.chunk(), &[1u8, 2]);
4118        let static_slice: &'static [u8] = b"slice";
4119        let from_static = IoBufs::from(static_slice);
4120        assert_eq!(from_static.chunk(), b"slice");
4121
4122        // Canonicalizing an already-empty buffer remains a single empty chunk.
4123        let mut single_empty = IoBufs::default();
4124        single_empty.canonicalize();
4125        assert!(single_empty.is_single());
4126
4127        // Triple path: prepend/append can promote into chunked while preserving order.
4128        let mut triple = IoBufs::from(vec![
4129            IoBuf::from(b"a".to_vec()),
4130            IoBuf::from(b"b".to_vec()),
4131            IoBuf::from(b"c".to_vec()),
4132        ]);
4133        assert!(triple.as_single().is_none());
4134        triple.prepend(IoBuf::from(vec![b'0']));
4135        triple.prepend(IoBuf::from(vec![b'1']));
4136        triple.append(IoBuf::from(vec![b'2']));
4137        assert_eq!(triple.copy_to_bytes(triple.remaining()).as_ref(), b"10abc2");
4138
4139        // Appending to an existing triple keeps byte order stable.
4140        let mut triple_append = IoBufs::from(vec![
4141            IoBuf::from(b"x".to_vec()),
4142            IoBuf::from(b"y".to_vec()),
4143            IoBuf::from(b"z".to_vec()),
4144        ]);
4145        triple_append.append(IoBuf::from(vec![b'w']));
4146        assert_eq!(triple_append.coalesce(), b"xyzw");
4147
4148        // coalesce_with_pool on a triple should preserve contents.
4149        let triple_pool = IoBufs::from(vec![
4150            IoBuf::from(b"a".to_vec()),
4151            IoBuf::from(b"b".to_vec()),
4152            IoBuf::from(b"c".to_vec()),
4153        ]);
4154        assert_eq!(triple_pool.coalesce_with_pool(&pool), b"abc");
4155
4156        // coalesce_with_pool on 4+ chunks should read only remaining bytes.
4157        let mut chunked_pool = IoBufs::from(vec![
4158            IoBuf::from(b"a".to_vec()),
4159            IoBuf::from(b"b".to_vec()),
4160            IoBuf::from(b"c".to_vec()),
4161            IoBuf::from(b"d".to_vec()),
4162        ]);
4163        assert_eq!(chunked_pool.remaining(), 4);
4164        chunked_pool.advance(1);
4165        assert_eq!(chunked_pool.coalesce_with_pool(&pool), b"bcd");
4166
4167        // Non-canonical Pair/Triple/Chunked shapes should still expose the first readable chunk.
4168        let pair_second = IoBufs {
4169            inner: IoBufsInner::Pair([IoBuf::default(), IoBuf::from(vec![1u8])]),
4170        };
4171        assert_eq!(pair_second.chunk(), &[1u8]);
4172        let pair_empty = IoBufs {
4173            inner: IoBufsInner::Pair([IoBuf::default(), IoBuf::default()]),
4174        };
4175        assert_eq!(pair_empty.chunk(), b"");
4176
4177        let triple_third = IoBufs {
4178            inner: IoBufsInner::Triple([
4179                IoBuf::default(),
4180                IoBuf::default(),
4181                IoBuf::from(vec![3u8]),
4182            ]),
4183        };
4184        assert_eq!(triple_third.chunk(), &[3u8]);
4185        let triple_second = IoBufs {
4186            inner: IoBufsInner::Triple([
4187                IoBuf::default(),
4188                IoBuf::from(vec![2u8]),
4189                IoBuf::default(),
4190            ]),
4191        };
4192        assert_eq!(triple_second.chunk(), &[2u8]);
4193        let triple_empty = IoBufs {
4194            inner: IoBufsInner::Triple([IoBuf::default(), IoBuf::default(), IoBuf::default()]),
4195        };
4196        assert_eq!(triple_empty.chunk(), b"");
4197
4198        let chunked_second = IoBufs {
4199            inner: IoBufsInner::Chunked(VecDeque::from([IoBuf::default(), IoBuf::from(vec![9u8])])),
4200        };
4201        assert_eq!(chunked_second.chunk(), &[9u8]);
4202        let chunked_empty = IoBufs {
4203            inner: IoBufsInner::Chunked(VecDeque::from([IoBuf::default()])),
4204        };
4205        assert_eq!(chunked_empty.chunk(), b"");
4206    }
4207
4208    #[test]
4209    fn test_iobufsmut_additional_shape_and_conversion_paths() {
4210        // `as_single` accessors should work only for single-shape containers.
4211        let mut single = IoBufsMut::from(IoBufMut::from(b"x"));
4212        assert!(single.as_single().is_some());
4213        assert!(single.as_single_mut().is_some());
4214        single.canonicalize();
4215        assert!(single.is_single());
4216
4217        let mut pair = IoBufsMut::from(vec![IoBufMut::from(b"a"), IoBufMut::from(b"b")]);
4218        assert!(pair.as_single().is_none());
4219        assert!(pair.as_single_mut().is_none());
4220
4221        // Constructor coverage for raw vec and BytesMut sources.
4222        let from_vec = IoBufsMut::from(vec![1u8, 2u8]);
4223        assert_eq!(from_vec.chunk(), &[1u8, 2]);
4224        let from_bytesmut = IoBufsMut::from(BytesMut::from(&b"cd"[..]));
4225        assert_eq!(from_bytesmut.chunk(), b"cd");
4226
4227        // Chunked write path: set_len + copy_from_slice + freeze round-trip.
4228        let mut chunked = IoBufsMut::from(vec![
4229            IoBufMut::with_capacity(1),
4230            IoBufMut::with_capacity(1),
4231            IoBufMut::with_capacity(1),
4232            IoBufMut::with_capacity(1),
4233        ]);
4234        // SAFETY: We only write/read initialized bytes after `copy_from_slice`.
4235        unsafe { chunked.set_len(4) };
4236        chunked.copy_from_slice(b"wxyz");
4237        assert_eq!(chunked.capacity(), 4);
4238        assert_eq!(chunked.remaining(), 4);
4239        let frozen = chunked.freeze();
4240        assert_eq!(frozen.coalesce(), b"wxyz");
4241    }
4242
4243    #[test]
4244    fn test_iobufsmut_coalesce_multi_shape_paths() {
4245        let pool = test_pool();
4246
4247        // Pair: plain coalesce and pool-backed coalesce-with-extra.
4248        let pair = IoBufsMut::from(vec![IoBufMut::from(b"ab"), IoBufMut::from(b"cd")]);
4249        assert_eq!(pair.coalesce(), b"abcd");
4250        let pair = IoBufsMut::from(vec![IoBufMut::from(b"ab"), IoBufMut::from(b"cd")]);
4251        let pair_extra = pair.coalesce_with_pool_extra(&pool, 3);
4252        assert_eq!(pair_extra, b"abcd");
4253        assert!(pair_extra.capacity() >= 7);
4254
4255        // Triple: both coalesce paths should preserve payload and requested spare capacity.
4256        let triple = IoBufsMut::from(vec![
4257            IoBufMut::from(b"a"),
4258            IoBufMut::from(b"b"),
4259            IoBufMut::from(b"c"),
4260        ]);
4261        assert_eq!(triple.coalesce(), b"abc");
4262        let triple = IoBufsMut::from(vec![
4263            IoBufMut::from(b"a"),
4264            IoBufMut::from(b"b"),
4265            IoBufMut::from(b"c"),
4266        ]);
4267        let triple_extra = triple.coalesce_with_pool_extra(&pool, 2);
4268        assert_eq!(triple_extra, b"abc");
4269        assert!(triple_extra.capacity() >= 5);
4270
4271        // Chunked (4+): same expectations as pair/triple for content + capacity.
4272        let chunked = IoBufsMut::from(vec![
4273            IoBufMut::from(b"1"),
4274            IoBufMut::from(b"2"),
4275            IoBufMut::from(b"3"),
4276            IoBufMut::from(b"4"),
4277        ]);
4278        assert_eq!(chunked.coalesce(), b"1234");
4279        let chunked = IoBufsMut::from(vec![
4280            IoBufMut::from(b"1"),
4281            IoBufMut::from(b"2"),
4282            IoBufMut::from(b"3"),
4283            IoBufMut::from(b"4"),
4284        ]);
4285        let chunked_extra = chunked.coalesce_with_pool_extra(&pool, 5);
4286        assert_eq!(chunked_extra, b"1234");
4287        assert!(chunked_extra.capacity() >= 9);
4288    }
4289
4290    #[test]
4291    fn test_iobufsmut_noncanonical_chunk_and_chunk_mut_paths() {
4292        fn no_spare_capacity_buf(pool: &BufferPool) -> IoBufMut {
4293            let mut buf = pool.alloc(1);
4294            let cap = buf.capacity();
4295            // SAFETY: We never read from this buffer in this helper.
4296            unsafe { buf.set_len(cap) };
4297            buf
4298        }
4299        let pool = test_pool();
4300
4301        // `chunk()` should skip empty front buffers across all shapes.
4302        let pair_second = IoBufsMut {
4303            inner: IoBufsMutInner::Pair([IoBufMut::default(), IoBufMut::from(b"b")]),
4304        };
4305        assert_eq!(pair_second.chunk(), b"b");
4306        let pair_empty = IoBufsMut {
4307            inner: IoBufsMutInner::Pair([IoBufMut::default(), IoBufMut::default()]),
4308        };
4309        assert_eq!(pair_empty.chunk(), b"");
4310
4311        let triple_third = IoBufsMut {
4312            inner: IoBufsMutInner::Triple([
4313                IoBufMut::default(),
4314                IoBufMut::default(),
4315                IoBufMut::from(b"c"),
4316            ]),
4317        };
4318        assert_eq!(triple_third.chunk(), b"c");
4319        let triple_second = IoBufsMut {
4320            inner: IoBufsMutInner::Triple([
4321                IoBufMut::default(),
4322                IoBufMut::from(b"b"),
4323                IoBufMut::default(),
4324            ]),
4325        };
4326        assert_eq!(triple_second.chunk(), b"b");
4327        let triple_empty = IoBufsMut {
4328            inner: IoBufsMutInner::Triple([
4329                IoBufMut::default(),
4330                IoBufMut::default(),
4331                IoBufMut::default(),
4332            ]),
4333        };
4334        assert_eq!(triple_empty.chunk(), b"");
4335
4336        let chunked_second = IoBufsMut {
4337            inner: IoBufsMutInner::Chunked(VecDeque::from([
4338                IoBufMut::default(),
4339                IoBufMut::from(b"d"),
4340            ])),
4341        };
4342        assert_eq!(chunked_second.chunk(), b"d");
4343        let chunked_empty = IoBufsMut {
4344            inner: IoBufsMutInner::Chunked(VecDeque::from([IoBufMut::default()])),
4345        };
4346        assert_eq!(chunked_empty.chunk(), b"");
4347
4348        // `chunk_mut()` should skip non-writable fronts and return first writable chunk.
4349        let mut pair_chunk_mut = IoBufsMut {
4350            inner: IoBufsMutInner::Pair([no_spare_capacity_buf(&pool), IoBufMut::with_capacity(2)]),
4351        };
4352        assert!(pair_chunk_mut.chunk_mut().len() >= 2);
4353
4354        let mut pair_chunk_mut_empty = IoBufsMut {
4355            inner: IoBufsMutInner::Pair([
4356                no_spare_capacity_buf(&pool),
4357                no_spare_capacity_buf(&pool),
4358            ]),
4359        };
4360        assert_eq!(pair_chunk_mut_empty.chunk_mut().len(), 0);
4361
4362        let mut triple_chunk_mut = IoBufsMut {
4363            inner: IoBufsMutInner::Triple([
4364                no_spare_capacity_buf(&pool),
4365                no_spare_capacity_buf(&pool),
4366                IoBufMut::with_capacity(3),
4367            ]),
4368        };
4369        assert!(triple_chunk_mut.chunk_mut().len() >= 3);
4370        let mut triple_chunk_mut_second = IoBufsMut {
4371            inner: IoBufsMutInner::Triple([
4372                no_spare_capacity_buf(&pool),
4373                IoBufMut::with_capacity(2),
4374                no_spare_capacity_buf(&pool),
4375            ]),
4376        };
4377        assert!(triple_chunk_mut_second.chunk_mut().len() >= 2);
4378
4379        let mut triple_chunk_mut_empty = IoBufsMut {
4380            inner: IoBufsMutInner::Triple([
4381                no_spare_capacity_buf(&pool),
4382                no_spare_capacity_buf(&pool),
4383                no_spare_capacity_buf(&pool),
4384            ]),
4385        };
4386        assert_eq!(triple_chunk_mut_empty.chunk_mut().len(), 0);
4387
4388        let mut chunked_chunk_mut = IoBufsMut {
4389            inner: IoBufsMutInner::Chunked(VecDeque::from([
4390                IoBufMut::default(),
4391                IoBufMut::with_capacity(4),
4392            ])),
4393        };
4394        assert!(chunked_chunk_mut.chunk_mut().len() >= 4);
4395
4396        let mut chunked_chunk_mut_empty = IoBufsMut {
4397            inner: IoBufsMutInner::Chunked(VecDeque::from([no_spare_capacity_buf(&pool)])),
4398        };
4399        assert_eq!(chunked_chunk_mut_empty.chunk_mut().len(), 0);
4400    }
4401
4402    #[test]
4403    fn test_iobuf_internal_chunk_helpers() {
4404        // `copy_to_bytes_chunked` should drop leading empties on zero-length reads.
4405        let mut empty_with_leading = VecDeque::from([IoBuf::default()]);
4406        let (bytes, needs_canonicalize) = copy_to_bytes_chunked(&mut empty_with_leading, 0, "x");
4407        assert!(bytes.is_empty());
4408        assert!(!needs_canonicalize);
4409        assert!(empty_with_leading.is_empty());
4410
4411        // Fast path: front chunk can fully satisfy the request.
4412        let mut fast = VecDeque::from([
4413            IoBuf::from(b"ab".to_vec()),
4414            IoBuf::from(b"cd".to_vec()),
4415            IoBuf::from(b"ef".to_vec()),
4416            IoBuf::from(b"gh".to_vec()),
4417        ]);
4418        let (bytes, needs_canonicalize) = copy_to_bytes_chunked(&mut fast, 2, "x");
4419        assert_eq!(bytes.as_ref(), b"ab");
4420        assert!(needs_canonicalize);
4421        assert_eq!(fast.front().expect("front exists").as_ref(), b"cd");
4422
4423        // Slow path: request spans multiple chunks.
4424        let mut slow = VecDeque::from([
4425            IoBuf::from(b"a".to_vec()),
4426            IoBuf::from(b"bc".to_vec()),
4427            IoBuf::from(b"d".to_vec()),
4428            IoBuf::from(b"e".to_vec()),
4429        ]);
4430        let (bytes, needs_canonicalize) = copy_to_bytes_chunked(&mut slow, 3, "x");
4431        assert_eq!(bytes.as_ref(), b"abc");
4432        assert!(needs_canonicalize);
4433
4434        let mut empty_with_leading_mut = VecDeque::from([IoBufMut::default()]);
4435        let (bytes, needs_canonicalize) =
4436            copy_to_bytes_chunked(&mut empty_with_leading_mut, 0, "x");
4437        assert!(bytes.is_empty());
4438        assert!(!needs_canonicalize);
4439        assert!(empty_with_leading_mut.is_empty());
4440
4441        // Mirror the fast/slow chunked helper paths for mutable chunks too.
4442        let mut fast_mut = VecDeque::from([
4443            IoBufMut::from(b"ab"),
4444            IoBufMut::from(b"cd"),
4445            IoBufMut::from(b"ef"),
4446            IoBufMut::from(b"gh"),
4447        ]);
4448        let (bytes, needs_canonicalize) = copy_to_bytes_chunked(&mut fast_mut, 2, "x");
4449        assert_eq!(bytes.as_ref(), b"ab");
4450        assert!(needs_canonicalize);
4451        assert_eq!(fast_mut.front().expect("front exists").as_ref(), b"cd");
4452
4453        let mut slow_mut = VecDeque::from([
4454            IoBufMut::from(b"a"),
4455            IoBufMut::from(b"bc"),
4456            IoBufMut::from(b"de"),
4457            IoBufMut::from(b"f"),
4458        ]);
4459        let (bytes, needs_canonicalize) = copy_to_bytes_chunked(&mut slow_mut, 4, "x");
4460        assert_eq!(bytes.as_ref(), b"abcd");
4461        assert!(needs_canonicalize);
4462        assert_eq!(slow_mut.front().expect("front exists").as_ref(), b"e");
4463
4464        // `advance_chunked_front` should skip empties and drain in linear order.
4465        let mut advance_chunked = VecDeque::from([
4466            IoBuf::default(),
4467            IoBuf::from(b"abc".to_vec()),
4468            IoBuf::from(b"d".to_vec()),
4469        ]);
4470        advance_chunked_front(&mut advance_chunked, 2);
4471        assert_eq!(
4472            advance_chunked.front().expect("front exists").as_ref(),
4473            b"c"
4474        );
4475        advance_chunked_front(&mut advance_chunked, 2);
4476        assert!(advance_chunked.is_empty());
4477
4478        // The front-advance helper also has a separate mutable monomorphization.
4479        let mut advance_chunked_mut = VecDeque::from([
4480            IoBufMut::default(),
4481            IoBufMut::from(b"abc"),
4482            IoBufMut::from(b"d"),
4483        ]);
4484        advance_chunked_front(&mut advance_chunked_mut, 2);
4485        assert_eq!(
4486            advance_chunked_mut.front().expect("front exists").as_ref(),
4487            b"c"
4488        );
4489        advance_chunked_front(&mut advance_chunked_mut, 2);
4490        assert!(advance_chunked_mut.is_empty());
4491
4492        // `advance_small_chunks` signals canonicalization when front chunks are exhausted.
4493        let mut small = [IoBuf::default(), IoBuf::from(b"abc".to_vec())];
4494        let needs_canonicalize = advance_small_chunks(&mut small, 2);
4495        assert!(needs_canonicalize);
4496        assert_eq!(small[1].as_ref(), b"c");
4497
4498        let mut small_exact = [
4499            IoBuf::from(b"a".to_vec()),
4500            IoBuf::from(b"b".to_vec()),
4501            IoBuf::from(b"c".to_vec()),
4502        ];
4503        let needs_canonicalize = advance_small_chunks(&mut small_exact, 3);
4504        assert!(needs_canonicalize);
4505        assert_eq!(small_exact[0].remaining(), 0);
4506        assert_eq!(small_exact[1].remaining(), 0);
4507        assert_eq!(small_exact[2].remaining(), 0);
4508
4509        // Small-chunk copy canonicalization is also instantiated for mutable chunks.
4510        let mut small_mut = [
4511            IoBufMut::from(b"a"),
4512            IoBufMut::from(b"bc"),
4513            IoBufMut::from(b"d"),
4514        ];
4515        let (bytes, needs_canonicalize) = copy_to_bytes_small_chunks(&mut small_mut, 3, "x");
4516        assert_eq!(bytes.as_ref(), b"abc");
4517        assert!(needs_canonicalize);
4518        assert_eq!(small_mut[2].as_ref(), b"d");
4519
4520        // `advance_mut_in_chunks` returns whether the request fully fit in writable chunks.
4521        let mut writable = [IoBufMut::with_capacity(2), IoBufMut::with_capacity(1)];
4522        let mut remaining = 3usize;
4523        // SAFETY: We do not read from advanced bytes in this test.
4524        let all_advanced = unsafe { advance_mut_in_chunks(&mut writable, &mut remaining) };
4525        assert!(all_advanced);
4526        assert_eq!(remaining, 0);
4527
4528        // `advance_mut_in_chunks` should skip non-writable chunks.
4529        let pool = test_pool();
4530        let mut full = pool.alloc(1);
4531        // SAFETY: We only mark initialized capacity; bytes are not read.
4532        unsafe { full.set_len(full.capacity()) };
4533        let mut writable_after_full = [full, IoBufMut::with_capacity(2)];
4534        let mut remaining = 2usize;
4535        // SAFETY: We do not read from advanced bytes in this test.
4536        let all_advanced =
4537            unsafe { advance_mut_in_chunks(&mut writable_after_full, &mut remaining) };
4538        assert!(all_advanced);
4539        assert_eq!(remaining, 0);
4540
4541        let mut writable_short = [IoBufMut::with_capacity(1), IoBufMut::with_capacity(1)];
4542        let mut remaining = 3usize;
4543        // SAFETY: We do not read from advanced bytes in this test.
4544        let all_advanced = unsafe { advance_mut_in_chunks(&mut writable_short, &mut remaining) };
4545        assert!(!all_advanced);
4546        assert_eq!(remaining, 1);
4547    }
4548
4549    #[test]
4550    fn test_iobufsmut_advance_mut_success_paths() {
4551        // Pair path.
4552        let mut pair = IoBufsMut {
4553            inner: IoBufsMutInner::Pair([IoBufMut::with_capacity(2), IoBufMut::with_capacity(2)]),
4554        };
4555        // SAFETY: We only verify cursor movement (`remaining`) and do not read bytes.
4556        unsafe { pair.advance_mut(3) };
4557        assert_eq!(pair.remaining(), 3);
4558
4559        // Triple path.
4560        let mut triple = IoBufsMut {
4561            inner: IoBufsMutInner::Triple([
4562                IoBufMut::with_capacity(1),
4563                IoBufMut::with_capacity(1),
4564                IoBufMut::with_capacity(1),
4565            ]),
4566        };
4567        // SAFETY: We only verify cursor movement (`remaining`) and do not read bytes.
4568        unsafe { triple.advance_mut(2) };
4569        assert_eq!(triple.remaining(), 2);
4570
4571        // Chunked wrapped-VecDeque path.
4572        let mut wrapped = VecDeque::with_capacity(5);
4573        wrapped.push_back(IoBufMut::with_capacity(1));
4574        wrapped.push_back(IoBufMut::with_capacity(1));
4575        wrapped.push_back(IoBufMut::with_capacity(1));
4576        wrapped.push_back(IoBufMut::with_capacity(1));
4577        wrapped.push_back(IoBufMut::with_capacity(1));
4578        let _ = wrapped.pop_front();
4579        wrapped.push_back(IoBufMut::with_capacity(1));
4580        let (first, second) = wrapped.as_slices();
4581        assert!(!first.is_empty());
4582        assert!(!second.is_empty());
4583
4584        // Force `advance_mut` to consume across the wrapped second slice as well.
4585        let to_advance = first.len() + 1;
4586        let mut chunked = IoBufsMut {
4587            inner: IoBufsMutInner::Chunked(wrapped),
4588        };
4589        // SAFETY: We only verify cursor movement (`remaining`) and do not read bytes.
4590        unsafe { chunked.advance_mut(to_advance) };
4591        assert_eq!(chunked.remaining(), to_advance);
4592        assert!(chunked.remaining_mut() > 0);
4593    }
4594
4595    #[test]
4596    fn test_iobufsmut_advance_mut_zero_noop_when_full() {
4597        fn full_chunk(pool: &BufferPool) -> IoBufMut {
4598            // Pooled buffers have bounded class capacity (unlike growable Bytes),
4599            // so force len == capacity to make remaining_mut() == 0.
4600            let mut buf = pool.alloc(1);
4601            let cap = buf.capacity();
4602            // SAFETY: We never read from this buffer in this test.
4603            unsafe { buf.set_len(cap) };
4604            buf
4605        }
4606
4607        let pool = test_pool();
4608
4609        // Pair path: fully-written chunks should allow advance_mut(0) as a no-op.
4610        let mut pair = IoBufsMut::from(vec![full_chunk(&pool), full_chunk(&pool)]);
4611        assert!(matches!(pair.inner, IoBufsMutInner::Pair(_)));
4612        assert_eq!(pair.remaining_mut(), 0);
4613        let before = pair.remaining();
4614        // SAFETY: Advancing by 0 does not expose uninitialized bytes.
4615        unsafe { pair.advance_mut(0) };
4616        assert_eq!(pair.remaining(), before);
4617
4618        // Triple path: same no-op behavior.
4619        let mut triple = IoBufsMut::from(vec![
4620            full_chunk(&pool),
4621            full_chunk(&pool),
4622            full_chunk(&pool),
4623        ]);
4624        assert!(matches!(triple.inner, IoBufsMutInner::Triple(_)));
4625        assert_eq!(triple.remaining_mut(), 0);
4626        let before = triple.remaining();
4627        // SAFETY: Advancing by 0 does not expose uninitialized bytes.
4628        unsafe { triple.advance_mut(0) };
4629        assert_eq!(triple.remaining(), before);
4630
4631        // Chunked path: 4+ fully-written chunks should also no-op.
4632        let mut chunked = IoBufsMut::from(vec![
4633            full_chunk(&pool),
4634            full_chunk(&pool),
4635            full_chunk(&pool),
4636            full_chunk(&pool),
4637        ]);
4638        assert!(matches!(chunked.inner, IoBufsMutInner::Chunked(_)));
4639        assert_eq!(chunked.remaining_mut(), 0);
4640        let before = chunked.remaining();
4641        // SAFETY: Advancing by 0 does not expose uninitialized bytes.
4642        unsafe { chunked.advance_mut(0) };
4643        assert_eq!(chunked.remaining(), before);
4644    }
4645
4646    #[test]
4647    #[should_panic(expected = "cannot advance past end of buffer")]
4648    fn test_iobufsmut_advance_mut_past_end_pair() {
4649        let mut pair = IoBufsMut {
4650            inner: IoBufsMutInner::Pair([IoBufMut::with_capacity(1), IoBufMut::with_capacity(1)]),
4651        };
4652        // SAFETY: Intentional panic path coverage.
4653        unsafe { pair.advance_mut(3) };
4654    }
4655
4656    #[test]
4657    #[should_panic(expected = "cannot advance past end of buffer")]
4658    fn test_iobufsmut_advance_mut_past_end_triple() {
4659        let mut triple = IoBufsMut {
4660            inner: IoBufsMutInner::Triple([
4661                IoBufMut::with_capacity(1),
4662                IoBufMut::with_capacity(1),
4663                IoBufMut::with_capacity(1),
4664            ]),
4665        };
4666        // SAFETY: Intentional panic path coverage.
4667        unsafe { triple.advance_mut(4) };
4668    }
4669
4670    #[test]
4671    #[should_panic(expected = "cannot advance past end of buffer")]
4672    fn test_iobufsmut_advance_mut_past_end_chunked() {
4673        let mut chunked = IoBufsMut {
4674            inner: IoBufsMutInner::Chunked(VecDeque::from([
4675                IoBufMut::with_capacity(1),
4676                IoBufMut::with_capacity(1),
4677                IoBufMut::with_capacity(1),
4678                IoBufMut::with_capacity(1),
4679            ])),
4680        };
4681        // SAFETY: Intentional panic path coverage.
4682        unsafe { chunked.advance_mut(5) };
4683    }
4684
4685    #[test]
4686    fn test_iobufsmut_set_len() {
4687        // SAFETY: we don't read the uninitialized bytes.
4688        unsafe {
4689            // Single buffer
4690            let mut bufs = IoBufsMut::from(IoBufMut::with_capacity(16));
4691            bufs.set_len(10);
4692            assert_eq!(bufs.len(), 10);
4693
4694            // Chunked: distributes across chunks [cap 5, cap 10], set 12 -> [5, 7]
4695            let mut bufs = IoBufsMut::from(vec![
4696                IoBufMut::with_capacity(5),
4697                IoBufMut::with_capacity(10),
4698            ]);
4699            bufs.set_len(12);
4700            assert_eq!(bufs.len(), 12);
4701            assert_eq!(bufs.chunk().len(), 5);
4702            bufs.advance(5);
4703            assert_eq!(bufs.chunk().len(), 7);
4704            bufs.advance(7);
4705            assert_eq!(bufs.remaining(), 0);
4706
4707            // Uneven capacities [3, 20, 2], set 18 -> [3, 15, 0].
4708            let mut bufs = IoBufsMut::from(vec![
4709                IoBufMut::with_capacity(3),
4710                IoBufMut::with_capacity(20),
4711                IoBufMut::with_capacity(2),
4712            ]);
4713            bufs.set_len(18);
4714            assert_eq!(bufs.chunk().len(), 3);
4715            bufs.advance(3);
4716            assert_eq!(bufs.chunk().len(), 15);
4717            bufs.advance(15);
4718            assert_eq!(bufs.remaining(), 0);
4719
4720            // Exact total capacity [4, 4], set 8 -> [4, 4]
4721            let mut bufs =
4722                IoBufsMut::from(vec![IoBufMut::with_capacity(4), IoBufMut::with_capacity(4)]);
4723            bufs.set_len(8);
4724            assert_eq!(bufs.chunk().len(), 4);
4725            bufs.advance(4);
4726            assert_eq!(bufs.chunk().len(), 4);
4727            bufs.advance(4);
4728            assert_eq!(bufs.remaining(), 0);
4729
4730            // Zero length preserves caller-provided layout.
4731            let mut bufs =
4732                IoBufsMut::from(vec![IoBufMut::with_capacity(4), IoBufMut::with_capacity(4)]);
4733            bufs.set_len(0);
4734            assert_eq!(bufs.len(), 0);
4735            assert_eq!(bufs.chunk(), b"");
4736        }
4737    }
4738
4739    #[test]
4740    #[should_panic(expected = "set_len(9) exceeds capacity(8)")]
4741    fn test_iobufsmut_set_len_overflow() {
4742        let mut bufs =
4743            IoBufsMut::from(vec![IoBufMut::with_capacity(4), IoBufMut::with_capacity(4)]);
4744        // SAFETY: this will panic before any read.
4745        unsafe { bufs.set_len(9) };
4746    }
4747
4748    #[test]
4749    #[should_panic(expected = "set_len(9) exceeds capacity(8)")]
4750    fn test_iobufmut_set_len_overflow() {
4751        let mut buf = IoBufMut::with_capacity(8);
4752        // SAFETY: this will panic before any read.
4753        unsafe { buf.set_len(9) };
4754    }
4755
4756    #[test]
4757    fn test_encode_with_pool_matches_encode() {
4758        let value = vec![1u8, 2, 3, 4, 5, 6];
4759        assert_encode_with_pool_matches_encode(&value);
4760    }
4761
4762    #[test]
4763    fn test_encode_with_pool_mut_len_matches_encode_size() {
4764        let pool = test_pool();
4765        let value = vec![9u8, 8, 7, 6];
4766
4767        let buf = value.encode_with_pool_mut(&pool);
4768        assert_eq!(buf.len(), value.encode_size());
4769    }
4770
4771    #[test]
4772    fn test_iobuf_encode_with_pool_matches_encode() {
4773        let value = IoBuf::from(vec![0xAB; 512]);
4774        assert_encode_with_pool_matches_encode(&value);
4775    }
4776
4777    #[test]
4778    fn test_nested_container_encode_with_pool_matches_encode() {
4779        let value = (
4780            Some(Bytes::from(vec![0xAA; 256])),
4781            vec![Bytes::from(vec![0xBB; 128]), Bytes::from(vec![0xCC; 64])],
4782        );
4783        assert_encode_with_pool_matches_encode(&value);
4784    }
4785
4786    #[test]
4787    fn test_map_encode_with_pool_matches_encode() {
4788        let mut btree = BTreeMap::new();
4789        btree.insert(2u8, Bytes::from(vec![0xDD; 96]));
4790        btree.insert(1u8, Bytes::from(vec![0xEE; 48]));
4791        assert_encode_with_pool_matches_encode(&btree);
4792
4793        let mut hash = HashMap::new();
4794        hash.insert(2u8, Bytes::from(vec![0x11; 96]));
4795        hash.insert(1u8, Bytes::from(vec![0x22; 48]));
4796        assert_encode_with_pool_matches_encode(&hash);
4797    }
4798
4799    #[test]
4800    fn test_lazy_encode_with_pool_matches_encode() {
4801        let value = Lazy::new(Bytes::from(vec![0x44; 200]));
4802        assert_encode_with_pool_matches_encode(&value);
4803    }
4804
4805    #[test]
4806    fn test_range_encode_with_pool_matches_encode() {
4807        let range: Range<Bytes> = Bytes::from(vec![0x10; 32])..Bytes::from(vec![0x20; 48]);
4808        assert_encode_with_pool_matches_encode(&range);
4809
4810        let inclusive: RangeInclusive<Bytes> =
4811            Bytes::from(vec![0x30; 16])..=Bytes::from(vec![0x40; 24]);
4812        assert_encode_with_pool_matches_encode(&inclusive);
4813
4814        let from: RangeFrom<IoBuf> = IoBuf::from(vec![0x50; 40])..;
4815        assert_encode_with_pool_matches_encode(&from);
4816
4817        let to_inclusive: RangeToInclusive<IoBuf> = ..=IoBuf::from(vec![0x60; 56]);
4818        assert_encode_with_pool_matches_encode(&to_inclusive);
4819    }
4820
4821    #[cfg(feature = "arbitrary")]
4822    mod conformance {
4823        use super::IoBuf;
4824        use commonware_codec::conformance::CodecConformance;
4825
4826        commonware_conformance::conformance_tests! {
4827            CodecConformance<IoBuf>
4828        }
4829    }
4830
4831    mod builder_tests {
4832        use super::*;
4833        use commonware_codec::{BufsMut, Encode, Write};
4834
4835        fn builder(capacity: usize) -> Builder {
4836            Builder::new(&test_pool(), NonZeroUsize::new(capacity).unwrap())
4837        }
4838
4839        // Only inline writes, no pushes.
4840        #[test]
4841        fn test_inline_only() {
4842            let mut b = builder(64);
4843            b.put_u32(42);
4844            b.put_u8(7);
4845            let mut r = b.finish();
4846            assert_eq!(r.remaining(), 5);
4847            assert_eq!(r.get_u32(), 42);
4848            assert_eq!(r.get_u8(), 7);
4849        }
4850
4851        // Only zero-copy pushes, no inline writes.
4852        #[test]
4853        fn test_push_only() {
4854            let mut b = builder(64);
4855            let data = Bytes::from(vec![0xAA; 1024]);
4856            b.push(data.clone());
4857            let mut r = b.finish();
4858            assert_eq!(r.remaining(), 1024);
4859            assert_eq!(r.copy_to_bytes(1024), data);
4860        }
4861
4862        // Interleaved: inline header, zero-copy push, inline trailer.
4863        #[test]
4864        fn test_inline_push_inline() {
4865            let mut b = builder(64);
4866            b.put_u16(99);
4867            let payload = Bytes::from(vec![0xBB; 512]);
4868            b.push(payload.clone());
4869            b.put_u8(1);
4870            let mut r = b.finish();
4871            assert_eq!(r.remaining(), 2 + 512 + 1);
4872            assert_eq!(r.get_u16(), 99);
4873            assert_eq!(r.copy_to_bytes(512), payload);
4874            assert_eq!(r.get_u8(), 1);
4875        }
4876
4877        // Bytes::write_bufs produces identical wire format to Bytes::write.
4878        #[test]
4879        fn test_write_bufs_matches_write() {
4880            let data = Bytes::from(vec![0xCC; 256]);
4881            let mut b = builder(64);
4882            data.write_bufs(&mut b);
4883            let mut bufs = b.finish();
4884
4885            let mut out = vec![0u8; bufs.remaining()];
4886            bufs.copy_to_slice(&mut out);
4887            assert_eq!(out, data.encode().as_ref());
4888        }
4889
4890        // Finishing an unused builder produces empty IoBufs.
4891        #[test]
4892        fn test_empty() {
4893            let bufs = builder(64).finish();
4894            assert_eq!(bufs.remaining(), 0);
4895        }
4896
4897        // Inline writes exceeding capacity panic.
4898        #[test]
4899        #[should_panic]
4900        fn test_inline_overflow_panics() {
4901            let mut b = builder(1);
4902            let cap = b.remaining_mut();
4903            b.put_slice(&vec![0xFF; cap]);
4904            b.put_u8(1); // exceeds capacity
4905        }
4906
4907        // Pushing empty Bytes is a no-op.
4908        #[test]
4909        fn test_empty_push_ignored() {
4910            let mut b = builder(64);
4911            b.push(Bytes::new());
4912            b.put_u8(1);
4913            let bufs = b.finish();
4914            assert_eq!(bufs.remaining(), 1);
4915        }
4916
4917        // Consecutive pushes without inline writes between them.
4918        #[test]
4919        fn test_multiple_pushes() {
4920            let mut b = builder(64);
4921            let a = Bytes::from(vec![0xAA; 100]);
4922            let c = Bytes::from(vec![0xCC; 200]);
4923            b.push(a.clone());
4924            b.push(c.clone());
4925            let mut r = b.finish();
4926            assert_eq!(r.remaining(), 300);
4927            assert_eq!(r.copy_to_bytes(100), a);
4928            assert_eq!(r.copy_to_bytes(200), c);
4929        }
4930
4931        // put() exceeding capacity panics.
4932        #[test]
4933        #[should_panic]
4934        fn test_put_exceeding_capacity_panics() {
4935            let mut b = builder(1);
4936            let cap = b.remaining_mut();
4937            let src = Bytes::from(vec![0xAB; cap + 1]);
4938            b.put(src);
4939        }
4940
4941        // put_slice() exceeding capacity panics.
4942        #[test]
4943        #[should_panic]
4944        fn test_put_slice_exceeding_capacity_panics() {
4945            let mut b = builder(1);
4946            let cap = b.remaining_mut();
4947            b.put_slice(&vec![0xFE; cap + 1]);
4948        }
4949
4950        // Simulates a multi-field struct: [u16 | Bytes (via push) | u32].
4951        // Verifies write_bufs produces identical wire format to write.
4952        #[test]
4953        fn test_multi_field_struct_equivalence() {
4954            let header: u16 = 0xCAFE;
4955            let payload = Bytes::from(vec![0xDD; 1024]);
4956            let trailer: u32 = 0xDEADBEEF;
4957
4958            // Flat encoding via write.
4959            let size = header.encode_size() + payload.encode_size() + trailer.encode_size();
4960            let mut flat = BytesMut::with_capacity(size);
4961            header.write(&mut flat);
4962            payload.write(&mut flat);
4963            trailer.write(&mut flat);
4964
4965            // Multi-buffer encoding via write_bufs.
4966            let mut b = builder(64);
4967            header.write(&mut b);
4968            payload.write_bufs(&mut b);
4969            trailer.write(&mut b);
4970            let mut bufs = b.finish();
4971
4972            let mut out = vec![0u8; bufs.remaining()];
4973            bufs.copy_to_slice(&mut out);
4974            assert_eq!(out, flat.as_ref());
4975        }
4976
4977        // encode_with_pool (Builder path) matches encode (flat BytesMut path).
4978        #[test]
4979        fn test_encode_with_pool_matches_encode() {
4980            let pool = test_pool();
4981            let data = Bytes::from(vec![0xEE; 500]);
4982            let mut pooled = data.encode_with_pool(&pool);
4983            let baseline = data.encode();
4984            let mut out = vec![0u8; pooled.remaining()];
4985            pooled.copy_to_slice(&mut out);
4986            assert_eq!(out, baseline.as_ref());
4987        }
4988
4989        // Exercise remaining_mut, chunk_mut, and advance_mut directly.
4990        #[test]
4991        fn test_chunk_mut_and_advance_mut() {
4992            let mut b = builder(64);
4993            let initial = b.remaining_mut();
4994            assert!(initial >= 64);
4995            let chunk = b.chunk_mut();
4996            chunk[0..1].copy_from_slice(&[0xAB]);
4997            // SAFETY: We just wrote 1 byte into chunk_mut above.
4998            unsafe { b.advance_mut(1) };
4999            assert_eq!(b.remaining_mut(), initial - 1);
5000            let mut r = b.finish();
5001            assert_eq!(r.remaining(), 1);
5002            assert_eq!(r.get_u8(), 0xAB);
5003        }
5004
5005        // Writing past a full buffer panics (fixed capacity).
5006        #[test]
5007        #[should_panic]
5008        fn test_write_past_full_panics() {
5009            let mut b = builder(1);
5010            let cap = b.remaining_mut();
5011            b.put_slice(&vec![0xFF; cap]); // fill the buffer completely
5012            assert_eq!(b.remaining_mut(), 0);
5013            b.put_u8(0x42); // panics
5014        }
5015
5016        // Push at offset 0 with inline trailer exercises finish branch
5017        // where offset == pos (no inline prefix before push).
5018        #[test]
5019        fn test_push_at_start_with_trailer() {
5020            let mut b = builder(64);
5021            let payload = Bytes::from(vec![0xCC; 32]);
5022            b.push(payload.clone());
5023            b.put_u8(0x01);
5024            let mut r = b.finish();
5025            assert_eq!(r.remaining(), 33);
5026            assert_eq!(r.copy_to_bytes(32), payload);
5027            assert_eq!(r.get_u8(), 0x01);
5028        }
5029    }
5030}