Skip to main content

commonware_runtime/iobuf/
buf.rs

1//! Contiguous immutable and mutable I/O buffer handles.
2//!
3//! [`IoBuf`] and [`IoBufMut`] keep readable cursor state in the handle, and
4//! [`IoBufMut`] also tracks writable capacity there. Allocation ownership and
5//! reclamation are delegated to [`super::owner`].
6//! This module implements slicing, freezing, mutable recovery, conversions,
7//! and codec integration for a single buffer.
8
9use super::{
10    owner::{HeapOwner, OwnerRef, PooledBuffer},
11    panic_advance,
12    pool::BufferPool,
13};
14use bytes::{Buf, BufMut, Bytes, BytesMut, TryGetError};
15use commonware_codec::{BufsMut, EncodeSize, Error, RangeCfg, Read, Write, util::at_least};
16use std::{
17    mem::ManuallyDrop,
18    num::NonZeroUsize,
19    ops::{Bound, RangeBounds},
20    ptr::NonNull,
21};
22
23/// Immutable byte buffer.
24///
25/// The handle stores the current readable pointer and length directly:
26///
27/// ```text
28/// [ readable bytes .......... ]
29/// ^
30/// ptr
31/// len = readable bytes
32/// ```
33///
34/// Allocation ownership is represented by `owner`, a compact tagged pointer to
35/// an internal owner header. `bytes::Buf` methods use only `ptr` and `len`.
36/// Clone/drop/slice/split use `owner` on colder lifecycle paths.
37///
38/// Cloning and slicing are zero-copy. For pooled-backed values, the underlying
39/// allocation is returned to the pool when the final immutable reference is
40/// dropped.
41///
42/// All `From<*> for IoBuf` implementations are guaranteed to be non-copy
43/// conversions. Use [`IoBuf::copy_from_slice`] when an explicit copy from
44/// borrowed data is required.
45pub struct IoBuf {
46    ptr: NonNull<u8>,
47    len: usize,
48    owner: OwnerRef,
49}
50
51// SAFETY: immutable handles expose read-only bytes and synchronize shared
52// ownership through the owner refcount.
53unsafe impl Send for IoBuf {}
54// SAFETY: shared access is read-only and lifecycle state is atomic.
55unsafe impl Sync for IoBuf {}
56
57// Debug intentionally omits the data pointer: raw addresses differ across
58// identically-seeded deterministic runs and would leak heap layout into logs.
59impl std::fmt::Debug for IoBuf {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        f.debug_struct("IoBuf")
62            .field("len", &self.len)
63            .field("pooled", &self.is_pooled())
64            .finish()
65    }
66}
67
68impl Clone for IoBuf {
69    #[inline]
70    fn clone(&self) -> Self {
71        // SAFETY: cloning an immutable view retains the shared owner when one
72        // exists. Static views have an empty owner and need no lifecycle work.
73        unsafe { self.owner.clone_shared() };
74        Self {
75            ptr: self.ptr,
76            len: self.len,
77            owner: self.owner,
78        }
79    }
80}
81
82impl Drop for IoBuf {
83    #[inline]
84    fn drop(&mut self) {
85        // SAFETY: dropping an immutable view releases exactly one shared owner
86        // reference. Static/empty views have no owner.
87        unsafe { self.owner.drop_shared() };
88    }
89}
90
91impl IoBuf {
92    /// Create a buffer by copying data from a slice.
93    ///
94    /// Use this when you have a non-static `&[u8]` that needs owned storage.
95    /// For static slices, prefer [`IoBuf::from`] which is zero-copy.
96    ///
97    /// The copy lands in one native heap allocation with an inline owner
98    /// header, so the result supports zero-copy [`IoBuf::try_into_mut`].
99    pub fn copy_from_slice(data: &[u8]) -> Self {
100        IoBufMut::from(data).freeze()
101    }
102
103    #[inline]
104    fn from_static(slice: &'static [u8]) -> Self {
105        if slice.is_empty() {
106            return Self::default();
107        }
108        let ptr = NonNull::new(slice.as_ptr().cast_mut()).expect("static slice data is non-null");
109        Self {
110            ptr,
111            len: slice.len(),
112            owner: OwnerRef::empty(),
113        }
114    }
115
116    /// Returns `true` if this buffer is tracked by a pool.
117    #[inline]
118    pub fn is_pooled(&self) -> bool {
119        self.owner.is_pooled()
120    }
121
122    /// Number of bytes remaining in the buffer.
123    #[inline]
124    pub const fn len(&self) -> usize {
125        self.len
126    }
127
128    /// Whether the buffer is empty.
129    #[inline]
130    pub const fn is_empty(&self) -> bool {
131        self.len == 0
132    }
133
134    /// Get raw pointer to the first readable byte.
135    #[inline]
136    pub const fn as_ptr(&self) -> *const u8 {
137        self.ptr.as_ptr()
138    }
139
140    /// Returns a slice of self for the provided range (zero-copy).
141    ///
142    /// Empty ranges return a detached empty buffer so pooled allocations are
143    /// not pinned by empty views.
144    ///
145    /// # Panics
146    ///
147    /// Panics if the range is out of bounds of the readable bytes, if its
148    /// start is greater than its end, or if an inclusive bound overflows.
149    #[inline]
150    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
151        let (start, end) = resolve_range(self.len, range);
152        if start == end {
153            return Self::default();
154        }
155
156        // SAFETY: range resolution bounds `start <= self.len`.
157        let ptr = unsafe { self.ptr.add(start) };
158        // SAFETY: the returned view aliases immutable bytes and retains the
159        // owner while it is live.
160        unsafe { self.owner.clone_shared() };
161        Self {
162            ptr,
163            len: end - start,
164            owner: self.owner,
165        }
166    }
167
168    /// Splits the buffer into two at the given index.
169    ///
170    /// Afterwards `self` contains bytes `[at, len)`, and the returned [`IoBuf`]
171    /// contains bytes `[0, at)`.
172    ///
173    /// This is an `O(1)` zero-copy operation. Empty halves detach from the
174    /// owner so pooled allocations are not pinned by empty views.
175    ///
176    /// # Panics
177    ///
178    /// Panics if `at > len`.
179    pub fn split_to(&mut self, at: usize) -> Self {
180        assert!(
181            at <= self.len,
182            "split_to out of bounds: {:?} <= {:?}",
183            at,
184            self.len,
185        );
186        if at == 0 {
187            return Self::default();
188        }
189        if at == self.len {
190            return std::mem::take(self);
191        }
192
193        // SAFETY: prefix aliases immutable bytes and retains the owner.
194        unsafe { self.owner.clone_shared() };
195        let prefix = Self {
196            ptr: self.ptr,
197            len: at,
198            owner: self.owner,
199        };
200        // SAFETY: `at < self.len`, so advancing within the current readable region is in bounds.
201        unsafe {
202            self.ptr = self.ptr.add(at);
203        }
204        self.len -= at;
205        prefix
206    }
207
208    /// Try to convert this buffer into [`IoBufMut`] without copying.
209    ///
210    /// Succeeds when this view is the unique owner of a native (heap,
211    /// pooled, or adopted-vec) allocation, including uniquely-owned slices:
212    /// capacity is recovered from the allocation base and the current view
213    /// offset, so spare capacity beyond the view returns with it. Views with
214    /// no owner (the default and detached empty views) convert trivially.
215    ///
216    /// Declines for shared owners, non-empty static views, and
217    /// external-backed views (`Bytes` cannot back a mutable handle). An empty
218    /// view that still holds a shared or external owner declines like any
219    /// other view.
220    pub fn try_into_mut(self) -> Result<IoBufMut, Self> {
221        if self.owner.is_empty() {
222            return if self.len == 0 {
223                Ok(IoBufMut::default())
224            } else {
225                Err(self)
226            };
227        }
228
229        // External owners always decline: `Bytes` cannot back a mutable
230        // handle, so `IoBufMut` is never external-backed.
231        if self.owner.is_external() {
232            return Err(self);
233        }
234
235        // SAFETY: owner is non-empty and live.
236        if !unsafe { self.owner.is_unique() } {
237            return Err(self);
238        }
239
240        let me = ManuallyDrop::new(self);
241        // SAFETY: owner is unique and live.
242        let base = unsafe { me.owner.data_base() };
243        // SAFETY: owner is unique and live.
244        let usable_capacity = unsafe { me.owner.usable_capacity() };
245        let offset = (me.ptr.as_ptr() as usize)
246            .checked_sub(base.as_ptr() as usize)
247            .expect("view pointer must be within owner allocation");
248        assert!(
249            offset <= usable_capacity,
250            "view pointer out of owner bounds"
251        );
252        let cap = usable_capacity - offset;
253        assert!(me.len <= cap, "view length out of owner bounds");
254
255        Ok(IoBufMut {
256            ptr: me.ptr,
257            len: me.len,
258            cap,
259            owner: me.owner,
260        })
261    }
262
263    /// Convert this buffer into [`IoBufMut`], allocating from `pool` if needed.
264    ///
265    /// This is zero-copy when `self` has exclusive ownership of the backing
266    /// storage. If the buffer is shared, this allocates a new buffer from
267    /// `pool` and copies the readable bytes into it.
268    pub fn into_mut_with_pool(self, pool: &BufferPool) -> IoBufMut {
269        match self.try_into_mut() {
270            Ok(buf) => buf,
271            Err(buf) => {
272                let mut result = pool.alloc(buf.len());
273                result.put_slice(buf.as_ref());
274                result
275            }
276        }
277    }
278}
279
280impl AsRef<[u8]> for IoBuf {
281    #[inline]
282    fn as_ref(&self) -> &[u8] {
283        // SAFETY: `ptr..ptr+len` is initialized and kept alive by `owner` or is
284        // an immortal static slice.
285        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
286    }
287}
288
289impl Default for IoBuf {
290    fn default() -> Self {
291        Self {
292            ptr: NonNull::dangling(),
293            len: 0,
294            owner: OwnerRef::empty(),
295        }
296    }
297}
298
299impl PartialEq for IoBuf {
300    fn eq(&self, other: &Self) -> bool {
301        self.as_ref() == other.as_ref()
302    }
303}
304
305impl Eq for IoBuf {}
306
307impl PartialEq<[u8]> for IoBuf {
308    #[inline]
309    fn eq(&self, other: &[u8]) -> bool {
310        self.as_ref() == other
311    }
312}
313
314impl PartialEq<&[u8]> for IoBuf {
315    #[inline]
316    fn eq(&self, other: &&[u8]) -> bool {
317        self.as_ref() == *other
318    }
319}
320
321impl<const N: usize> PartialEq<[u8; N]> for IoBuf {
322    #[inline]
323    fn eq(&self, other: &[u8; N]) -> bool {
324        self.as_ref() == other
325    }
326}
327
328impl<const N: usize> PartialEq<&[u8; N]> for IoBuf {
329    #[inline]
330    fn eq(&self, other: &&[u8; N]) -> bool {
331        self.as_ref() == *other
332    }
333}
334
335impl Buf for IoBuf {
336    #[inline(always)]
337    fn remaining(&self) -> usize {
338        self.len
339    }
340
341    #[inline(always)]
342    fn chunk(&self) -> &[u8] {
343        self.as_ref()
344    }
345
346    #[inline(always)]
347    fn advance(&mut self, cnt: usize) {
348        if cnt > self.len {
349            panic_advance(cnt, self.len);
350        }
351        // SAFETY: `cnt <= self.len`, so the new pointer remains in or one byte
352        // past the readable region.
353        unsafe {
354            self.ptr = self.ptr.add(cnt);
355        }
356        self.len -= cnt;
357    }
358
359    #[inline]
360    fn copy_to_slice(&mut self, dst: &mut [u8]) {
361        if let Err(error) = self.try_copy_to_slice(dst) {
362            panic_try_get(error);
363        }
364    }
365
366    #[inline]
367    fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), TryGetError> {
368        if dst.len() > self.len {
369            return Err(TryGetError {
370                requested: dst.len(),
371                available: self.len,
372            });
373        }
374        // SAFETY: source and destination are valid for `dst.len()` bytes and
375        // cannot overlap because `dst` is a unique mutable slice outside this
376        // immutable buffer.
377        unsafe {
378            std::ptr::copy_nonoverlapping(self.ptr.as_ptr(), dst.as_mut_ptr(), dst.len());
379            self.ptr = self.ptr.add(dst.len());
380        }
381        self.len -= dst.len();
382        Ok(())
383    }
384
385    /// Drains `len` readable bytes into [`Bytes`].
386    ///
387    /// Zero-copy despite the trait method's name: a shared view is carved
388    /// off and converted through the `From<IoBuf> for Bytes` fast paths.
389    #[inline]
390    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
391        assert!(len <= self.len, "copy_to_bytes out of bounds");
392        if len == 0 {
393            return Bytes::new();
394        }
395        if len == self.len {
396            return Bytes::from(std::mem::take(self));
397        }
398
399        // External-backed views slice the inner Bytes directly: one inner
400        // refcount clone, instead of cloning and then dropping our owner
401        // through the shared-decrement path.
402        if self.owner.is_external() {
403            // SAFETY: the external owner is live while `self` holds its
404            // reference, and the view prefix lies within the inner `Bytes`
405            // range by invariant, as `slice_ref` requires.
406            let inner = unsafe { self.owner.external_bytes() };
407            let bytes = inner.slice_ref(&self.as_ref()[..len]);
408            self.advance(len);
409            return bytes;
410        }
411
412        let drained = Self {
413            ptr: self.ptr,
414            len,
415            owner: self.owner,
416        };
417        // SAFETY: `drained` is a new immutable view into the same owner.
418        unsafe { drained.owner.clone_shared() };
419        self.advance(len);
420        Bytes::from(drained)
421    }
422}
423
424/// Convert a [`Vec<u8>`] into an [`IoBuf`] without copying.
425///
426/// Adopts the vec's allocation when its spare capacity can host the owner
427/// header, and otherwise moves it into [`Bytes`] behind an external owner.
428impl From<Vec<u8>> for IoBuf {
429    fn from(vec: Vec<u8>) -> Self {
430        let (ptr, len, owner) = OwnerRef::from_vec(vec);
431        Self { ptr, len, owner }
432    }
433}
434
435/// Convert [`Bytes`] into an [`IoBuf`] without copying.
436///
437/// The `Bytes` value moves into a small external owner and the handle points
438/// directly into its payload. Handle clones and drops never touch the inner
439/// refcount. Only the final release and the `slice_ref` conversion fast paths
440/// do.
441impl From<Bytes> for IoBuf {
442    fn from(bytes: Bytes) -> Self {
443        let (ptr, len, owner) = OwnerRef::from_bytes(bytes);
444        Self { ptr, len, owner }
445    }
446}
447
448/// Convert [`BytesMut`] into an [`IoBuf`] without copying (via `freeze`).
449impl From<BytesMut> for IoBuf {
450    fn from(bytes: BytesMut) -> Self {
451        Self::from(bytes.freeze())
452    }
453}
454
455/// Zero-copy: creates a static view with no owner.
456impl<const N: usize> From<&'static [u8; N]> for IoBuf {
457    fn from(array: &'static [u8; N]) -> Self {
458        Self::from_static(array)
459    }
460}
461
462/// Zero-copy: creates a static view with no owner.
463impl From<&'static [u8]> for IoBuf {
464    fn from(slice: &'static [u8]) -> Self {
465        Self::from_static(slice)
466    }
467}
468
469/// Convert an [`IoBuf`] into a [`Vec<u8>`].
470///
471/// This conversion copies the readable bytes.
472impl From<IoBuf> for Vec<u8> {
473    fn from(buf: IoBuf) -> Self {
474        buf.as_ref().to_vec()
475    }
476}
477
478/// Convert an [`IoBuf`] into [`Bytes`] without copying readable data.
479///
480/// Static views convert via [`Bytes::from_static`] (free), external-backed
481/// views via [`Bytes::slice_ref`] on the inner `Bytes` (a refcount clone,
482/// though the first conversion of a still-promotable inner `Bytes` pays
483/// bytes' one shared-header allocation), and native heap/pooled views via
484/// [`Bytes::from_owner`] (one box).
485impl From<IoBuf> for Bytes {
486    fn from(buf: IoBuf) -> Self {
487        if buf.is_empty() {
488            return Self::new();
489        }
490        if buf.owner.is_empty() {
491            // Non-empty views with no owner are 'static by invariant.
492            // SAFETY: `ptr..ptr+len` is an initialized immortal slice.
493            let slice: &'static [u8] =
494                unsafe { std::slice::from_raw_parts(buf.ptr.as_ptr(), buf.len) };
495            return Self::from_static(slice);
496        }
497        if buf.owner.is_external() {
498            // SAFETY: the external owner is live while `buf` holds its
499            // reference, and the view lies within the inner `Bytes` range by
500            // invariant, as `slice_ref` requires.
501            let inner = unsafe { buf.owner.external_bytes() };
502            return inner.slice_ref(buf.as_ref());
503        }
504        Self::from_owner(buf)
505    }
506}
507
508impl Write for IoBuf {
509    #[inline]
510    fn write(&self, buf: &mut impl BufMut) {
511        self.len().write(buf);
512        buf.put_slice(self.as_ref());
513    }
514
515    #[inline]
516    fn write_bufs(&self, buf: &mut impl BufsMut) {
517        self.len().write(buf);
518        buf.push(self.clone());
519    }
520}
521
522impl EncodeSize for IoBuf {
523    #[inline]
524    fn encode_size(&self) -> usize {
525        self.len().encode_size() + self.len()
526    }
527
528    #[inline]
529    fn encode_inline_size(&self) -> usize {
530        self.len().encode_size()
531    }
532}
533
534impl Read for IoBuf {
535    type Cfg = RangeCfg<usize>;
536
537    #[inline]
538    fn read_cfg(buf: &mut impl Buf, range: &Self::Cfg) -> Result<Self, Error> {
539        let len = usize::read_cfg(buf, range)?;
540        at_least(buf, len)?;
541        Ok(Self::from(buf.copy_to_bytes(len)))
542    }
543}
544
545#[cfg(feature = "arbitrary")]
546impl arbitrary::Arbitrary<'_> for IoBuf {
547    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
548        let len = u.arbitrary_len::<u8>()?;
549        let data: Vec<u8> = u.arbitrary_iter()?.take(len).collect::<Result<_, _>>()?;
550        Ok(Self::from(data))
551    }
552}
553
554/// Mutable byte buffer.
555///
556/// The handle stores the first readable byte, readable length, and writable
557/// view capacity directly:
558///
559/// ```text
560/// before advance:
561/// [ readable len ][ writable cap-len ]
562/// ^
563/// ptr
564///
565/// after advance(n):
566/// [ consumed ][ readable len-n ][ writable cap-len ]
567///              ^
568///              ptr
569/// ```
570///
571/// `advance` moves `ptr` forward and shrinks both `len` and `cap`. `BufMut`
572/// writes always begin at `ptr + len`.
573///
574/// # Capacity
575///
576/// The capacity is fixed at construction: unlike [`BytesMut`], the buffer
577/// never grows, and every write past `capacity()` panics (including through
578/// [`BufMut`] methods such as `put_slice`). [`Self::default`] and zero-sized
579/// constructions own no storage, so any write to them panics. Allocate the
580/// full expected size up front. `remaining_mut()` reports the actual writable
581/// tail rather than `usize::MAX`.
582pub struct IoBufMut {
583    ptr: NonNull<u8>,
584    len: usize,
585    cap: usize,
586    owner: OwnerRef,
587}
588
589// SAFETY: mutable handles have unique ownership. Moving them across threads is
590// safe because final release uses thread-safe pool/allocator paths.
591unsafe impl Send for IoBufMut {}
592// SAFETY: shared references expose only immutable reads. Mutation requires
593// `&mut self`.
594unsafe impl Sync for IoBufMut {}
595
596// Debug intentionally omits the data pointer: raw addresses differ across
597// identically-seeded deterministic runs and would leak heap layout into logs.
598impl std::fmt::Debug for IoBufMut {
599    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600        f.debug_struct("IoBufMut")
601            .field("len", &self.len)
602            .field("cap", &self.cap)
603            .field("pooled", &self.is_pooled())
604            .finish()
605    }
606}
607
608impl Drop for IoBufMut {
609    #[inline]
610    fn drop(&mut self) {
611        // SAFETY: mutable buffers uniquely own their allocation.
612        unsafe { self.owner.release_unique_mut_at(self.ptr, self.cap) };
613    }
614}
615
616impl Default for IoBufMut {
617    fn default() -> Self {
618        Self {
619            ptr: NonNull::dangling(),
620            len: 0,
621            cap: 0,
622            owner: OwnerRef::empty(),
623        }
624    }
625}
626
627impl IoBufMut {
628    /// Create a buffer with the given capacity.
629    ///
630    /// The capacity is exact and fixed. Writes past it panic (see the
631    /// [capacity](Self#capacity) section).
632    #[inline]
633    pub fn with_capacity(capacity: usize) -> Self {
634        Self::with_alignment(capacity, NonZeroUsize::MIN)
635    }
636
637    /// Create an untracked aligned buffer with the given capacity and alignment.
638    ///
639    /// The returned buffer is not tracked by a [`BufferPool`], so dropping it
640    /// deallocates the aligned allocation immediately.
641    ///
642    /// For alignments above the owner header alignment (8 bytes on 64-bit
643    /// targets) the usable region rounds the request up to the header
644    /// alignment, so `capacity()` may exceed the request by up to that
645    /// alignment minus one.
646    ///
647    /// # Panics
648    ///
649    /// Panics if `capacity` is nonzero and `alignment` is not a power of two.
650    #[inline]
651    pub fn with_alignment(capacity: usize, alignment: NonZeroUsize) -> Self {
652        if capacity == 0 {
653            return Self::default();
654        }
655        let (ptr, cap, owner) = HeapOwner::allocate_aligned_mut(capacity, alignment.get(), false);
656        Self {
657            ptr,
658            len: 0,
659            cap,
660            owner,
661        }
662    }
663
664    /// Create a zero-initialized untracked aligned buffer with the given
665    /// length and alignment.
666    ///
667    /// For alignments above the owner header alignment (8 bytes on 64-bit
668    /// targets) the usable region rounds the request up to the header
669    /// alignment, so `capacity()` may exceed `len` by up to that alignment
670    /// minus one (zero-initialized) bytes.
671    ///
672    /// # Panics
673    ///
674    /// Panics if `len` is nonzero and `alignment` is not a power of two.
675    #[inline]
676    pub fn zeroed_with_alignment(len: usize, alignment: NonZeroUsize) -> Self {
677        if len == 0 {
678            return Self::default();
679        }
680        let (ptr, cap, owner) = HeapOwner::allocate_aligned_mut(len, alignment.get(), true);
681        Self {
682            ptr,
683            len,
684            cap,
685            owner,
686        }
687    }
688
689    /// Create a buffer of `len` bytes, all initialized to zero.
690    ///
691    /// Unlike [`Self::with_capacity`], the full buffer is immediately
692    /// readable (`len() == capacity() == len`), which suits APIs that fill a
693    /// preallocated buffer such as `read_exact`.
694    #[inline]
695    pub fn zeroed(len: usize) -> Self {
696        Self::zeroed_with_alignment(len, NonZeroUsize::MIN)
697    }
698
699    /// Create a buffer from a pooled allocation.
700    ///
701    /// # Safety
702    ///
703    /// `buffer` must have an initialized live lease in its pooled slot.
704    #[inline]
705    pub(crate) unsafe fn from_pooled_parts(buffer: PooledBuffer) -> Self {
706        let cap = buffer.capacity();
707        let ptr = buffer.data_ptr();
708        // SAFETY: guaranteed by the caller.
709        let owner = unsafe { buffer.owner_ref() };
710        Self {
711            ptr,
712            len: 0,
713            cap,
714            owner,
715        }
716    }
717
718    /// Returns `true` if this buffer is tracked by a pool.
719    #[inline]
720    pub fn is_pooled(&self) -> bool {
721        self.owner.is_pooled()
722    }
723
724    /// Sets the length of the buffer.
725    ///
726    /// # Safety
727    ///
728    /// Caller must ensure all bytes in `0..len` are initialized before any
729    /// read operations.
730    ///
731    /// # Panics
732    ///
733    /// Panics if `len > capacity()`.
734    #[inline]
735    pub unsafe fn set_len(&mut self, len: usize) {
736        assert!(
737            len <= self.capacity(),
738            "set_len({len}) exceeds capacity({})",
739            self.capacity()
740        );
741        self.len = len;
742    }
743
744    /// Number of readable bytes remaining in the buffer.
745    #[inline]
746    pub const fn len(&self) -> usize {
747        self.len
748    }
749
750    /// Whether the buffer has no readable bytes.
751    #[inline]
752    pub const fn is_empty(&self) -> bool {
753        self.len == 0
754    }
755
756    /// Freeze into immutable [`IoBuf`].
757    ///
758    /// Free: the owner word moves to the immutable handle without a refcount
759    /// operation (a reserved front heap header is initialized, including its
760    /// refcount sentinel, before the owner is shared). Freezing an empty
761    /// buffer releases the allocation immediately so empty immutable views
762    /// never pin pool memory.
763    #[inline]
764    pub fn freeze(self) -> IoBuf {
765        let mut me = ManuallyDrop::new(self);
766        if me.len == 0 {
767            // SAFETY: mutable buffers uniquely own their allocation. Empty
768            // freeze releases it so empty immutable views do not pin pool memory.
769            unsafe { me.owner.release_unique_mut_at(me.ptr, me.cap) };
770            return IoBuf::default();
771        }
772        let ptr = me.ptr;
773        let cap = me.cap;
774        // SAFETY: mutable buffers uniquely own their allocation. A reserved
775        // front heap header must be initialized before the owner is shared by
776        // the immutable handle.
777        unsafe { me.owner.ensure_heap_header_for_mut(ptr, cap) };
778        IoBuf {
779            ptr: me.ptr,
780            len: me.len,
781            owner: me.owner,
782        }
783    }
784
785    /// Returns the number of bytes the buffer can hold without reallocating.
786    #[inline]
787    pub const fn capacity(&self) -> usize {
788        self.cap
789    }
790
791    /// Returns an unsafe mutable pointer to the first readable byte.
792    #[inline]
793    pub const fn as_mut_ptr(&mut self) -> *mut u8 {
794        self.ptr.as_ptr()
795    }
796
797    /// Truncates the buffer to `len` readable bytes.
798    ///
799    /// Has no effect when `len` is greater than the current length.
800    #[inline]
801    pub fn truncate(&mut self, len: usize) {
802        self.len = self.len.min(len);
803    }
804
805    /// Clears the buffer, removing all readable data. Existing view capacity is preserved.
806    #[inline]
807    pub const fn clear(&mut self) {
808        self.len = 0;
809    }
810}
811
812impl AsRef<[u8]> for IoBufMut {
813    #[inline]
814    fn as_ref(&self) -> &[u8] {
815        // SAFETY: bytes in `0..len` from `ptr` are initialized.
816        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
817    }
818}
819
820impl AsMut<[u8]> for IoBufMut {
821    #[inline]
822    fn as_mut(&mut self) -> &mut [u8] {
823        // SAFETY: bytes in `0..len` from `ptr` are initialized and `&mut self`
824        // proves unique access.
825        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
826    }
827}
828
829impl PartialEq<[u8]> for IoBufMut {
830    #[inline]
831    fn eq(&self, other: &[u8]) -> bool {
832        self.as_ref() == other
833    }
834}
835
836impl PartialEq<&[u8]> for IoBufMut {
837    #[inline]
838    fn eq(&self, other: &&[u8]) -> bool {
839        self.as_ref() == *other
840    }
841}
842
843impl<const N: usize> PartialEq<[u8; N]> for IoBufMut {
844    #[inline]
845    fn eq(&self, other: &[u8; N]) -> bool {
846        self.as_ref() == other
847    }
848}
849
850impl<const N: usize> PartialEq<&[u8; N]> for IoBufMut {
851    #[inline]
852    fn eq(&self, other: &&[u8; N]) -> bool {
853        self.as_ref() == *other
854    }
855}
856
857impl Buf for IoBufMut {
858    #[inline(always)]
859    fn remaining(&self) -> usize {
860        self.len
861    }
862
863    #[inline(always)]
864    fn chunk(&self) -> &[u8] {
865        self.as_ref()
866    }
867
868    #[inline(always)]
869    fn advance(&mut self, cnt: usize) {
870        if cnt > self.len {
871            panic_advance(cnt, self.len);
872        }
873        // SAFETY: `cnt <= len <= cap`, so the pointer stays within the view
874        // (zero-length pointer adds are always valid, so `cnt == 0` needs no
875        // special case).
876        unsafe {
877            self.ptr = self.ptr.add(cnt);
878        }
879        self.len -= cnt;
880        self.cap -= cnt;
881    }
882
883    #[inline]
884    fn copy_to_slice(&mut self, dst: &mut [u8]) {
885        if let Err(error) = self.try_copy_to_slice(dst) {
886            panic_try_get(error);
887        }
888    }
889
890    #[inline]
891    fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), TryGetError> {
892        if dst.len() > self.len {
893            return Err(TryGetError {
894                requested: dst.len(),
895                available: self.len,
896            });
897        }
898        // SAFETY: source and destination are valid for `dst.len()` bytes and
899        // cannot overlap because `dst` is a unique mutable slice outside this
900        // buffer (zero-length copies with valid pointers need no special
901        // case).
902        unsafe {
903            std::ptr::copy_nonoverlapping(self.ptr.as_ptr(), dst.as_mut_ptr(), dst.len());
904            self.ptr = self.ptr.add(dst.len());
905        }
906        self.len -= dst.len();
907        self.cap -= dst.len();
908        Ok(())
909    }
910
911    /// Drains `len` readable bytes into [`Bytes`].
912    ///
913    /// Draining the full readable length consumes the whole handle
914    /// (`mem::take` plus `freeze`) to avoid a copy: unlike `BytesMut`, the
915    /// caller's handle keeps no spare capacity afterwards. A partial drain
916    /// copies the prefix and preserves the handle's remaining capacity.
917    #[inline]
918    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
919        assert!(len <= self.len, "copy_to_bytes out of bounds");
920        if len == 0 {
921            return Bytes::new();
922        }
923        if len == self.len {
924            let drained = std::mem::take(self);
925            return Bytes::from(drained.freeze());
926        }
927
928        let bytes = Bytes::copy_from_slice(&self.as_ref()[..len]);
929        self.advance(len);
930        bytes
931    }
932}
933
934// SAFETY: `IoBufMut` exposes only the uninitialized tail `[len..cap)` through
935// `chunk_mut`, and `advance_mut` is bounded by that tail.
936unsafe impl BufMut for IoBufMut {
937    #[inline(always)]
938    fn remaining_mut(&self) -> usize {
939        self.cap - self.len
940    }
941
942    #[inline(always)]
943    unsafe fn advance_mut(&mut self, cnt: usize) {
944        let writable = self.cap - self.len;
945        if cnt > writable {
946            panic_advance(cnt, writable);
947        }
948        self.len += cnt;
949    }
950
951    #[inline(always)]
952    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
953        // SAFETY: `ptr + len` begins the uninitialized writable tail and
954        // `cap - len` is in bounds.
955        unsafe {
956            let ptr = self.ptr.as_ptr().add(self.len);
957            bytes::buf::UninitSlice::from_raw_parts_mut(ptr, self.cap - self.len)
958        }
959    }
960
961    #[inline]
962    fn put_slice(&mut self, src: &[u8]) {
963        let writable = self.cap - self.len;
964        if src.len() > writable {
965            panic_advance(src.len(), writable);
966        }
967        // SAFETY: the unique writable tail has at least `src.len()` bytes.
968        unsafe {
969            std::ptr::copy_nonoverlapping(src.as_ptr(), self.ptr.as_ptr().add(self.len), src.len());
970        }
971        self.len += src.len();
972    }
973
974    #[inline]
975    fn put_bytes(&mut self, val: u8, cnt: usize) {
976        let writable = self.cap - self.len;
977        if cnt > writable {
978            panic_advance(cnt, writable);
979        }
980        // SAFETY: the unique writable tail has at least `cnt` bytes.
981        unsafe {
982            std::ptr::write_bytes(self.ptr.as_ptr().add(self.len), val, cnt);
983        }
984        self.len += cnt;
985    }
986
987    #[inline]
988    fn put<T: Buf>(&mut self, mut src: T)
989    where
990        Self: Sized,
991    {
992        // Early check for a clear panic message, not a safety boundary.
993        let remaining = src.remaining();
994        if remaining > self.cap - self.len {
995            panic_advance(remaining, self.cap - self.len);
996        }
997        while src.has_remaining() {
998            let chunk = src.chunk();
999            let cnt = chunk.len();
1000            // Safety boundary: `Buf` is a safe trait, so `src` may report a
1001            // `remaining()` smaller than the chunks it hands out. Bound every
1002            // copy by this buffer's own capacity arithmetic, never by `src`.
1003            let writable = self.cap - self.len;
1004            if cnt > writable {
1005                panic_advance(cnt, writable);
1006            }
1007            // SAFETY: `cnt` is bounded by the unique writable tail just above.
1008            unsafe {
1009                std::ptr::copy_nonoverlapping(chunk.as_ptr(), self.ptr.as_ptr().add(self.len), cnt);
1010            }
1011            self.len += cnt;
1012            src.advance(cnt);
1013        }
1014    }
1015}
1016
1017/// Create a mutable buffer by copying the slice.
1018impl From<&[u8]> for IoBufMut {
1019    fn from(slice: &[u8]) -> Self {
1020        let mut buf = Self::with_capacity(slice.len());
1021        buf.put_slice(slice);
1022        buf
1023    }
1024}
1025
1026/// Create a mutable buffer by copying the array.
1027impl<const N: usize> From<[u8; N]> for IoBufMut {
1028    fn from(array: [u8; N]) -> Self {
1029        Self::from(array.as_ref())
1030    }
1031}
1032
1033/// Create a mutable buffer by copying the array.
1034impl<const N: usize> From<&[u8; N]> for IoBufMut {
1035    fn from(array: &[u8; N]) -> Self {
1036        Self::from(array.as_ref())
1037    }
1038}
1039
1040/// Create a mutable buffer by copying `vec`.
1041///
1042/// Zero-copy adoption is not used because it would reserve owner header space
1043/// inside the vec's allocation, shrinking the writable capacity below
1044/// `vec.capacity()`. Use `From<Vec<u8>> for IoBuf` for zero-copy immutable
1045/// conversion (and [`IoBuf::try_into_mut`] to recover mutability).
1046impl From<Vec<u8>> for IoBufMut {
1047    fn from(vec: Vec<u8>) -> Self {
1048        let mut buf = Self::with_capacity(vec.capacity());
1049        buf.put_slice(&vec);
1050        buf
1051    }
1052}
1053
1054/// Create a mutable buffer by copying `bytes`.
1055///
1056/// A mutable buffer requires runtime-owned storage for its owner header, which
1057/// a `BytesMut` allocation cannot host, so this conversion copies. The
1058/// caller's reserved capacity is preserved.
1059impl From<BytesMut> for IoBufMut {
1060    fn from(bytes: BytesMut) -> Self {
1061        let mut out = Self::with_capacity(bytes.capacity());
1062        out.put_slice(bytes.as_ref());
1063        out
1064    }
1065}
1066
1067/// Create a mutable buffer by copying `bytes`.
1068///
1069/// A mutable buffer requires unique ownership of its storage, which shared
1070/// [`Bytes`] cannot provide, so this conversion copies.
1071impl From<Bytes> for IoBufMut {
1072    fn from(bytes: Bytes) -> Self {
1073        Self::from(bytes.as_ref())
1074    }
1075}
1076
1077/// Zero-copy when exclusive ownership can be recovered (see
1078/// [`IoBuf::try_into_mut`]), copies otherwise.
1079impl From<IoBuf> for IoBufMut {
1080    fn from(buf: IoBuf) -> Self {
1081        match buf.try_into_mut() {
1082            Ok(buf) => buf,
1083            Err(buf) => Self::from(buf.as_ref()),
1084        }
1085    }
1086}
1087
1088/// Panics for a failed `copy_to_slice`, preserving the [`TryGetError`]
1089/// message.
1090#[cold]
1091#[inline(never)]
1092fn panic_try_get(error: TryGetError) -> ! {
1093    panic!("{error}");
1094}
1095
1096/// Resolves `range` against a buffer of length `len` into `(start, end)`.
1097///
1098/// Panics if a bound overflows `usize`, the range is inverted, or the end
1099/// exceeds `len`. Callers forward these as their documented slice panics.
1100fn resolve_range(len: usize, range: impl RangeBounds<usize>) -> (usize, usize) {
1101    let start = match range.start_bound() {
1102        Bound::Included(&n) => n,
1103        Bound::Excluded(&n) => n.checked_add(1).expect("range start overflow"),
1104        Bound::Unbounded => 0,
1105    };
1106    let end = match range.end_bound() {
1107        Bound::Included(&n) => n.checked_add(1).expect("range end overflow"),
1108        Bound::Excluded(&n) => n,
1109        Bound::Unbounded => len,
1110    };
1111    assert!(start <= end, "slice start must be <= end");
1112    assert!(end <= len, "slice out of bounds");
1113    (start, end)
1114}
1115
1116#[cfg(all(test, not(feature = "loom")))]
1117mod tests {
1118    use super::{
1119        super::{bufs::IoBufs, pool::BufferPoolConfig},
1120        *,
1121    };
1122    use bytes::{Bytes, BytesMut};
1123    use commonware_codec::{Decode, Encode, RangeCfg};
1124    use core::ops::Bound;
1125    use std::mem::size_of;
1126
1127    fn test_pool() -> BufferPool {
1128        cfg_if::cfg_if! {
1129            if #[cfg(miri)] {
1130                // Reduce the class limits to avoid slow atomics under miri.
1131                let pool_config = BufferPoolConfig::for_network()
1132                    .with_pool_min_size(0)
1133                    .with_max_per_class(commonware_utils::NZU32!(32));
1134            } else {
1135                let pool_config = BufferPoolConfig::for_network().with_pool_min_size(0);
1136            }
1137        }
1138        let mut registry = crate::telemetry::metrics::Registry::default();
1139        BufferPool::new(pool_config, &mut registry)
1140    }
1141
1142    #[test]
1143    fn test_iobuf_core_behaviors() {
1144        // Clone stays zero-copy for immutable buffers.
1145        let buf1 = IoBuf::from(vec![1u8; 1000]);
1146        let buf2 = buf1.clone();
1147        assert_eq!(buf1.as_ref().as_ptr(), buf2.as_ref().as_ptr());
1148
1149        // copy_from_slice creates an owned immutable buffer.
1150        let data = vec![1u8, 2, 3, 4, 5];
1151        let copied = IoBuf::copy_from_slice(&data);
1152        assert_eq!(copied, [1, 2, 3, 4, 5]);
1153        assert_eq!(copied.len(), 5);
1154        let empty = IoBuf::copy_from_slice(&[]);
1155        assert!(empty.is_empty());
1156
1157        // Equality works against both arrays and slices.
1158        let eq = IoBuf::from(b"hello");
1159        assert_eq!(eq, *b"hello");
1160        assert_eq!(eq, b"hello");
1161        assert_ne!(eq, *b"world");
1162        assert_ne!(eq, b"world");
1163        assert_eq!(IoBuf::from(b"hello"), IoBuf::from(b"hello"));
1164        assert_ne!(IoBuf::from(b"hello"), IoBuf::from(b"world"));
1165        let bytes: Bytes = IoBuf::from(b"bytes").into();
1166        assert_eq!(bytes.as_ref(), b"bytes");
1167
1168        // Buf trait operations keep `len()` and `remaining()` in sync.
1169        let mut buf = IoBuf::from(b"hello world");
1170        assert_eq!(buf.len(), buf.remaining());
1171        assert_eq!(buf.as_ref(), buf.chunk());
1172        assert_eq!(buf.remaining(), 11);
1173        buf.advance(6);
1174        assert_eq!(buf.chunk(), b"world");
1175        assert_eq!(buf.len(), buf.remaining());
1176
1177        // copy_to_bytes drains in-order and advances the source.
1178        let first = buf.copy_to_bytes(2);
1179        assert_eq!(&first[..], b"wo");
1180        let rest = buf.copy_to_bytes(3);
1181        assert_eq!(&rest[..], b"rld");
1182        assert_eq!(buf.remaining(), 0);
1183
1184        // Slicing remains zero-copy and supports all common range forms.
1185        let src = IoBuf::from(b"hello world");
1186        assert_eq!(src.slice(..5), b"hello");
1187        assert_eq!(src.slice(6..), b"world");
1188        assert_eq!(src.slice(3..8), b"lo wo");
1189        assert!(src.slice(5..5).is_empty());
1190    }
1191
1192    #[test]
1193    fn test_iobuf_from_conversions_are_zero_copy() {
1194        // The module doc guarantees every From conversion into IoBuf is
1195        // zero-copy. Pin payload pointer identity for each route.
1196
1197        // A vec with header room adopts its own allocation.
1198        let mut vec = Vec::with_capacity(128);
1199        vec.extend_from_slice(b"adopt");
1200        let ptr = vec.as_ptr();
1201        let buf = IoBuf::from(vec);
1202        assert_eq!(buf.as_ref().as_ptr(), ptr);
1203
1204        // An exactly-sized vec moves into Bytes without copying.
1205        let vec = b"exact".to_vec();
1206        let ptr = vec.as_ptr();
1207        let buf = IoBuf::from(vec);
1208        assert_eq!(buf.as_ref().as_ptr(), ptr);
1209
1210        // Bytes moves behind the external owner.
1211        let bytes = Bytes::from(b"bytes".to_vec());
1212        let ptr = bytes.as_ptr();
1213        let buf = IoBuf::from(bytes);
1214        assert_eq!(buf.as_ref().as_ptr(), ptr);
1215
1216        // BytesMut freezes in place.
1217        let mut bytes = BytesMut::with_capacity(16);
1218        bytes.put_slice(b"frozen");
1219        let ptr = bytes.as_ref().as_ptr();
1220        let buf = IoBuf::from(bytes);
1221        assert_eq!(buf.as_ref().as_ptr(), ptr);
1222
1223        // Static views point at the static data itself.
1224        static DATA: [u8; 4] = *b"data";
1225        let buf = IoBuf::from(&DATA[..]);
1226        assert_eq!(buf.as_ref().as_ptr(), DATA.as_ptr());
1227    }
1228
1229    #[test]
1230    fn test_iobuf_codec_roundtrip() {
1231        let cfg: RangeCfg<usize> = (0..=1024).into();
1232
1233        let original = IoBuf::from(b"hello world");
1234        let encoded = original.encode();
1235        let decoded = IoBuf::decode_cfg(encoded, &cfg).unwrap();
1236        assert_eq!(original, decoded);
1237
1238        let empty = IoBuf::default();
1239        let encoded = empty.encode();
1240        let decoded = IoBuf::decode_cfg(encoded, &cfg).unwrap();
1241        assert_eq!(empty, decoded);
1242
1243        let large_cfg: RangeCfg<usize> = (0..=20000).into();
1244        let large = IoBuf::from(vec![42u8; 10000]);
1245        let encoded = large.encode();
1246        let decoded = IoBuf::decode_cfg(encoded, &large_cfg).unwrap();
1247        assert_eq!(large, decoded);
1248
1249        let mut truncated = BytesMut::new();
1250        4usize.write(&mut truncated);
1251        truncated.extend_from_slice(b"xy");
1252        let mut truncated = truncated.freeze();
1253        assert!(IoBuf::read_cfg(&mut truncated, &cfg).is_err());
1254
1255        // Directly exercise the successful `read_cfg` path, not just decode helpers.
1256        let mut direct = BytesMut::new();
1257        4usize.write(&mut direct);
1258        direct.extend_from_slice(b"wxyz");
1259        let mut direct = direct.freeze();
1260        let decoded = IoBuf::read_cfg(&mut direct, &cfg).unwrap();
1261        assert_eq!(decoded, b"wxyz");
1262    }
1263
1264    #[test]
1265    #[should_panic(expected = "cannot advance")]
1266    fn test_iobuf_advance_past_end() {
1267        let mut buf = IoBuf::from(b"hello");
1268        buf.advance(10);
1269    }
1270
1271    #[test]
1272    fn test_iobuf_copy_to_slice_paths() {
1273        let mut buf = IoBuf::from(b"hello world");
1274        let mut dst = [0u8; 5];
1275        buf.copy_to_slice(&mut dst);
1276        assert_eq!(&dst, b"hello");
1277        assert_eq!(buf.as_ref(), b" world");
1278
1279        let mut dst = [0u8; 3];
1280        buf.try_copy_to_slice(&mut dst).unwrap();
1281        assert_eq!(&dst, b" wo");
1282
1283        // Requesting more than remaining fails without consuming anything.
1284        let mut dst = [0u8; 4];
1285        let err = buf.try_copy_to_slice(&mut dst).unwrap_err();
1286        assert_eq!(err.requested, 4);
1287        assert_eq!(err.available, 3);
1288        assert_eq!(buf.as_ref(), b"rld");
1289    }
1290
1291    #[test]
1292    #[should_panic(expected = "Not enough bytes remaining in buffer")]
1293    fn test_iobuf_copy_to_slice_past_end() {
1294        let mut buf = IoBuf::from(b"ab");
1295        let mut dst = [0u8; 3];
1296        buf.copy_to_slice(&mut dst);
1297    }
1298
1299    #[test]
1300    #[should_panic(expected = "copy_to_bytes out of bounds")]
1301    fn test_iobuf_copy_to_bytes_past_end() {
1302        let mut buf = IoBuf::from(b"ab");
1303        let _ = buf.copy_to_bytes(3);
1304    }
1305
1306    #[test]
1307    fn test_iobuf_slice_excluded_start_bound() {
1308        // Excluded start bounds resolve to start + 1.
1309        let buf = IoBuf::from(b"hello");
1310        let sliced = buf.slice((Bound::Excluded(0), Bound::Unbounded));
1311        assert_eq!(sliced, b"ello");
1312    }
1313
1314    #[test]
1315    #[should_panic(expected = "slice out of bounds")]
1316    fn test_iobuf_slice_out_of_bounds() {
1317        let buf = IoBuf::from(b"hello");
1318        let _ = buf.slice(..6);
1319    }
1320
1321    #[test]
1322    #[should_panic(expected = "slice start must be <= end")]
1323    fn test_iobuf_slice_inverted_range() {
1324        let buf = IoBuf::from(b"hello");
1325        #[allow(clippy::reversed_empty_ranges)]
1326        let _ = buf.slice(3..1);
1327    }
1328
1329    #[test]
1330    #[should_panic(expected = "range end overflow")]
1331    fn test_iobuf_slice_inclusive_end_overflow() {
1332        let buf = IoBuf::from(b"hello");
1333        let _ = buf.slice(0..=usize::MAX);
1334    }
1335
1336    #[test]
1337    #[should_panic(expected = "range start overflow")]
1338    fn test_iobuf_slice_excluded_start_overflow() {
1339        let buf = IoBuf::from(b"hello");
1340        let _ = buf.slice((Bound::Excluded(usize::MAX), Bound::Unbounded));
1341    }
1342
1343    #[test]
1344    fn test_iobuf_try_into_mut_empty_and_static() {
1345        // Empty views convert trivially.
1346        let buf = IoBuf::default().try_into_mut().expect("empty converts");
1347        assert!(buf.is_empty());
1348        assert_eq!(buf.capacity(), 0);
1349
1350        // Non-empty static views decline: there is no allocation to recover.
1351        let err = IoBuf::from(b"static").try_into_mut().unwrap_err();
1352        assert_eq!(err, b"static");
1353
1354        // Empty buffers convert to empty Bytes without touching an owner.
1355        let empty = Bytes::from(IoBuf::default());
1356        assert!(empty.is_empty());
1357
1358        // Empty static slices detach to the default representation.
1359        let empty = IoBuf::from(&b""[..]);
1360        assert!(empty.is_empty());
1361        assert!(empty.try_into_mut().is_ok());
1362    }
1363
1364    #[test]
1365    fn test_iobuf_split_to_consistent_across_backings() {
1366        // split_to on pooled and Bytes-backed IoBufs should produce identical results.
1367        let pool = test_pool();
1368        let mut pooled = pool.try_alloc(256).expect("pooled allocation");
1369        pooled.put_slice(b"hello world");
1370        let mut pooled_buf = pooled.freeze();
1371        let mut bytes_buf = IoBuf::from(b"hello world");
1372
1373        assert!(pooled_buf.is_pooled());
1374        assert!(!bytes_buf.is_pooled());
1375
1376        let pooled_empty = pooled_buf.split_to(0);
1377        let bytes_empty = bytes_buf.split_to(0);
1378        assert_eq!(pooled_empty, bytes_empty);
1379        assert_eq!(pooled_buf, bytes_buf);
1380        assert!(!pooled_empty.is_pooled());
1381
1382        let pooled_prefix = pooled_buf.split_to(5);
1383        let bytes_prefix = bytes_buf.split_to(5);
1384        assert_eq!(pooled_prefix, bytes_prefix);
1385        assert_eq!(pooled_buf, bytes_buf);
1386        assert!(pooled_prefix.is_pooled());
1387
1388        let pooled_rest = pooled_buf.split_to(pooled_buf.len());
1389        let bytes_rest = bytes_buf.split_to(bytes_buf.len());
1390        assert_eq!(pooled_rest, bytes_rest);
1391        assert_eq!(pooled_buf, bytes_buf);
1392        assert!(pooled_buf.is_empty());
1393        assert!(bytes_buf.is_empty());
1394        assert!(!pooled_buf.is_pooled());
1395    }
1396
1397    #[test]
1398    #[should_panic(expected = "split_to out of bounds")]
1399    fn test_iobuf_split_to_out_of_bounds() {
1400        let mut buf = IoBuf::from(b"abc");
1401        let _ = buf.split_to(4);
1402    }
1403
1404    #[test]
1405    fn test_iobufmut_core_behaviors() {
1406        // Build mutable buffers incrementally and freeze to immutable.
1407        let mut buf = IoBufMut::with_capacity(100);
1408        assert!(buf.capacity() >= 100);
1409        assert_eq!(buf.len(), 0);
1410        buf.put_slice(b"hello");
1411        buf.put_slice(b" world");
1412        assert_eq!(buf, b"hello world");
1413        assert_eq!(buf, &b"hello world"[..]);
1414        assert_eq!(buf.freeze(), b"hello world");
1415
1416        // `zeroed` creates readable initialized bytes, so `set_len` can shrink safely.
1417        let mut zeroed = IoBufMut::zeroed(10);
1418        assert_eq!(zeroed, &[0u8; 10]);
1419        // SAFETY: shrinking readable length to initialized region.
1420        unsafe { zeroed.set_len(5) };
1421        assert_eq!(zeroed, &[0u8; 5]);
1422        zeroed.as_mut()[..5].copy_from_slice(b"hello");
1423        assert_eq!(&zeroed.as_ref()[..5], b"hello");
1424        let frozen = zeroed.freeze();
1425        let vec: Vec<u8> = frozen.into();
1426        assert_eq!(&vec[..5], b"hello");
1427
1428        // Exercise pooled branch behavior for `is_empty`.
1429        let pool = test_pool();
1430        let mut pooled = pool.alloc(8);
1431        assert!(pooled.is_empty());
1432        pooled.put_slice(b"x");
1433        assert!(!pooled.is_empty());
1434    }
1435
1436    #[test]
1437    fn test_iobufmut_low_alignment_freeze_after_advance_recovers_capacity() {
1438        let mut buf = IoBufMut::with_capacity(16);
1439        assert_eq!(buf.capacity(), 16);
1440        buf.put_slice(b"abcdefghijklmnop");
1441        buf.advance(3);
1442        assert_eq!(buf.as_ref(), b"defghijklmnop");
1443        assert_eq!(buf.capacity(), 13);
1444
1445        let frozen = buf.freeze();
1446        assert_eq!(frozen.as_ref(), b"defghijklmnop");
1447
1448        let recovered = frozen
1449            .try_into_mut()
1450            .expect("unique low-alignment buffer should recover mutability");
1451        assert_eq!(recovered.as_ref(), b"defghijklmnop");
1452        assert_eq!(recovered.capacity(), 13);
1453    }
1454
1455    #[test]
1456    fn test_iobufmut_buf_trait() {
1457        // Buf trait on IoBufMut: remaining/chunk/advance should work like BytesMut.
1458        let mut buf = IoBufMut::from(b"hello world");
1459        assert_eq!(buf.remaining(), 11);
1460        assert_eq!(buf.chunk(), b"hello world");
1461
1462        buf.advance(6);
1463        assert_eq!(buf.remaining(), 5);
1464        assert_eq!(buf.chunk(), b"world");
1465
1466        buf.advance(5);
1467        assert_eq!(buf.remaining(), 0);
1468        assert!(buf.chunk().is_empty());
1469    }
1470
1471    #[test]
1472    #[should_panic(expected = "cannot advance")]
1473    fn test_iobufmut_advance_past_end() {
1474        let mut buf = IoBufMut::from(b"hello");
1475        buf.advance(10);
1476    }
1477
1478    #[test]
1479    fn test_iobufmut_copy_to_slice_tracks_len_and_cap() {
1480        // copy_to_slice must shrink len and cap in lockstep with the pointer
1481        // advance: the front-heap release path derives the allocation size
1482        // from ptr + cap, so a cap mismatch would corrupt the dealloc layout.
1483        let mut buf = IoBufMut::with_capacity(16);
1484        buf.put_slice(b"abcdefgh");
1485
1486        let mut dst = [0u8; 3];
1487        buf.copy_to_slice(&mut dst);
1488        assert_eq!(&dst, b"abc");
1489        assert_eq!(buf.as_ref(), b"defgh");
1490        assert_eq!(buf.len(), 5);
1491        assert_eq!(buf.capacity(), 13);
1492
1493        // try_copy_to_slice success mirrors copy_to_slice.
1494        let mut dst = [0u8; 2];
1495        buf.try_copy_to_slice(&mut dst).unwrap();
1496        assert_eq!(&dst, b"de");
1497        assert_eq!(buf.capacity(), 11);
1498
1499        // Requesting more than remaining fails without consuming anything.
1500        let mut dst = [0u8; 4];
1501        let err = buf.try_copy_to_slice(&mut dst).unwrap_err();
1502        assert_eq!(err.requested, 4);
1503        assert_eq!(err.available, 3);
1504        assert_eq!(buf.as_ref(), b"fgh");
1505        assert_eq!(buf.capacity(), 11);
1506
1507        // Freeze and recover: the owner must observe a consistent allocation
1508        // for the advanced handle (view offset 5 leaves 16 - 5 = 11 bytes of
1509        // capacity), and the final drop (checked under miri) must deallocate
1510        // with the original layout.
1511        let frozen = buf.freeze();
1512        assert_eq!(frozen.as_ref(), b"fgh");
1513        let recovered = frozen.try_into_mut().expect("unique buffer recovers");
1514        assert_eq!(recovered.as_ref(), b"fgh");
1515        assert_eq!(recovered.capacity(), 11);
1516    }
1517
1518    #[test]
1519    fn test_iobufmut_write_after_partial_advance_appends_at_tail() {
1520        // A partial advance moves the view start while retaining readable
1521        // bytes. A subsequent write must land at the initialized tail so old
1522        // and new data stay adjacent, with len and cap tracked in lockstep.
1523        let mut buf = IoBufMut::with_capacity(16);
1524        buf.put_slice(b"hello");
1525        buf.advance(2);
1526        buf.put_slice(b"world");
1527        assert_eq!(buf.as_ref(), b"lloworld");
1528        assert_eq!(buf.len(), 8);
1529        assert_eq!(buf.capacity(), 14);
1530
1531        // The same holds for pooled buffers, whose cursor bookkeeping feeds
1532        // the thread-cache return path instead of a dealloc layout.
1533        let pool = test_pool();
1534        let mut buf = pool.alloc(16);
1535        let capacity = buf.capacity();
1536        buf.put_slice(b"hello");
1537        buf.advance(2);
1538        buf.put_slice(b"world");
1539        assert_eq!(buf.as_ref(), b"lloworld");
1540        assert_eq!(buf.len(), 8);
1541        assert_eq!(buf.capacity(), capacity - 2);
1542    }
1543
1544    #[test]
1545    #[should_panic(expected = "Not enough bytes remaining in buffer")]
1546    fn test_iobufmut_copy_to_slice_past_end() {
1547        let mut buf = IoBufMut::from(b"ab");
1548        let mut dst = [0u8; 3];
1549        buf.copy_to_slice(&mut dst);
1550    }
1551
1552    #[test]
1553    #[should_panic(expected = "copy_to_bytes out of bounds")]
1554    fn test_iobufmut_copy_to_bytes_past_end() {
1555        let mut buf = IoBufMut::from(b"ab");
1556        let _ = buf.copy_to_bytes(3);
1557    }
1558
1559    #[test]
1560    fn test_iobufmut_put_bytes_success() {
1561        let mut buf = IoBufMut::with_capacity(8);
1562        buf.put_bytes(7, 5);
1563        assert_eq!(buf.as_ref(), &[7u8; 5]);
1564        assert_eq!(buf.len(), 5);
1565        assert_eq!(buf.remaining_mut(), 3);
1566    }
1567
1568    #[test]
1569    fn test_iobufmut_put_multi_chunk_source() {
1570        let mut buf = IoBufMut::with_capacity(8);
1571        buf.put((&b"hel"[..]).chain(&b"lo"[..]));
1572        assert_eq!(buf.as_ref(), b"hello");
1573        assert_eq!(buf.remaining_mut(), 3);
1574    }
1575
1576    #[test]
1577    #[should_panic(expected = "cannot advance past end of buffer")]
1578    fn test_iobufmut_put_slice_past_capacity() {
1579        let mut buf = IoBufMut::with_capacity(4);
1580        buf.put_slice(b"hello");
1581    }
1582
1583    #[test]
1584    #[should_panic(expected = "cannot advance past end of buffer")]
1585    fn test_iobufmut_put_bytes_past_capacity() {
1586        let mut buf = IoBufMut::with_capacity(4);
1587        buf.put_bytes(0, 5);
1588    }
1589
1590    #[test]
1591    #[should_panic(expected = "cannot advance past end of buffer")]
1592    fn test_iobufmut_advance_mut_past_capacity() {
1593        let mut buf = IoBufMut::with_capacity(4);
1594        // SAFETY: the call panics on the bounds check before any byte in the
1595        // advanced region could be observed.
1596        unsafe { buf.advance_mut(5) };
1597    }
1598
1599    #[test]
1600    #[should_panic(expected = "cannot advance past end of buffer")]
1601    fn test_iobufmut_put_past_capacity() {
1602        let mut buf = IoBufMut::with_capacity(4);
1603        buf.put(&b"hello"[..]);
1604    }
1605
1606    #[test]
1607    fn test_iobuf_additional_conversion_and_trait_paths() {
1608        let pool = test_pool();
1609
1610        let mut pooled_mut = pool.alloc(4);
1611        pooled_mut.put_slice(b"data");
1612        let pooled = pooled_mut.freeze();
1613        assert!(!pooled.as_ptr().is_null());
1614
1615        // A vec with spare capacity adopts its allocation, so the unique
1616        // immutable view recovers mutability zero-copy.
1617        let mut adopted_vec = Vec::with_capacity(64);
1618        adopted_vec.extend_from_slice(&[1u8, 2, 3]);
1619        let unique = IoBuf::from(adopted_vec);
1620        let unique_mut = unique.try_into_mut().expect("adopted vec should convert");
1621        assert_eq!(unique_mut.as_ref(), &[1u8, 2, 3]);
1622
1623        let shared = IoBuf::from(vec![4u8, 5, 6]);
1624        let _shared_clone = shared.clone();
1625        assert!(shared.try_into_mut().is_err());
1626
1627        // External-backed views (exactly-sized vecs, `Bytes`) always decline
1628        // mutable recovery.
1629        let external = IoBuf::from(vec![7u8, 8, 9]);
1630        assert!(external.try_into_mut().is_err());
1631
1632        let expected: &[u8] = &[9u8, 8];
1633        let eq_buf = IoBuf::from(vec![9u8, 8]);
1634        assert!(PartialEq::<[u8]>::eq(&eq_buf, expected));
1635
1636        let static_slice: &'static [u8] = b"static";
1637        assert_eq!(IoBuf::from(static_slice), b"static");
1638
1639        let mut pooled_mut = pool.alloc(3);
1640        pooled_mut.put_slice(b"xyz");
1641        let pooled = pooled_mut.freeze();
1642        let vec_out: Vec<u8> = pooled.clone().into();
1643        let bytes_out: Bytes = pooled.into();
1644        assert_eq!(vec_out, b"xyz");
1645        assert_eq!(bytes_out.as_ref(), b"xyz");
1646    }
1647
1648    #[test]
1649    fn test_iobuf_from_bytes_zero_copy_round_trip() {
1650        // Bytes -> IoBuf is zero-copy: the handle points into the payload.
1651        let bytes = Bytes::from(vec![1u8; 64]);
1652        let payload_ptr = bytes.as_ptr();
1653        let buf = IoBuf::from(bytes.clone());
1654        assert_eq!(buf.as_ptr(), payload_ptr);
1655        assert_eq!(buf, bytes.as_ref());
1656
1657        // IoBuf -> Bytes on an external backing uses slice_ref: same payload,
1658        // no copy, no extra owner box.
1659        let out: Bytes = buf.into();
1660        assert_eq!(out.as_ptr(), payload_ptr);
1661        assert_eq!(out, bytes);
1662
1663        // Sliced external views convert through slice_ref too.
1664        let sliced = IoBuf::from(bytes).slice(8..32);
1665        let sliced_ptr = sliced.as_ptr();
1666        let sliced_out: Bytes = sliced.into();
1667        assert_eq!(sliced_out.as_ptr(), sliced_ptr);
1668        assert_eq!(sliced_out.len(), 24);
1669    }
1670
1671    #[test]
1672    fn test_iobuf_from_bytes_mut_zero_copy() {
1673        let mut bytes = BytesMut::with_capacity(32);
1674        bytes.extend_from_slice(b"hello");
1675        let payload_ptr = bytes.as_ref().as_ptr();
1676        let buf = IoBuf::from(bytes);
1677        assert_eq!(buf.as_ptr(), payload_ptr);
1678        assert_eq!(buf, b"hello");
1679    }
1680
1681    #[test]
1682    fn test_iobuf_static_into_bytes_uses_from_static() {
1683        let buf = IoBuf::from(b"static-payload");
1684        let payload_ptr = buf.as_ptr();
1685        let bytes: Bytes = buf.into();
1686        assert_eq!(bytes.as_ptr(), payload_ptr);
1687        assert_eq!(bytes.as_ref(), b"static-payload");
1688    }
1689
1690    #[test]
1691    fn test_iobuf_vec_adoption_round_trip_zero_copy() {
1692        // Vec with spare capacity -> IoBuf adopts the allocation, and
1693        // try_into_mut recovers a writable handle at the same address.
1694        let mut vec = Vec::with_capacity(128);
1695        vec.extend_from_slice(b"adopted payload");
1696        let base = vec.as_ptr() as usize;
1697        let buf = IoBuf::from(vec);
1698        assert_eq!(buf.as_ptr() as usize, base);
1699
1700        let mut recovered = buf
1701            .try_into_mut()
1702            .expect("adopted vec recovers mutability zero-copy");
1703        assert_eq!(recovered.as_mut_ptr() as usize, base);
1704        assert_eq!(recovered.as_ref(), b"adopted payload");
1705        assert!(recovered.capacity() > recovered.len());
1706        recovered.put_slice(b"!");
1707        assert_eq!(recovered.as_ref(), b"adopted payload!");
1708    }
1709
1710    #[test]
1711    fn test_iobuf_read_cfg_zero_copy_from_iobuf_source() {
1712        // Decoding an IoBuf field from an IoBuf source must not copy the
1713        // payload: copy_to_bytes carves a zero-copy slice and From wraps it.
1714        let cfg: RangeCfg<usize> = (0..=1024).into();
1715        let mut source = IoBuf::from(IoBuf::from(vec![7u8; 100]).encode());
1716        let prefix = source.len() - 100;
1717        let payload_ptr = source.as_ref()[prefix..].as_ptr();
1718        let decoded = IoBuf::read_cfg(&mut source, &cfg).unwrap();
1719        assert_eq!(decoded.len(), 100);
1720        assert_eq!(decoded.as_ptr(), payload_ptr);
1721        assert_eq!(decoded, [7u8; 100]);
1722    }
1723
1724    #[test]
1725    #[should_panic(expected = "cannot advance")]
1726    fn test_iobufmut_put_does_not_trust_lying_buf() {
1727        // `Buf` is a safe trait: a misbehaving source may hand out chunks
1728        // larger than its reported remaining(). `put` must bound each copy by
1729        // its own capacity and panic instead of overflowing the buffer.
1730        struct LyingBuf;
1731        impl Buf for LyingBuf {
1732            fn remaining(&self) -> usize {
1733                1
1734            }
1735            fn chunk(&self) -> &[u8] {
1736                &[0xAB; 64]
1737            }
1738            fn advance(&mut self, _cnt: usize) {}
1739        }
1740
1741        let mut buf = IoBufMut::with_capacity(8);
1742        buf.put(LyingBuf);
1743    }
1744
1745    #[test]
1746    #[cfg(target_pointer_width = "64")]
1747    fn test_iobuf_handle_sizes() {
1748        assert_eq!(size_of::<IoBuf>(), 24);
1749        assert_eq!(size_of::<IoBufMut>(), 32);
1750    }
1751
1752    #[test]
1753    fn test_iobuf_into_mut_with_pool() {
1754        let pool = test_pool();
1755
1756        // Unique buffers recover mutability without copying.
1757        let mut unique = pool.alloc(4);
1758        unique.put_slice(b"data");
1759        let unique_ptr = unique.as_mut_ptr();
1760        let mut recovered = unique.freeze().into_mut_with_pool(&pool);
1761        assert_eq!(recovered.as_ref(), b"data");
1762        assert_eq!(recovered.as_mut_ptr(), unique_ptr);
1763
1764        // Shared buffers allocate from the pool and copy readable bytes.
1765        let mut shared = pool.alloc(4);
1766        shared.put_slice(b"copy");
1767        let shared = shared.freeze();
1768        let shared_ptr = shared.as_ptr();
1769        let _clone = shared.clone();
1770        let mut copied = shared.into_mut_with_pool(&pool);
1771        assert_eq!(copied.as_ref(), b"copy");
1772        assert_ne!(copied.as_mut_ptr() as *const u8, shared_ptr);
1773        assert!(copied.is_pooled());
1774
1775        // Recovery after transient slices are dropped is zero-copy and preserves
1776        // the full readable length and capacity.
1777        let mut mirror = pool.alloc(8);
1778        mirror.put_slice(b"abcdefgh");
1779        let mirror_cap = mirror.capacity();
1780        let mirror_ptr = mirror.as_mut_ptr();
1781        let frozen = mirror.freeze();
1782        let head = frozen.slice(0..3);
1783        let tail = frozen.slice(5..8);
1784        drop(head);
1785        drop(tail);
1786        let mut recovered = frozen.into_mut_with_pool(&pool);
1787        assert_eq!(recovered.as_ref(), b"abcdefgh");
1788        assert_eq!(recovered.as_mut_ptr(), mirror_ptr);
1789        assert_eq!(recovered.capacity(), mirror_cap);
1790    }
1791
1792    #[test]
1793    fn test_iobufmut_additional_conversion_and_trait_paths() {
1794        // Basic mutable operations should keep readable bytes consistent.
1795        let mut buf = IoBufMut::from([1u8, 2, 3, 4]);
1796        assert!(!buf.is_empty());
1797        buf.truncate(2);
1798        assert_eq!(buf.as_ref(), &[1u8, 2]);
1799        buf.clear();
1800        assert!(buf.is_empty());
1801        buf.put_slice(b"xyz");
1802
1803        // Equality should work across slice, array, and byte-string forms.
1804        let expected: &[u8] = b"xyz";
1805        assert!(PartialEq::<[u8]>::eq(&buf, expected));
1806        assert!(buf == b"xyz"[..]);
1807        assert!(buf == *b"xyz");
1808        assert!(buf == b"xyz");
1809
1810        // Conversions from common owned/shared containers preserve contents.
1811        let from_array = IoBufMut::from([7u8, 8]);
1812        assert_eq!(from_array.as_ref(), &[7u8, 8]);
1813
1814        let from_bytesmut = IoBufMut::from(BytesMut::from(&b"hi"[..]));
1815        assert_eq!(from_bytesmut.as_ref(), b"hi");
1816
1817        let from_bytes = IoBufMut::from(Bytes::from_static(b"ok"));
1818        assert_eq!(from_bytes.as_ref(), b"ok");
1819
1820        // `Bytes::from_static` cannot be converted to mutable without copy.
1821        let from_iobuf = IoBufMut::from(IoBuf::from(Bytes::from_static(b"io")));
1822        assert_eq!(from_iobuf.as_ref(), b"io");
1823    }
1824
1825    #[test]
1826    fn test_iobufmut_from_bytesmut_preserves_capacity() {
1827        let mut bytes = BytesMut::with_capacity(100);
1828        bytes.put_slice(b"abc");
1829        let cap = bytes.capacity();
1830        let buf = IoBufMut::from(bytes);
1831        assert_eq!(buf.as_ref(), b"abc");
1832        assert_eq!(buf.capacity(), cap);
1833
1834        // An empty reserved BytesMut keeps its reservation writable.
1835        let bytes = BytesMut::with_capacity(64);
1836        let cap = bytes.capacity();
1837        let mut buf = IoBufMut::from(bytes);
1838        assert!(buf.is_empty());
1839        assert_eq!(buf.capacity(), cap);
1840        buf.put_bytes(7, cap);
1841        assert_eq!(buf.len(), cap);
1842    }
1843
1844    #[test]
1845    fn test_iobufmut_from_vec_preserves_capacity() {
1846        let mut vec = Vec::with_capacity(100);
1847        vec.extend_from_slice(b"abc");
1848        let buf = IoBufMut::from(vec);
1849        assert_eq!(buf.as_ref(), b"abc");
1850        assert_eq!(buf.capacity(), 100);
1851
1852        // An empty reservation converts to a writable buffer of the same
1853        // capacity.
1854        let mut buf = IoBufMut::from(Vec::with_capacity(64));
1855        assert!(buf.is_empty());
1856        assert_eq!(buf.capacity(), 64);
1857        buf.put_bytes(7, 64);
1858        assert_eq!(buf.len(), 64);
1859    }
1860
1861    #[test]
1862    #[should_panic(expected = "front heap layout size overflow")]
1863    fn test_iobufmut_with_capacity_rejects_oversized_request() {
1864        // Constructs the layout (and must panic) before any allocation.
1865        let _ = IoBufMut::with_capacity(isize::MAX as usize);
1866    }
1867
1868    #[test]
1869    fn test_iobuf_aligned_public_paths() {
1870        // Exercise the public IoBuf/IoBufMut API through the untracked aligned
1871        // backing: write, advance, copy_to_bytes, freeze, slice, split_to,
1872        // try_into_mut, and From/Into conversions.
1873        static ARRAY: &[u8; 4] = b"wxyz";
1874
1875        let alignment = NonZeroUsize::new(64).expect("non-zero alignment");
1876
1877        // Start from a non-zero untracked aligned buffer to cover the public mutable API.
1878        let mut aligned_mut = IoBufMut::with_alignment(8, alignment);
1879        assert!(!aligned_mut.is_pooled());
1880        assert!(aligned_mut.is_empty());
1881        assert_eq!(aligned_mut.capacity(), 8);
1882        assert!((aligned_mut.as_mut_ptr() as usize).is_multiple_of(64));
1883
1884        aligned_mut.put_slice(b"abcdefgh");
1885        assert_eq!(aligned_mut.as_mut(), b"abcdefgh");
1886        assert_eq!(aligned_mut.chunk(), b"abcdefgh");
1887        aligned_mut.advance(2);
1888        assert_eq!(aligned_mut.chunk(), b"cdefgh");
1889
1890        let partial = aligned_mut.copy_to_bytes(2);
1891        assert_eq!(partial.as_ref(), b"cd");
1892        assert_eq!(aligned_mut.as_ref(), b"efgh");
1893        let empty = aligned_mut.copy_to_bytes(0);
1894        assert!(empty.is_empty());
1895        assert_eq!(aligned_mut.as_ref(), b"efgh");
1896
1897        aligned_mut.clear();
1898        assert!(aligned_mut.is_empty());
1899        aligned_mut.put_slice(ARRAY);
1900        assert!(aligned_mut == ARRAY);
1901
1902        // Full aligned drains should use the owner-transfer path, including len == 0 first.
1903        let mut fully_drained = IoBufMut::with_alignment(4, alignment);
1904        fully_drained.put_slice(b"lmno");
1905        let empty = fully_drained.copy_to_bytes(0);
1906        assert!(empty.is_empty());
1907        assert_eq!(fully_drained.as_ref(), b"lmno");
1908        let drained = fully_drained.copy_to_bytes(4);
1909        assert_eq!(drained.as_ref(), b"lmno");
1910        assert!(fully_drained.is_empty());
1911
1912        // Freeze to an immutable aligned `IoBuf` and exercise its view/Buf dispatch.
1913        let aligned = aligned_mut.freeze();
1914        assert!(!aligned.is_pooled());
1915        assert_eq!(aligned.as_ref(), &ARRAY[..]);
1916        assert!(aligned == ARRAY);
1917        assert!(!aligned.as_ptr().is_null());
1918        assert_eq!(aligned.slice(..2), b"wx");
1919        assert_eq!(aligned.slice(1..), b"xyz");
1920        assert_eq!(aligned.slice(1..=2), b"xy");
1921        assert_eq!(aligned.chunk(), b"wxyz");
1922
1923        let mut split = aligned.clone();
1924        let prefix = split.split_to(2);
1925        assert_eq!(prefix, b"wx");
1926        assert_eq!(split, b"yz");
1927
1928        let mut advanced = aligned.clone();
1929        advanced.advance(2);
1930        assert_eq!(advanced.chunk(), b"yz");
1931
1932        // Partial and full immutable drains should preserve the aligned backing behavior.
1933        let mut drained = aligned.clone();
1934        let empty = drained.copy_to_bytes(0);
1935        assert!(empty.is_empty());
1936        assert_eq!(drained.as_ref(), &ARRAY[..]);
1937        let first = drained.copy_to_bytes(1);
1938        assert_eq!(first.as_ref(), b"w");
1939        let rest = drained.copy_to_bytes(3);
1940        assert_eq!(rest.as_ref(), b"xyz");
1941        assert_eq!(drained.remaining(), 0);
1942
1943        // Unique aligned immutable buffers can become mutable again.
1944        let mut unique_source = IoBufMut::zeroed_with_alignment(4, alignment);
1945        unique_source.as_mut().copy_from_slice(b"pqrs");
1946        let unique = unique_source.freeze();
1947        let recovered = unique
1948            .try_into_mut()
1949            .expect("unique aligned iobuf should recover mutability");
1950        assert_eq!(recovered.as_ref(), b"pqrs");
1951
1952        // Shared aligned immutable buffers must reject the mutable conversion.
1953        let mut shared_source = IoBufMut::zeroed_with_alignment(4, alignment);
1954        shared_source.as_mut().copy_from_slice(b"tuvw");
1955        let shared = shared_source.freeze();
1956        let _shared_clone = shared.clone();
1957        assert!(shared.try_into_mut().is_err());
1958
1959        // Owned/container conversions should preserve bytes for aligned backings.
1960        let vec_out: Vec<u8> = aligned.clone().into();
1961        let bytes_out: Bytes = aligned.into();
1962        assert_eq!(vec_out, ARRAY.to_vec());
1963        assert_eq!(bytes_out.as_ref(), &ARRAY[..]);
1964
1965        let from_array = IoBuf::from(ARRAY);
1966        assert_eq!(from_array, b"wxyz");
1967
1968        let iobufs = IoBufs::from(ARRAY);
1969        assert_eq!(iobufs.chunk(), b"wxyz");
1970    }
1971
1972    #[test]
1973    fn test_iobufmut_aligned_zero_length_constructors() {
1974        let alignment = NonZeroUsize::new(64).expect("non-zero alignment");
1975
1976        let with_alignment = IoBufMut::with_alignment(0, alignment);
1977        assert!(with_alignment.is_empty());
1978        assert_eq!(with_alignment.len(), 0);
1979        assert_eq!(with_alignment.capacity(), 0);
1980
1981        let zeroed = IoBufMut::zeroed_with_alignment(0, alignment);
1982        assert!(zeroed.is_empty());
1983        assert_eq!(zeroed.len(), 0);
1984        assert_eq!(zeroed.capacity(), 0);
1985
1986        // Zero-sized buffers do not allocate, so alignment is not validated.
1987        let invalid_alignment = NonZeroUsize::new(3).expect("non-zero alignment");
1988        assert_eq!(IoBufMut::with_alignment(0, invalid_alignment).capacity(), 0);
1989        assert_eq!(
1990            IoBufMut::zeroed_with_alignment(0, invalid_alignment).capacity(),
1991            0
1992        );
1993    }
1994
1995    #[test]
1996    fn test_iobufmut_aligned_capacity_stable_across_recovery() {
1997        // High-alignment requests round the usable region up to the header
1998        // alignment. The handle must report that capacity from construction
1999        // so a freeze/try_into_mut round trip cannot grow it.
2000        let alignment = NonZeroUsize::new(4096).expect("non-zero alignment");
2001        let mut buf = IoBufMut::with_alignment(100, alignment);
2002        assert_eq!(buf.capacity(), 104);
2003
2004        buf.put_slice(b"data");
2005        let recovered = buf
2006            .freeze()
2007            .try_into_mut()
2008            .expect("unique native view recovers");
2009        assert_eq!(recovered.capacity(), 104);
2010
2011        // Zeroed variant: the rounded tail is writable and zero-initialized,
2012        // while len stays at the request.
2013        let zeroed = IoBufMut::zeroed_with_alignment(100, alignment);
2014        assert_eq!(zeroed.len(), 100);
2015        assert_eq!(zeroed.capacity(), 104);
2016
2017        // Multiple-of-8 requests stay exact.
2018        let exact = IoBufMut::with_alignment(128, alignment);
2019        assert_eq!(exact.capacity(), 128);
2020    }
2021
2022    #[test]
2023    #[should_panic(expected = "set_len(9) exceeds capacity(8)")]
2024    fn test_iobufmut_set_len_overflow() {
2025        let mut buf = IoBufMut::with_capacity(8);
2026        // SAFETY: this will panic before any read.
2027        unsafe { buf.set_len(9) };
2028    }
2029
2030    #[cfg(feature = "arbitrary")]
2031    mod conformance {
2032        use super::IoBuf;
2033        use commonware_codec::conformance::CodecConformance;
2034
2035        commonware_conformance::conformance_tests! {
2036            CodecConformance<IoBuf>
2037        }
2038    }
2039}