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`]. This module implements
6//! slicing, freezing, mutable recovery, conversions, and codec integration for
7//! a single buffer.
8
9use super::{
10    owner::{Descriptor, Owner, OwnerCell, Storage},
11    panic_advance,
12};
13use bytes::{BufMut, Bytes, BytesMut, TryGetError};
14use commonware_codec::{Buf, BufsMut, EncodeSize, Error, Input, OwnedBytes, RangeCfg, Read, Write};
15use std::{
16    alloc::Layout,
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 lives in `owner`. It describes a native allocation
35/// this handle owns alone, points to an internal owner record (for shared
36/// allocations, allocations the word cannot describe, and [`Bytes`]), or is
37/// empty for static slices and detached empty views. Cursor methods such as
38/// `remaining`, `chunk`, `advance`, and `copy_to_slice` use only `ptr` and
39/// `len`. Cloning, dropping, slicing, splitting, and `copy_to_bytes` use
40/// `owner` on colder lifecycle paths.
41///
42/// Cloning and slicing are zero-copy, and so is every `From` conversion into
43/// `IoBuf`. The first clone, slice, or partial split of an unshared native
44/// buffer allocates its owner record. Use [`IoBuf::copy_from_slice`] to copy
45/// borrowed data.
46pub struct IoBuf {
47    ptr: NonNull<u8>,
48    len: usize,
49    owner: OwnerCell,
50}
51
52// Keep the handle at three words outside loom builds, whose atomics are larger.
53#[cfg(not(feature = "loom"))]
54const _: () = assert!(size_of::<IoBuf>() == 3 * size_of::<usize>());
55
56// SAFETY: immutable handles expose read-only bytes and synchronize shared
57// ownership through the owner refcount.
58unsafe impl Send for IoBuf {}
59// SAFETY: shared access is read-only and lifecycle state is atomic.
60unsafe impl Sync for IoBuf {}
61
62// Debug omits the data pointer because raw addresses differ across
63// identically-seeded deterministic runs and would leak heap layout into logs.
64impl std::fmt::Debug for IoBuf {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("IoBuf").field("len", &self.len).finish()
67    }
68}
69
70impl Clone for IoBuf {
71    #[inline]
72    fn clone(&self) -> Self {
73        let owner = self.retain();
74        Self {
75            ptr: self.ptr,
76            len: self.len,
77            owner: OwnerCell::new(owner),
78        }
79    }
80}
81
82impl Drop for IoBuf {
83    #[inline]
84    fn drop(&mut self) {
85        // Only a final release reads the view, so a shared drop never loads
86        // `ptr` or `len`. A descriptor and a record whose count reads 1 release
87        // through one outlined call. That keeps this inlined drop small enough
88        // for drops of multi-chunk containers to unroll. When other views drop
89        // concurrently and leave this one final, `drop_view` releases the
90        // record.
91        let owner = self.owner.get();
92        // SAFETY: this handle owns what its word owns, as an immutable view.
93        if unsafe { owner.drop_view() } {
94            // SAFETY: a true result leaves this handle as the word's only
95            // owner, and `ptr` and `len` are its view.
96            unsafe { owner.release_unique(self.ptr, self.len) };
97        }
98    }
99}
100
101impl IoBuf {
102    /// Takes a reference to this handle's allocation for a new view through a
103    /// shared borrow.
104    ///
105    /// A record gains a reference, and an empty word needs none. A descriptor
106    /// is promoted to a record through the outlined [`Self::promote`].
107    #[inline(always)]
108    fn retain(&self) -> Owner {
109        let owner = self.owner.load();
110        if let Some(descriptor) = owner.descriptor() {
111            // SAFETY: `descriptor` was just loaded from this handle's cell.
112            return unsafe { self.promote(descriptor) };
113        }
114        // SAFETY: this handle holds a reference to the record, if any, for the
115        // whole call. Static views have no owner.
116        unsafe { owner.clone_shared() };
117        owner
118    }
119
120    /// Promotes this handle's descriptor to a record and returns a reference
121    /// for a new view.
122    ///
123    /// # Safety
124    ///
125    /// `descriptor` must have been loaded from this handle's cell.
126    // A descriptor is promoted once, on the first share of its buffer, so this
127    // stays out of line and the sharing paths inline only a tag test and a
128    // refcount increment.
129    //
130    // Taking and returning single words lets the sharing path build the new
131    // handle in registers and store it straight into its destination. A
132    // handle returned through a stack slot can be reloaded with loads wider
133    // than the stores that filled it, for example by the derived clone of an
134    // array of handles, and that stalls store forwarding.
135    #[cold]
136    #[inline(never)]
137    unsafe fn promote(&self, descriptor: Descriptor) -> Owner {
138        // SAFETY: the caller loaded `descriptor` from this cell, and `ptr` and
139        // `len` are the view of this handle, borrowed for the whole call.
140        unsafe { self.owner.promote(descriptor, self.ptr, self.len) }
141    }
142
143    /// Create a buffer by copying data from a slice.
144    ///
145    /// Use this when you have a non-static `&[u8]` that needs owned storage.
146    /// For static slices, prefer [`IoBuf::from`] which is zero-copy.
147    ///
148    /// The copy lands in a native heap allocation, so the result supports
149    /// zero-copy [`IoBuf::try_into_mut`].
150    pub fn copy_from_slice(data: &[u8]) -> Self {
151        IoBufMut::from(data).freeze()
152    }
153
154    #[inline]
155    fn from_static(slice: &'static [u8]) -> Self {
156        if slice.is_empty() {
157            return Self::default();
158        }
159        let ptr = NonNull::new(slice.as_ptr().cast_mut()).expect("static slice data is non-null");
160        Self {
161            ptr,
162            len: slice.len(),
163            owner: OwnerCell::empty(),
164        }
165    }
166
167    /// Number of bytes remaining in the buffer.
168    #[inline]
169    pub const fn len(&self) -> usize {
170        self.len
171    }
172
173    /// Whether the buffer is empty.
174    #[inline]
175    pub const fn is_empty(&self) -> bool {
176        self.len == 0
177    }
178
179    /// Get raw pointer to the first readable byte.
180    #[inline]
181    pub const fn as_ptr(&self) -> *const u8 {
182        self.ptr.as_ptr()
183    }
184
185    /// Returns a slice of self for the provided range (zero-copy).
186    ///
187    /// An empty range returns a detached empty buffer that holds no
188    /// allocation.
189    ///
190    /// # Panics
191    ///
192    /// Panics if the range is out of bounds of the readable bytes, if its
193    /// start is greater than its end, or if an inclusive bound overflows.
194    #[inline]
195    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
196        let (start, end) = resolve_range(self.len, range);
197        if start == end {
198            return Self::default();
199        }
200
201        let owner = self.retain();
202        Self {
203            // SAFETY: range resolution bounds `start <= self.len`.
204            ptr: unsafe { self.ptr.add(start) },
205            len: end - start,
206            owner: OwnerCell::new(owner),
207        }
208    }
209
210    /// Splits the buffer into two at the given index.
211    ///
212    /// Afterwards `self` contains bytes `[at, len)`, and the returned [`IoBuf`]
213    /// contains bytes `[0, at)`.
214    ///
215    /// This is an `O(1)` zero-copy operation. A split that leaves both halves
216    /// nonempty shares the allocation, which allocates the owner record of an
217    /// unshared native buffer. The empty half of a split at either end of a
218    /// nonempty buffer is detached and holds no allocation. Splitting an empty
219    /// buffer leaves `self` unchanged.
220    ///
221    /// # Panics
222    ///
223    /// Panics if `at > len`.
224    // An outlined copy would save and restore the registers that stay live
225    // across the cold promotion call in `retain_mut`, and that costs more than
226    // the split itself.
227    #[inline(always)]
228    pub fn split_to(&mut self, at: usize) -> Self {
229        if at > self.len {
230            panic_split_to(at, self.len);
231        }
232        if at == 0 {
233            return Self::default();
234        }
235        if at == self.len {
236            return std::mem::take(self);
237        }
238
239        let (ptr, len) = (self.ptr, self.len);
240        // SAFETY: `ptr` and `len` are this handle's view, and the prefix
241        // takes the returned reference.
242        let owner = unsafe { self.owner.retain_mut(ptr, len) };
243        // SAFETY: `at < len`, so advancing within the current readable region is in bounds.
244        self.ptr = unsafe { ptr.add(at) };
245        self.len = len - at;
246        Self {
247            ptr,
248            len: at,
249            owner: OwnerCell::new(owner),
250        }
251    }
252
253    /// Try to convert this buffer into [`IoBufMut`] without copying.
254    ///
255    /// Succeeds when this view, whole or sliced, is the only handle to a
256    /// native allocation (one that an [`IoBufMut`] allocated or adopted from a
257    /// [`Vec<u8>`]). The recovered capacity runs from the view's start to the
258    /// end of the allocation, so spare capacity beyond the view returns with
259    /// it. An empty view with no owner converts to an empty [`IoBufMut`].
260    ///
261    /// Returns `self` when other handles share the allocation or when the
262    /// view is a non-empty static slice. Views of [`Bytes`] always decline
263    /// because `Bytes` cannot back a mutable handle.
264    #[inline]
265    pub fn try_into_mut(mut self) -> Result<IoBufMut, Self> {
266        let owner = self.owner.get();
267        if let Some(descriptor) = owner.descriptor() {
268            // A descriptor handle owns its allocation alone, so it needs no
269            // refcount check. Its tail returns as spare capacity, and the sum
270            // cannot wrap because it is bounded by the allocation size.
271            let cap = self.len.wrapping_add(descriptor.tail());
272            let me = ManuallyDrop::new(self);
273            return Ok(IoBufMut {
274                ptr: me.ptr,
275                len: me.len,
276                cap,
277                owner: descriptor.without_tail().into(),
278            });
279        }
280        if owner.is_empty() {
281            return if self.len == 0 {
282                Ok(IoBufMut::default())
283            } else {
284                Err(self)
285            };
286        }
287
288        // `IoBufMut` is never backed by `Bytes`, so external owners decline.
289        // SAFETY: owner is non-empty and live.
290        let Some(&Storage::Native { base, layout }) = (unsafe { owner.storage() }) else {
291            return Err(self);
292        };
293
294        // SAFETY: owner is non-empty and live.
295        if !unsafe { owner.is_unique() } {
296            return Err(self);
297        }
298
299        let me = ManuallyDrop::new(self);
300        let offset = (me.ptr.as_ptr() as usize)
301            .checked_sub(base.as_ptr() as usize)
302            .expect("view pointer must be within owner allocation");
303        assert!(
304            offset <= layout.size(),
305            "view pointer must be within owner bounds"
306        );
307        let cap = layout.size() - offset;
308        assert!(me.len <= cap, "view length must be within owner bounds");
309
310        Ok(IoBufMut {
311            ptr: me.ptr,
312            len: me.len,
313            cap,
314            owner,
315        })
316    }
317
318    /// The owner cell, for the owner loom models.
319    #[cfg(all(test, feature = "loom"))]
320    pub(crate) const fn owner_cell(&self) -> &OwnerCell {
321        &self.owner
322    }
323}
324
325impl AsRef<[u8]> for IoBuf {
326    #[inline]
327    fn as_ref(&self) -> &[u8] {
328        // SAFETY: `ptr..ptr+len` is initialized and kept alive by `owner` or is
329        // an immortal static slice.
330        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
331    }
332}
333
334impl Default for IoBuf {
335    fn default() -> Self {
336        Self {
337            ptr: NonNull::dangling(),
338            len: 0,
339            owner: OwnerCell::empty(),
340        }
341    }
342}
343
344impl PartialEq for IoBuf {
345    fn eq(&self, other: &Self) -> bool {
346        self.as_ref() == other.as_ref()
347    }
348}
349
350impl Eq for IoBuf {}
351
352impl PartialEq<[u8]> for IoBuf {
353    #[inline]
354    fn eq(&self, other: &[u8]) -> bool {
355        self.as_ref() == other
356    }
357}
358
359impl PartialEq<&[u8]> for IoBuf {
360    #[inline]
361    fn eq(&self, other: &&[u8]) -> bool {
362        self.as_ref() == *other
363    }
364}
365
366impl<const N: usize> PartialEq<[u8; N]> for IoBuf {
367    #[inline]
368    fn eq(&self, other: &[u8; N]) -> bool {
369        self.as_ref() == other
370    }
371}
372
373impl<const N: usize> PartialEq<&[u8; N]> for IoBuf {
374    #[inline]
375    fn eq(&self, other: &&[u8; N]) -> bool {
376        self.as_ref() == *other
377    }
378}
379
380impl Buf for IoBuf {
381    /// Returns the prefix as a view of this buffer when `T` is [`IoBuf`], and as its bytes
382    /// otherwise.
383    #[inline]
384    fn take_owned<T: OwnedBytes>(&mut self, len: usize) -> T {
385        T::claim(self.split_to(len)).unwrap_or_else(|view| T::from_bytes(view.into()))
386    }
387}
388
389impl OwnedBytes for IoBuf {
390    #[inline]
391    fn from_bytes(bytes: Bytes) -> Self {
392        Self::from(bytes)
393    }
394}
395
396impl bytes::Buf for IoBuf {
397    #[inline(always)]
398    fn remaining(&self) -> usize {
399        self.len
400    }
401
402    #[inline(always)]
403    fn chunk(&self) -> &[u8] {
404        self.as_ref()
405    }
406
407    #[inline(always)]
408    fn advance(&mut self, cnt: usize) {
409        if cnt > self.len {
410            panic_advance(cnt, self.len);
411        }
412        // SAFETY: `cnt <= self.len`, so the new pointer remains in or one byte
413        // past the readable region.
414        unsafe {
415            self.ptr = self.ptr.add(cnt);
416        }
417        self.len -= cnt;
418    }
419
420    #[inline]
421    fn copy_to_slice(&mut self, dst: &mut [u8]) {
422        if let Err(error) = self.try_copy_to_slice(dst) {
423            panic_try_get(error);
424        }
425    }
426
427    #[inline]
428    fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), TryGetError> {
429        if dst.len() > self.len {
430            return Err(TryGetError {
431                requested: dst.len(),
432                available: self.len,
433            });
434        }
435        // SAFETY: source and destination are valid for `dst.len()` bytes and
436        // cannot overlap because `dst` is a unique mutable slice outside this
437        // immutable buffer.
438        unsafe {
439            std::ptr::copy_nonoverlapping(self.ptr.as_ptr(), dst.as_mut_ptr(), dst.len());
440            self.ptr = self.ptr.add(dst.len());
441        }
442        self.len -= dst.len();
443        Ok(())
444    }
445
446    /// Drains `len` readable bytes into [`Bytes`].
447    ///
448    /// This is zero-copy despite the trait method's name. The drained prefix
449    /// becomes a shared view, converted through the `From<IoBuf> for Bytes`
450    /// fast paths. A partial drain of an unshared native buffer allocates its
451    /// owner record.
452    #[inline]
453    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
454        assert!(len <= self.len, "copy_to_bytes out of bounds");
455        if len == 0 {
456            return Bytes::new();
457        }
458        if len == self.len {
459            return Bytes::from(std::mem::take(self));
460        }
461
462        // Slicing an external-backed view's inner `Bytes` directly skips the
463        // owner retain and release that the shared-view path below pays.
464        let owner = self.owner.get();
465        if owner.descriptor().is_none()
466            // SAFETY: the owner is live while `self` holds its reference.
467            && let Some(Storage::External(inner)) = unsafe { owner.storage() }
468        {
469            // The view prefix lies within the inner `Bytes` range by the
470            // `Storage::External` invariant, as `slice_ref` requires.
471            let bytes = inner.slice_ref(&self.as_ref()[..len]);
472            self.advance(len);
473            return bytes;
474        }
475
476        Bytes::from(self.split_to(len))
477    }
478}
479
480/// Convert a [`Vec<u8>`] into an [`IoBuf`] without copying.
481///
482/// Adopts the vec's allocation as native storage, so the result supports
483/// zero-copy [`IoBuf::try_into_mut`]. An empty vec becomes an empty buffer.
484impl From<Vec<u8>> for IoBuf {
485    #[inline]
486    fn from(vec: Vec<u8>) -> Self {
487        IoBufMut::from(vec).freeze()
488    }
489}
490
491/// Convert [`Bytes`] into an [`IoBuf`] without copying.
492///
493/// The `Bytes` value moves into a small external owner and the handle points
494/// directly into its payload. Handle clones and drops never touch the inner
495/// refcount. Only the final release and the `slice_ref` conversion fast paths
496/// do.
497impl From<Bytes> for IoBuf {
498    fn from(bytes: Bytes) -> Self {
499        if bytes.is_empty() {
500            return Self::default();
501        }
502        let ptr = NonNull::new(bytes.as_ptr().cast_mut())
503            .expect("non-empty Bytes must have non-null data");
504        Self {
505            ptr,
506            len: bytes.len(),
507            owner: OwnerCell::new(Owner::new(Storage::External(bytes))),
508        }
509    }
510}
511
512/// Convert [`BytesMut`] into an [`IoBuf`] without copying (via `freeze`).
513impl From<BytesMut> for IoBuf {
514    fn from(bytes: BytesMut) -> Self {
515        Self::from(bytes.freeze())
516    }
517}
518
519/// Convert [`IoBufMut`] into an [`IoBuf`] without copying (via [`IoBufMut::freeze`]).
520impl From<IoBufMut> for IoBuf {
521    #[inline]
522    fn from(buf: IoBufMut) -> Self {
523        buf.freeze()
524    }
525}
526
527/// Create a zero-copy static view with no owner.
528impl<const N: usize> From<&'static [u8; N]> for IoBuf {
529    #[inline]
530    fn from(array: &'static [u8; N]) -> Self {
531        Self::from_static(array)
532    }
533}
534
535/// Create a zero-copy static view with no owner.
536impl From<&'static [u8]> for IoBuf {
537    #[inline]
538    fn from(slice: &'static [u8]) -> Self {
539        Self::from_static(slice)
540    }
541}
542
543/// Convert an [`IoBuf`] into a [`Vec<u8>`].
544///
545/// This conversion copies the readable bytes.
546impl From<IoBuf> for Vec<u8> {
547    fn from(buf: IoBuf) -> Self {
548        buf.as_ref().to_vec()
549    }
550}
551
552/// Convert an [`IoBuf`] into [`Bytes`] without copying readable data.
553///
554/// - Static views use [`Bytes::from_static`] and allocate nothing.
555/// - Views of a [`Bytes`] use [`Bytes::slice_ref`] on it. That costs at most
556///   a refcount increment, except that the first conversion of an inner
557///   `Bytes` that still exclusively owns its vector allocates a
558///   shared-ownership record.
559/// - Native views use [`Bytes::from_owner`] and allocate one box.
560impl From<IoBuf> for Bytes {
561    fn from(mut buf: IoBuf) -> Self {
562        if buf.is_empty() {
563            return Self::new();
564        }
565        let owner = buf.owner.get();
566        if owner.descriptor().is_none() {
567            if owner.is_empty() {
568                // Non-empty views with no owner are 'static by invariant.
569                // SAFETY: `ptr..ptr+len` is an initialized immortal slice.
570                let slice: &'static [u8] =
571                    unsafe { std::slice::from_raw_parts(buf.ptr.as_ptr(), buf.len) };
572                return Self::from_static(slice);
573            }
574
575            // SAFETY: the owner is live while `buf` holds its reference, and
576            // the view lies within the inner `Bytes` range by the
577            // `Storage::External` invariant, as `slice_ref` requires.
578            if let Some(Storage::External(inner)) = unsafe { owner.storage() } {
579                return inner.slice_ref(buf.as_ref());
580            }
581        }
582
583        // A native view moves into the box with its owner word, so an unshared
584        // view stays unshared.
585        let buf = ManuallyDrop::new(buf);
586        // SAFETY: `owner` is the owner word of `buf`, and `ManuallyDrop` hands
587        // its ownership to the returned `Bytes`.
588        unsafe { owner.into_bytes(buf.ptr, buf.len) }
589    }
590}
591
592impl Write for IoBuf {
593    #[inline]
594    fn write(&self, buf: &mut impl BufMut) {
595        self.len().write(buf);
596        buf.put_slice(self.as_ref());
597    }
598
599    #[inline]
600    fn write_bufs(&self, buf: &mut impl BufsMut) {
601        self.len().write(buf);
602        buf.push(self.clone());
603    }
604}
605
606impl EncodeSize for IoBuf {
607    #[inline]
608    fn encode_size(&self) -> usize {
609        self.len().encode_size() + self.len()
610    }
611
612    #[inline]
613    fn encode_inline_size(&self) -> usize {
614        self.len().encode_size()
615    }
616}
617
618impl Read for IoBuf {
619    type Cfg = RangeCfg<usize>;
620
621    #[inline]
622    fn read_cfg(buf: &mut impl Buf, range: &Self::Cfg) -> Result<Self, Error> {
623        let len = usize::read_cfg(buf, range)?;
624        buf.try_take_owned(len).map_err(|_| Error::EndOfBuffer)
625    }
626}
627
628#[cfg(feature = "arbitrary")]
629impl arbitrary::Arbitrary<'_> for IoBuf {
630    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
631        let len = u.arbitrary_len::<u8>()?;
632        let data: Vec<u8> = u.arbitrary_iter()?.take(len).collect::<Result<_, _>>()?;
633        Ok(Self::from(data))
634    }
635}
636
637/// Mutable byte buffer.
638///
639/// The handle stores the first readable byte, readable length, and writable
640/// view capacity directly:
641///
642/// ```text
643/// before advance:
644/// [ readable len ][ writable cap-len ]
645/// ^
646/// ptr
647///
648/// after advance(n):
649/// [ consumed ][ readable len-n ][ writable cap-len ]
650///              ^
651///              ptr
652/// ```
653///
654/// `advance` moves `ptr` forward and shrinks both `len` and `cap`. `BufMut`
655/// writes always begin at `ptr + len`.
656///
657/// # Capacity
658///
659/// The capacity is fixed at construction, and the buffer never grows the way
660/// [`BytesMut`] does. Every write past `capacity()` panics, including writes
661/// through [`BufMut`] methods such as `put_slice`, so allocate the full
662/// expected size up front. [`Self::default`] and zero-sized constructions own
663/// no storage, so any nonempty write to them panics. `remaining_mut()`
664/// reports the actual writable tail rather than `usize::MAX`.
665pub struct IoBufMut {
666    ptr: NonNull<u8>,
667    len: usize,
668    cap: usize,
669    owner: Owner,
670}
671
672// Keep the handle at four words.
673const _: () = assert!(size_of::<IoBufMut>() == 4 * size_of::<usize>());
674
675// SAFETY: mutable handles have unique ownership. Moving them across threads is
676// safe because final release uses the thread-safe global allocator.
677unsafe impl Send for IoBufMut {}
678// SAFETY: shared references expose only immutable reads. Mutation requires
679// `&mut self`.
680unsafe impl Sync for IoBufMut {}
681
682// Debug omits the data pointer because raw addresses differ across
683// identically-seeded deterministic runs and would leak heap layout into logs.
684impl std::fmt::Debug for IoBufMut {
685    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686        f.debug_struct("IoBufMut")
687            .field("len", &self.len)
688            .field("cap", &self.cap)
689            .finish()
690    }
691}
692
693impl Drop for IoBufMut {
694    #[inline]
695    fn drop(&mut self) {
696        // SAFETY: mutable buffers uniquely own their allocation, and `ptr` and
697        // `cap` are their view.
698        unsafe { self.owner.release_unique(self.ptr, self.cap) };
699    }
700}
701
702impl Default for IoBufMut {
703    fn default() -> Self {
704        Self {
705            ptr: NonNull::dangling(),
706            len: 0,
707            cap: 0,
708            owner: Owner::empty(),
709        }
710    }
711}
712
713impl IoBufMut {
714    /// Create a buffer whose [capacity](Self#capacity) is exactly `capacity`
715    /// bytes.
716    #[inline]
717    pub fn with_capacity(capacity: usize) -> Self {
718        Self::with_alignment(capacity, NonZeroUsize::MIN)
719    }
720
721    /// Create a buffer whose allocation is exactly `capacity` bytes at
722    /// `alignment`.
723    ///
724    /// # Panics
725    ///
726    /// Panics if `capacity` is nonzero and `alignment` is not a power of two,
727    /// or if `capacity` rounded up to `alignment` exceeds `isize::MAX`.
728    #[inline]
729    pub fn with_alignment(capacity: usize, alignment: NonZeroUsize) -> Self {
730        Self::try_with_alignment(capacity, alignment)
731            .expect("alignment must be a power of two and the size must fit a layout")
732    }
733
734    /// Create an aligned buffer with the given capacity and alignment, or
735    /// `None` if a nonzero `capacity` has no valid layout.
736    ///
737    /// A layout is invalid when `alignment` is not a power of two or
738    /// `capacity` rounded up to `alignment` exceeds `isize::MAX`. A valid
739    /// layout the allocator cannot provide is an allocation error, reported
740    /// through [`std::alloc::handle_alloc_error`].
741    #[inline]
742    pub(crate) fn try_with_alignment(capacity: usize, alignment: NonZeroUsize) -> Option<Self> {
743        if capacity == 0 {
744            return Some(Self::default());
745        }
746        Layout::from_size_align(capacity, alignment.get()).ok()?;
747        let (ptr, owner) = Owner::allocate(capacity, alignment.get(), false);
748        Some(Self {
749            ptr,
750            len: 0,
751            cap: capacity,
752            owner,
753        })
754    }
755
756    /// Create a zero-initialized aligned buffer with the given length and
757    /// alignment.
758    ///
759    /// The allocation is exactly `len` bytes at `alignment`, so
760    /// `capacity() == len`.
761    ///
762    /// # Panics
763    ///
764    /// Panics if `len` is nonzero and `alignment` is not a power of two, or
765    /// if `len` rounded up to `alignment` exceeds `isize::MAX`.
766    #[inline]
767    pub fn zeroed_with_alignment(len: usize, alignment: NonZeroUsize) -> Self {
768        if len == 0 {
769            return Self::default();
770        }
771        let (ptr, owner) = Owner::allocate(len, alignment.get(), true);
772        Self {
773            ptr,
774            len,
775            cap: len,
776            owner,
777        }
778    }
779
780    /// Create a buffer of `len` bytes, all initialized to zero.
781    ///
782    /// Unlike [`Self::with_capacity`], the full buffer is immediately
783    /// readable (`len() == capacity() == len`), which suits APIs that fill a
784    /// preallocated buffer such as `read_exact`.
785    #[inline]
786    pub fn zeroed(len: usize) -> Self {
787        Self::zeroed_with_alignment(len, NonZeroUsize::MIN)
788    }
789
790    /// Sets the length of the buffer.
791    ///
792    /// # Safety
793    ///
794    /// Caller must ensure all bytes in `0..len` are initialized before any
795    /// read operations.
796    ///
797    /// # Panics
798    ///
799    /// Panics if `len > capacity()`.
800    #[inline]
801    pub unsafe fn set_len(&mut self, len: usize) {
802        assert!(
803            len <= self.capacity(),
804            "set_len({len}) exceeds capacity({})",
805            self.capacity()
806        );
807        self.len = len;
808    }
809
810    /// Number of readable bytes remaining in the buffer.
811    #[inline]
812    pub const fn len(&self) -> usize {
813        self.len
814    }
815
816    /// Whether the buffer has no readable bytes.
817    #[inline]
818    pub const fn is_empty(&self) -> bool {
819        self.len == 0
820    }
821
822    /// Freeze into immutable [`IoBuf`].
823    ///
824    /// The owner moves to the immutable handle without a refcount operation.
825    /// Freezing allocates only when the spare capacity `capacity() - len()`
826    /// is too large to record in the handle (at least 128 KiB on 64-bit
827    /// targets, 128 bytes on 32-bit targets), and then allocates the owner
828    /// record that sharing would otherwise allocate. Freezing an empty buffer
829    /// releases its allocation, so the result holds no memory.
830    #[inline]
831    pub fn freeze(self) -> IoBuf {
832        if self.len == 0 {
833            // Dropping `self` releases its allocation.
834            drop(self);
835            return IoBuf::default();
836        }
837        // SAFETY: these are this handle's fields. A fallback record
838        // allocation runs while `self` still owns the allocation, so a
839        // failure that unwinds releases it normally.
840        let owner = unsafe { self.owner.frozen(self.ptr, self.len, self.cap) };
841        // The frozen word now owns the allocation.
842        let me = ManuallyDrop::new(self);
843        IoBuf {
844            ptr: me.ptr,
845            len: me.len,
846            owner: OwnerCell::new(owner),
847        }
848    }
849
850    /// Returns how many bytes the buffer can hold, counted from its first
851    /// readable byte.
852    #[inline]
853    pub const fn capacity(&self) -> usize {
854        self.cap
855    }
856
857    /// Returns an unsafe mutable pointer to the first readable byte.
858    #[inline]
859    pub const fn as_mut_ptr(&mut self) -> *mut u8 {
860        self.ptr.as_ptr()
861    }
862
863    /// Truncates the buffer to `len` readable bytes.
864    ///
865    /// Has no effect when `len` is greater than the current length.
866    #[inline]
867    pub fn truncate(&mut self, len: usize) {
868        self.len = self.len.min(len);
869    }
870
871    /// Clears the buffer, removing all readable data. Existing view capacity is preserved.
872    #[inline]
873    pub const fn clear(&mut self) {
874        self.len = 0;
875    }
876}
877
878impl AsRef<[u8]> for IoBufMut {
879    #[inline]
880    fn as_ref(&self) -> &[u8] {
881        // SAFETY: bytes in `0..len` from `ptr` are initialized.
882        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
883    }
884}
885
886impl AsMut<[u8]> for IoBufMut {
887    #[inline]
888    fn as_mut(&mut self) -> &mut [u8] {
889        // SAFETY: bytes in `0..len` from `ptr` are initialized and `&mut self`
890        // proves unique access.
891        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
892    }
893}
894
895impl PartialEq<[u8]> for IoBufMut {
896    #[inline]
897    fn eq(&self, other: &[u8]) -> bool {
898        self.as_ref() == other
899    }
900}
901
902impl PartialEq<&[u8]> for IoBufMut {
903    #[inline]
904    fn eq(&self, other: &&[u8]) -> bool {
905        self.as_ref() == *other
906    }
907}
908
909impl<const N: usize> PartialEq<[u8; N]> for IoBufMut {
910    #[inline]
911    fn eq(&self, other: &[u8; N]) -> bool {
912        self.as_ref() == other
913    }
914}
915
916impl<const N: usize> PartialEq<&[u8; N]> for IoBufMut {
917    #[inline]
918    fn eq(&self, other: &&[u8; N]) -> bool {
919        self.as_ref() == *other
920    }
921}
922
923impl Input for IoBufMut {
924    type Buf = IoBuf;
925}
926
927impl bytes::Buf for IoBufMut {
928    #[inline(always)]
929    fn remaining(&self) -> usize {
930        self.len
931    }
932
933    #[inline(always)]
934    fn chunk(&self) -> &[u8] {
935        self.as_ref()
936    }
937
938    #[inline(always)]
939    fn advance(&mut self, cnt: usize) {
940        if cnt > self.len {
941            panic_advance(cnt, self.len);
942        }
943        // SAFETY: `cnt <= len <= cap`, so the pointer stays within the view
944        // (zero-length pointer adds are always valid, so `cnt == 0` needs no
945        // special case).
946        unsafe {
947            self.ptr = self.ptr.add(cnt);
948        }
949        self.len -= cnt;
950        self.cap -= cnt;
951    }
952
953    #[inline]
954    fn copy_to_slice(&mut self, dst: &mut [u8]) {
955        if let Err(error) = self.try_copy_to_slice(dst) {
956            panic_try_get(error);
957        }
958    }
959
960    #[inline]
961    fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), TryGetError> {
962        if dst.len() > self.len {
963            return Err(TryGetError {
964                requested: dst.len(),
965                available: self.len,
966            });
967        }
968        // SAFETY: source and destination are valid for `dst.len()` bytes and
969        // cannot overlap because `dst` is a unique mutable slice outside this
970        // buffer (zero-length copies with valid pointers need no special
971        // case).
972        unsafe {
973            std::ptr::copy_nonoverlapping(self.ptr.as_ptr(), dst.as_mut_ptr(), dst.len());
974            self.ptr = self.ptr.add(dst.len());
975        }
976        self.len -= dst.len();
977        self.cap -= dst.len();
978        Ok(())
979    }
980
981    /// Drains `len` readable bytes into [`Bytes`].
982    ///
983    /// Draining a nonempty buffer's full readable length moves the whole
984    /// buffer into the result without copying. The handle is then empty with
985    /// zero capacity, whereas `BytesMut` would keep its spare capacity. A
986    /// partial or zero-length drain copies the prefix and keeps the handle's
987    /// remaining capacity.
988    #[inline]
989    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
990        assert!(len <= self.len, "copy_to_bytes out of bounds");
991        if len == 0 {
992            return Bytes::new();
993        }
994        if len == self.len {
995            let drained = std::mem::take(self);
996            return Bytes::from(drained.freeze());
997        }
998
999        let bytes = Bytes::copy_from_slice(&self.as_ref()[..len]);
1000        self.advance(len);
1001        bytes
1002    }
1003}
1004
1005// SAFETY: `IoBufMut` exposes only the uninitialized tail `[len..cap)` through
1006// `chunk_mut`, and `advance_mut` is bounded by that tail.
1007unsafe impl BufMut for IoBufMut {
1008    #[inline(always)]
1009    fn remaining_mut(&self) -> usize {
1010        self.cap - self.len
1011    }
1012
1013    #[inline(always)]
1014    unsafe fn advance_mut(&mut self, cnt: usize) {
1015        let writable = self.cap - self.len;
1016        if cnt > writable {
1017            panic_advance(cnt, writable);
1018        }
1019        self.len += cnt;
1020    }
1021
1022    #[inline(always)]
1023    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
1024        // SAFETY: `ptr + len` begins the uninitialized writable tail and
1025        // `cap - len` is in bounds.
1026        unsafe {
1027            let ptr = self.ptr.as_ptr().add(self.len);
1028            bytes::buf::UninitSlice::from_raw_parts_mut(ptr, self.cap - self.len)
1029        }
1030    }
1031
1032    #[inline]
1033    fn put_slice(&mut self, src: &[u8]) {
1034        let writable = self.cap - self.len;
1035        if src.len() > writable {
1036            panic_advance(src.len(), writable);
1037        }
1038        // SAFETY: the unique writable tail has at least `src.len()` bytes.
1039        unsafe {
1040            std::ptr::copy_nonoverlapping(src.as_ptr(), self.ptr.as_ptr().add(self.len), src.len());
1041        }
1042        self.len += src.len();
1043    }
1044
1045    #[inline]
1046    fn put_bytes(&mut self, val: u8, cnt: usize) {
1047        let writable = self.cap - self.len;
1048        if cnt > writable {
1049            panic_advance(cnt, writable);
1050        }
1051        // SAFETY: the unique writable tail has at least `cnt` bytes.
1052        unsafe {
1053            std::ptr::write_bytes(self.ptr.as_ptr().add(self.len), val, cnt);
1054        }
1055        self.len += cnt;
1056    }
1057
1058    #[inline]
1059    fn put<T: bytes::Buf>(&mut self, mut src: T)
1060    where
1061        Self: Sized,
1062    {
1063        // Early check for a clear panic message, not a safety boundary.
1064        let remaining = src.remaining();
1065        if remaining > self.cap - self.len {
1066            panic_advance(remaining, self.cap - self.len);
1067        }
1068        while src.has_remaining() {
1069            let chunk = src.chunk();
1070            let cnt = chunk.len();
1071            // Safety boundary: `Buf` is a safe trait, so `src` may report a
1072            // `remaining()` smaller than the chunks it hands out. Bound every
1073            // copy by this buffer's own capacity arithmetic, never by `src`.
1074            let writable = self.cap - self.len;
1075            if cnt > writable {
1076                panic_advance(cnt, writable);
1077            }
1078            // SAFETY: `cnt` is bounded by the unique writable tail just above.
1079            unsafe {
1080                std::ptr::copy_nonoverlapping(chunk.as_ptr(), self.ptr.as_ptr().add(self.len), cnt);
1081            }
1082            self.len += cnt;
1083            src.advance(cnt);
1084        }
1085    }
1086}
1087
1088/// Create a mutable buffer by copying the slice.
1089impl From<&[u8]> for IoBufMut {
1090    fn from(slice: &[u8]) -> Self {
1091        let mut buf = Self::with_capacity(slice.len());
1092        buf.put_slice(slice);
1093        buf
1094    }
1095}
1096
1097/// Create a mutable buffer by copying the array.
1098impl<const N: usize> From<[u8; N]> for IoBufMut {
1099    fn from(array: [u8; N]) -> Self {
1100        Self::from(array.as_ref())
1101    }
1102}
1103
1104/// Create a mutable buffer by copying the array.
1105impl<const N: usize> From<&[u8; N]> for IoBufMut {
1106    fn from(array: &[u8; N]) -> Self {
1107        Self::from(array.as_ref())
1108    }
1109}
1110
1111/// Convert a [`Vec<u8>`] into an [`IoBufMut`] without copying.
1112///
1113/// The buffer adopts the vec's allocation, with the vec's contents as its
1114/// readable bytes and `vec.capacity()` as its capacity. A vec without an
1115/// allocation becomes an empty buffer.
1116impl From<Vec<u8>> for IoBufMut {
1117    #[inline]
1118    fn from(vec: Vec<u8>) -> Self {
1119        if vec.capacity() == 0 {
1120            return Self::default();
1121        }
1122        let (ptr, len, cap, owner) = Owner::adopt_vec(vec);
1123        Self {
1124            ptr,
1125            len,
1126            cap,
1127            owner,
1128        }
1129    }
1130}
1131
1132/// Convert a [`BytesMut`] into an [`IoBufMut`].
1133///
1134/// When no other handle shares the allocation, the buffer reuses it and moves
1135/// the readable bytes to its front. Otherwise it copies the readable bytes
1136/// into a new allocation. Either way the capacity is at least
1137/// `bytes.capacity()`.
1138impl From<BytesMut> for IoBufMut {
1139    fn from(bytes: BytesMut) -> Self {
1140        let capacity = bytes.capacity();
1141        let mut vec = Vec::from(bytes);
1142        if vec.capacity() < capacity {
1143            vec.reserve_exact(capacity - vec.len());
1144        }
1145        Self::from(vec)
1146    }
1147}
1148
1149/// Convert [`Bytes`] into an [`IoBufMut`].
1150///
1151/// When no other handle shares an allocation the `Bytes` owns (one that came
1152/// from a `Vec<u8>`, `Box<[u8]>`, or `BytesMut`), the buffer reuses that whole
1153/// allocation and moves the readable bytes to its front. Otherwise, including
1154/// for `'static` slices and [`Bytes::from_owner`] storage, it copies the
1155/// readable bytes into an allocation of exactly their length.
1156impl From<Bytes> for IoBufMut {
1157    fn from(bytes: Bytes) -> Self {
1158        Self::from(Vec::from(bytes))
1159    }
1160}
1161
1162/// Convert an [`IoBuf`] into an [`IoBufMut`], without copying when
1163/// [`IoBuf::try_into_mut`] succeeds and copying the readable bytes otherwise.
1164impl From<IoBuf> for IoBufMut {
1165    fn from(buf: IoBuf) -> Self {
1166        match buf.try_into_mut() {
1167            Ok(buf) => buf,
1168            Err(buf) => Self::from(buf.as_ref()),
1169        }
1170    }
1171}
1172
1173/// Panics for a `split_to` past the readable bytes.
1174///
1175/// Outlined so the inlined `split_to` keeps only a compare and a branch for
1176/// the bounds check.
1177#[cold]
1178#[inline(never)]
1179fn panic_split_to(at: usize, len: usize) -> ! {
1180    panic!("split_to out of bounds: {at:?} <= {len:?}");
1181}
1182
1183/// Panics for a failed `copy_to_slice`, preserving the [`TryGetError`]
1184/// message.
1185#[cold]
1186#[inline(never)]
1187fn panic_try_get(error: TryGetError) -> ! {
1188    panic!("{error}");
1189}
1190
1191/// Resolves `range` against a buffer of length `len` into `(start, end)`.
1192///
1193/// Panics if a bound overflows `usize`, the range is inverted, or the end
1194/// exceeds `len`. Callers forward these as their documented slice panics.
1195fn resolve_range(len: usize, range: impl RangeBounds<usize>) -> (usize, usize) {
1196    let start = match range.start_bound() {
1197        Bound::Included(&n) => n,
1198        Bound::Excluded(&n) => n.checked_add(1).expect("range start overflow"),
1199        Bound::Unbounded => 0,
1200    };
1201    let end = match range.end_bound() {
1202        Bound::Included(&n) => n.checked_add(1).expect("range end overflow"),
1203        Bound::Excluded(&n) => n,
1204        Bound::Unbounded => len,
1205    };
1206    assert!(start <= end, "slice start must be <= end");
1207    assert!(end <= len, "slice out of bounds");
1208    (start, end)
1209}
1210
1211#[cfg(all(test, not(feature = "loom")))]
1212#[commonware_macros::test_group("miri")]
1213mod unit_tests {
1214    use super::{super::bufs::IoBufs, *};
1215    use bytes::{Buf as _, Bytes, BytesMut};
1216    use commonware_codec::{Decode, Encode, RangeCfg};
1217    use core::ops::Bound;
1218    use std::panic::{RefUnwindSafe, UnwindSafe};
1219
1220    /// Whether `buf` owns a native allocation, through a descriptor or a
1221    /// native record.
1222    fn is_native(buf: &IoBuf) -> bool {
1223        let owner = buf.owner.load();
1224        owner.descriptor().is_some()
1225            // SAFETY: `buf` holds a reference to the record for the whole call.
1226            || matches!(unsafe { owner.storage() }, Some(Storage::Native { .. }))
1227    }
1228
1229    /// Whether `buf` owns its native allocation alone, through a descriptor.
1230    fn is_unshared(buf: &IoBuf) -> bool {
1231        buf.owner.load().descriptor().is_some()
1232    }
1233
1234    /// Address and refcount of `buf`'s owner record, or `None` without one.
1235    fn record(buf: &IoBuf) -> Option<(usize, usize)> {
1236        let owner = buf.owner.load();
1237        if owner.descriptor().is_some() {
1238            return None;
1239        }
1240        // SAFETY: `buf` holds a reference to the record for the whole call,
1241        // and the storage address identifies the record.
1242        unsafe {
1243            let addr = owner.storage()?;
1244            Some((std::ptr::from_ref(addr).addr(), owner.refcount()?))
1245        }
1246    }
1247
1248    /// Refcount of `buf`'s owner record, or `None` without one.
1249    fn refcount(buf: &IoBuf) -> Option<usize> {
1250        record(buf).map(|(_, refs)| refs)
1251    }
1252
1253    /// Frozen native buffer holding the bytes `0..len` in an allocation of
1254    /// `cap` bytes, not yet shared.
1255    fn unshared(len: usize, cap: usize) -> IoBuf {
1256        let mut buf = IoBufMut::with_capacity(cap);
1257        for byte in 0..len {
1258            buf.put_u8(byte as u8);
1259        }
1260        let buf = buf.freeze();
1261        assert!(is_unshared(&buf));
1262        buf
1263    }
1264
1265    #[test]
1266    fn test_iobuf_core_behaviors() {
1267        // Clone stays zero-copy for immutable buffers.
1268        let buf1 = IoBuf::from(vec![1u8; 1000]);
1269        let buf2 = buf1.clone();
1270        assert_eq!(buf1.as_ref().as_ptr(), buf2.as_ref().as_ptr());
1271
1272        // copy_from_slice creates an owned immutable buffer.
1273        let data = vec![1u8, 2, 3, 4, 5];
1274        let copied = IoBuf::copy_from_slice(&data);
1275        assert_eq!(copied, [1, 2, 3, 4, 5]);
1276        assert_eq!(copied.len(), 5);
1277        let empty = IoBuf::copy_from_slice(&[]);
1278        assert!(empty.is_empty());
1279
1280        // Equality works against both arrays and slices.
1281        let eq = IoBuf::from(b"hello");
1282        assert_eq!(eq, *b"hello");
1283        assert_eq!(eq, b"hello");
1284        assert_ne!(eq, *b"world");
1285        assert_ne!(eq, b"world");
1286        assert_eq!(IoBuf::from(b"hello"), IoBuf::from(b"hello"));
1287        assert_ne!(IoBuf::from(b"hello"), IoBuf::from(b"world"));
1288        let bytes: Bytes = IoBuf::from(b"bytes").into();
1289        assert_eq!(bytes.as_ref(), b"bytes");
1290
1291        // Buf trait operations keep `len()` and `remaining()` in sync.
1292        let mut buf = IoBuf::from(b"hello world");
1293        assert_eq!(buf.len(), buf.remaining());
1294        assert_eq!(buf.as_ref(), buf.chunk());
1295        assert_eq!(buf.remaining(), 11);
1296        buf.advance(6);
1297        assert_eq!(buf.chunk(), b"world");
1298        assert_eq!(buf.len(), buf.remaining());
1299
1300        // copy_to_bytes drains in-order and advances the source.
1301        let first = buf.copy_to_bytes(2);
1302        assert_eq!(&first[..], b"wo");
1303        let rest = buf.copy_to_bytes(3);
1304        assert_eq!(&rest[..], b"rld");
1305        assert_eq!(buf.remaining(), 0);
1306
1307        // Slicing remains zero-copy and supports all common range forms.
1308        let src = IoBuf::from(b"hello world");
1309        assert_eq!(src.slice(..5), b"hello");
1310        assert_eq!(src.slice(6..), b"world");
1311        assert_eq!(src.slice(3..8), b"lo wo");
1312        assert!(src.slice(5..5).is_empty());
1313    }
1314
1315    #[test]
1316    fn test_iobuf_from_conversions_are_zero_copy() {
1317        // The `IoBuf` docs guarantee every From conversion into IoBuf is
1318        // zero-copy. Pin payload pointer identity for each route.
1319
1320        // A vec adopts its own allocation.
1321        let mut vec = Vec::with_capacity(128);
1322        vec.extend_from_slice(b"adopt");
1323        let ptr = vec.as_ptr();
1324        let buf = IoBuf::from(vec);
1325        assert_eq!(buf.as_ref().as_ptr(), ptr);
1326
1327        // So does an exactly-sized vec, and converting it onward to `Bytes`
1328        // keeps the same payload.
1329        let vec = b"exact".to_vec();
1330        let ptr = vec.as_ptr();
1331        let buf = IoBuf::from(vec);
1332        assert_eq!(buf.as_ref().as_ptr(), ptr);
1333        assert_eq!(Bytes::from(buf).as_ptr(), ptr);
1334
1335        // Bytes moves behind the external owner.
1336        let bytes = Bytes::from(b"bytes".to_vec());
1337        let ptr = bytes.as_ptr();
1338        let buf = IoBuf::from(bytes);
1339        assert_eq!(buf.as_ref().as_ptr(), ptr);
1340
1341        // BytesMut freezes in place.
1342        let mut bytes = BytesMut::with_capacity(16);
1343        bytes.put_slice(b"frozen");
1344        let ptr = bytes.as_ref().as_ptr();
1345        let buf = IoBuf::from(bytes);
1346        assert_eq!(buf.as_ref().as_ptr(), ptr);
1347
1348        // Static views point at the static data itself.
1349        static DATA: [u8; 4] = *b"data";
1350        let buf = IoBuf::from(&DATA[..]);
1351        assert_eq!(buf.as_ref().as_ptr(), DATA.as_ptr());
1352    }
1353
1354    #[test]
1355    fn test_iobuf_codec_roundtrip() {
1356        let cfg: RangeCfg<usize> = (0..=1024).into();
1357
1358        let original = IoBuf::from(b"hello world");
1359        let encoded = original.encode();
1360        let decoded = IoBuf::decode_cfg(encoded, &cfg).unwrap();
1361        assert_eq!(original, decoded);
1362
1363        let empty = IoBuf::default();
1364        let encoded = empty.encode();
1365        let decoded = IoBuf::decode_cfg(encoded, &cfg).unwrap();
1366        assert_eq!(empty, decoded);
1367
1368        let large_cfg: RangeCfg<usize> = (0..=20000).into();
1369        let large = IoBuf::from(vec![42u8; 10000]);
1370        let encoded = large.encode();
1371        let decoded = IoBuf::decode_cfg(encoded, &large_cfg).unwrap();
1372        assert_eq!(large, decoded);
1373
1374        let mut truncated = BytesMut::new();
1375        4usize.write(&mut truncated);
1376        truncated.extend_from_slice(b"xy");
1377        let mut truncated = truncated.freeze();
1378        assert!(matches!(
1379            IoBuf::read_cfg(&mut truncated, &cfg),
1380            Err(Error::EndOfBuffer)
1381        ));
1382
1383        // Directly exercise the successful `read_cfg` path, not just decode helpers.
1384        let mut direct = BytesMut::new();
1385        4usize.write(&mut direct);
1386        direct.extend_from_slice(b"wxyz");
1387        let mut direct = direct.freeze();
1388        let decoded = IoBuf::read_cfg(&mut direct, &cfg).unwrap();
1389        assert_eq!(decoded, b"wxyz");
1390    }
1391
1392    #[test]
1393    #[should_panic(expected = "cannot advance")]
1394    fn test_iobuf_advance_past_end() {
1395        let mut buf = IoBuf::from(b"hello");
1396        buf.advance(10);
1397    }
1398
1399    #[test]
1400    fn test_iobuf_copy_to_slice_paths() {
1401        let mut buf = IoBuf::from(b"hello world");
1402        let mut dst = [0u8; 5];
1403        buf.copy_to_slice(&mut dst);
1404        assert_eq!(&dst, b"hello");
1405        assert_eq!(buf.as_ref(), b" world");
1406
1407        let mut dst = [0u8; 3];
1408        buf.try_copy_to_slice(&mut dst).unwrap();
1409        assert_eq!(&dst, b" wo");
1410
1411        // Requesting more than remaining fails without consuming anything.
1412        let mut dst = [0u8; 4];
1413        let err = buf.try_copy_to_slice(&mut dst).unwrap_err();
1414        assert_eq!(err.requested, 4);
1415        assert_eq!(err.available, 3);
1416        assert_eq!(buf.as_ref(), b"rld");
1417    }
1418
1419    #[test]
1420    #[should_panic(expected = "Not enough bytes remaining in buffer")]
1421    fn test_iobuf_copy_to_slice_past_end() {
1422        let mut buf = IoBuf::from(b"ab");
1423        let mut dst = [0u8; 3];
1424        buf.copy_to_slice(&mut dst);
1425    }
1426
1427    #[test]
1428    #[should_panic(expected = "copy_to_bytes out of bounds")]
1429    fn test_iobuf_copy_to_bytes_past_end() {
1430        let mut buf = IoBuf::from(b"ab");
1431        let _ = buf.copy_to_bytes(3);
1432    }
1433
1434    #[test]
1435    fn test_iobuf_slice_excluded_start_bound() {
1436        // Excluded start bounds resolve to start + 1.
1437        let buf = IoBuf::from(b"hello");
1438        let sliced = buf.slice((Bound::Excluded(0), Bound::Unbounded));
1439        assert_eq!(sliced, b"ello");
1440    }
1441
1442    #[test]
1443    #[should_panic(expected = "slice out of bounds")]
1444    fn test_iobuf_slice_out_of_bounds() {
1445        let buf = IoBuf::from(b"hello");
1446        let _ = buf.slice(..6);
1447    }
1448
1449    #[test]
1450    #[should_panic(expected = "slice start must be <= end")]
1451    fn test_iobuf_slice_inverted_range() {
1452        let buf = IoBuf::from(b"hello");
1453        #[allow(clippy::reversed_empty_ranges)]
1454        let _ = buf.slice(3..1);
1455    }
1456
1457    #[test]
1458    #[should_panic(expected = "range end overflow")]
1459    fn test_iobuf_slice_inclusive_end_overflow() {
1460        let buf = IoBuf::from(b"hello");
1461        let _ = buf.slice(0..=usize::MAX);
1462    }
1463
1464    #[test]
1465    #[should_panic(expected = "range start overflow")]
1466    fn test_iobuf_slice_excluded_start_overflow() {
1467        let buf = IoBuf::from(b"hello");
1468        let _ = buf.slice((Bound::Excluded(usize::MAX), Bound::Unbounded));
1469    }
1470
1471    #[test]
1472    fn test_iobuf_try_into_mut_empty_and_static() {
1473        // Empty views convert trivially.
1474        let buf = IoBuf::default().try_into_mut().expect("empty converts");
1475        assert!(buf.is_empty());
1476        assert_eq!(buf.capacity(), 0);
1477
1478        // Non-empty static views decline because there is no allocation to
1479        // recover.
1480        let err = IoBuf::from(b"static").try_into_mut().unwrap_err();
1481        assert_eq!(err, b"static");
1482
1483        // Empty buffers convert to empty Bytes without touching an owner.
1484        let empty = Bytes::from(IoBuf::default());
1485        assert!(empty.is_empty());
1486
1487        // Empty static slices detach to the default representation.
1488        let empty = IoBuf::from(&b""[..]);
1489        assert!(empty.is_empty());
1490        assert!(empty.try_into_mut().is_ok());
1491    }
1492
1493    #[test]
1494    fn test_iobuf_split_to_consistent_across_backings() {
1495        // split_to on native and static IoBufs should produce identical results.
1496        let mut native = IoBufMut::with_capacity(256);
1497        native.put_slice(b"hello world");
1498        let mut native_buf = native.freeze();
1499        let mut bytes_buf = IoBuf::from(b"hello world");
1500
1501        let native_empty = native_buf.split_to(0);
1502        let bytes_empty = bytes_buf.split_to(0);
1503        assert_eq!(native_empty, bytes_empty);
1504        assert_eq!(native_buf, bytes_buf);
1505
1506        let native_prefix = native_buf.split_to(5);
1507        let bytes_prefix = bytes_buf.split_to(5);
1508        assert_eq!(native_prefix, bytes_prefix);
1509        assert_eq!(native_buf, bytes_buf);
1510
1511        let native_rest = native_buf.split_to(native_buf.len());
1512        let bytes_rest = bytes_buf.split_to(bytes_buf.len());
1513        assert_eq!(native_rest, bytes_rest);
1514        assert_eq!(native_buf, bytes_buf);
1515        assert!(native_buf.is_empty());
1516        assert!(bytes_buf.is_empty());
1517    }
1518
1519    #[test]
1520    #[should_panic(expected = "split_to out of bounds")]
1521    fn test_iobuf_split_to_out_of_bounds() {
1522        let mut buf = IoBuf::from(b"abc");
1523        let _ = buf.split_to(4);
1524    }
1525
1526    #[test]
1527    fn test_iobufmut_core_behaviors() {
1528        // Build mutable buffers incrementally and freeze to immutable.
1529        let mut buf = IoBufMut::with_capacity(100);
1530        assert!(buf.capacity() >= 100);
1531        assert_eq!(buf.len(), 0);
1532        buf.put_slice(b"hello");
1533        buf.put_slice(b" world");
1534        assert_eq!(buf, b"hello world");
1535        assert_eq!(buf, &b"hello world"[..]);
1536        assert_eq!(buf.freeze(), b"hello world");
1537
1538        // `zeroed` creates readable initialized bytes, so `set_len` can shrink safely.
1539        let mut zeroed = IoBufMut::zeroed(10);
1540        assert_eq!(zeroed, &[0u8; 10]);
1541        // SAFETY: shrinking readable length to initialized region.
1542        unsafe { zeroed.set_len(5) };
1543        assert_eq!(zeroed, &[0u8; 5]);
1544        zeroed.as_mut()[..5].copy_from_slice(b"hello");
1545        assert_eq!(&zeroed.as_ref()[..5], b"hello");
1546        let frozen = zeroed.freeze();
1547        let vec: Vec<u8> = frozen.into();
1548        assert_eq!(&vec[..5], b"hello");
1549    }
1550
1551    #[test]
1552    fn test_iobufmut_freeze_after_advance_recovers_capacity() {
1553        let mut buf = IoBufMut::with_capacity(16);
1554        assert_eq!(buf.capacity(), 16);
1555        buf.put_slice(b"abcdefghijklmnop");
1556        buf.advance(3);
1557        assert_eq!(buf.as_ref(), b"defghijklmnop");
1558        assert_eq!(buf.capacity(), 13);
1559
1560        let frozen = buf.freeze();
1561        assert_eq!(frozen.as_ref(), b"defghijklmnop");
1562
1563        let recovered = frozen
1564            .try_into_mut()
1565            .expect("unique buffer must recover mutability");
1566        assert_eq!(recovered.as_ref(), b"defghijklmnop");
1567        assert_eq!(recovered.capacity(), 13);
1568    }
1569
1570    #[test]
1571    fn test_iobufmut_buf_trait() {
1572        // Buf trait on IoBufMut: remaining/chunk/advance should work like BytesMut.
1573        let mut buf = IoBufMut::from(b"hello world");
1574        assert_eq!(buf.remaining(), 11);
1575        assert_eq!(buf.chunk(), b"hello world");
1576
1577        buf.advance(6);
1578        assert_eq!(buf.remaining(), 5);
1579        assert_eq!(buf.chunk(), b"world");
1580
1581        buf.advance(5);
1582        assert_eq!(buf.remaining(), 0);
1583        assert!(buf.chunk().is_empty());
1584    }
1585
1586    #[test]
1587    #[should_panic(expected = "cannot advance")]
1588    fn test_iobufmut_advance_past_end() {
1589        let mut buf = IoBufMut::from(b"hello");
1590        buf.advance(10);
1591    }
1592
1593    #[test]
1594    fn test_iobufmut_copy_to_slice_tracks_len_and_cap() {
1595        // copy_to_slice must shrink len and cap in lockstep with the pointer
1596        // advance so the remaining writable capacity stays within the
1597        // allocation.
1598        let mut buf = IoBufMut::with_capacity(16);
1599        buf.put_slice(b"abcdefgh");
1600
1601        let mut dst = [0u8; 3];
1602        buf.copy_to_slice(&mut dst);
1603        assert_eq!(&dst, b"abc");
1604        assert_eq!(buf.as_ref(), b"defgh");
1605        assert_eq!(buf.len(), 5);
1606        assert_eq!(buf.capacity(), 13);
1607
1608        // try_copy_to_slice success mirrors copy_to_slice.
1609        let mut dst = [0u8; 2];
1610        buf.try_copy_to_slice(&mut dst).unwrap();
1611        assert_eq!(&dst, b"de");
1612        assert_eq!(buf.capacity(), 11);
1613
1614        // Requesting more than remaining fails without consuming anything.
1615        let mut dst = [0u8; 4];
1616        let err = buf.try_copy_to_slice(&mut dst).unwrap_err();
1617        assert_eq!(err.requested, 4);
1618        assert_eq!(err.available, 3);
1619        assert_eq!(buf.as_ref(), b"fgh");
1620        assert_eq!(buf.capacity(), 11);
1621
1622        // Recovering the advanced handle after a freeze derives its capacity
1623        // from the view offset (5 bytes into 16 leaves 11), and the final
1624        // drop, checked under miri, must deallocate with the original layout.
1625        let frozen = buf.freeze();
1626        assert_eq!(frozen.as_ref(), b"fgh");
1627        let recovered = frozen.try_into_mut().expect("unique buffer recovers");
1628        assert_eq!(recovered.as_ref(), b"fgh");
1629        assert_eq!(recovered.capacity(), 11);
1630    }
1631
1632    #[test]
1633    fn test_iobufmut_write_after_partial_advance_appends_at_tail() {
1634        // A partial advance moves the view start while retaining readable
1635        // bytes. A subsequent write must land at the initialized tail so old
1636        // and new data stay adjacent, with len and cap tracked in lockstep.
1637        let mut buf = IoBufMut::with_capacity(16);
1638        buf.put_slice(b"hello");
1639        buf.advance(2);
1640        buf.put_slice(b"world");
1641        assert_eq!(buf.as_ref(), b"lloworld");
1642        assert_eq!(buf.len(), 8);
1643        assert_eq!(buf.capacity(), 14);
1644    }
1645
1646    #[test]
1647    #[should_panic(expected = "Not enough bytes remaining in buffer")]
1648    fn test_iobufmut_copy_to_slice_past_end() {
1649        let mut buf = IoBufMut::from(b"ab");
1650        let mut dst = [0u8; 3];
1651        buf.copy_to_slice(&mut dst);
1652    }
1653
1654    #[test]
1655    #[should_panic(expected = "copy_to_bytes out of bounds")]
1656    fn test_iobufmut_copy_to_bytes_past_end() {
1657        let mut buf = IoBufMut::from(b"ab");
1658        let _ = buf.copy_to_bytes(3);
1659    }
1660
1661    #[test]
1662    fn test_iobufmut_put_bytes_success() {
1663        let mut buf = IoBufMut::with_capacity(8);
1664        buf.put_bytes(7, 5);
1665        assert_eq!(buf.as_ref(), &[7u8; 5]);
1666        assert_eq!(buf.len(), 5);
1667        assert_eq!(buf.remaining_mut(), 3);
1668    }
1669
1670    #[test]
1671    fn test_iobufmut_put_multi_chunk_source() {
1672        let mut buf = IoBufMut::with_capacity(8);
1673        buf.put((&b"hel"[..]).chain(&b"lo"[..]));
1674        assert_eq!(buf.as_ref(), b"hello");
1675        assert_eq!(buf.remaining_mut(), 3);
1676    }
1677
1678    #[test]
1679    #[should_panic(expected = "cannot advance past end of buffer")]
1680    fn test_iobufmut_put_slice_past_capacity() {
1681        let mut buf = IoBufMut::with_capacity(4);
1682        buf.put_slice(b"hello");
1683    }
1684
1685    #[test]
1686    #[should_panic(expected = "cannot advance past end of buffer")]
1687    fn test_iobufmut_put_bytes_past_capacity() {
1688        let mut buf = IoBufMut::with_capacity(4);
1689        buf.put_bytes(0, 5);
1690    }
1691
1692    #[test]
1693    #[should_panic(expected = "cannot advance past end of buffer")]
1694    fn test_iobufmut_advance_mut_past_capacity() {
1695        let mut buf = IoBufMut::with_capacity(4);
1696        // SAFETY: the call panics on the bounds check before any byte in the
1697        // advanced region could be observed.
1698        unsafe { buf.advance_mut(5) };
1699    }
1700
1701    #[test]
1702    #[should_panic(expected = "cannot advance past end of buffer")]
1703    fn test_iobufmut_put_past_capacity() {
1704        let mut buf = IoBufMut::with_capacity(4);
1705        buf.put(&b"hello"[..]);
1706    }
1707
1708    #[test]
1709    fn test_iobuf_additional_conversion_and_trait_paths() {
1710        let mut native_mut = IoBufMut::with_capacity(4);
1711        native_mut.put_slice(b"data");
1712        let native = native_mut.freeze();
1713        assert!(!native.as_ptr().is_null());
1714
1715        // A vec adopts its allocation, so the unique immutable view recovers
1716        // mutability zero-copy.
1717        let mut adopted_vec = Vec::with_capacity(64);
1718        adopted_vec.extend_from_slice(&[1u8, 2, 3]);
1719        let unique = IoBuf::from(adopted_vec);
1720        let unique_mut = unique.try_into_mut().expect("adopted vec should convert");
1721        assert_eq!(unique_mut.as_ref(), &[1u8, 2, 3]);
1722
1723        let shared = IoBuf::from(vec![4u8, 5, 6]);
1724        let _shared_clone = shared.clone();
1725        assert!(shared.try_into_mut().is_err());
1726
1727        // Exactly-sized vecs adopt as well, and external `Bytes` views always
1728        // decline mutable recovery.
1729        let exact = IoBuf::from(vec![7u8, 8, 9]);
1730        assert_eq!(
1731            exact
1732                .try_into_mut()
1733                .expect("exactly-sized vec must adopt")
1734                .capacity(),
1735            3
1736        );
1737        let external = IoBuf::from(Bytes::from(vec![7u8, 8, 9]));
1738        assert!(external.try_into_mut().is_err());
1739
1740        let expected: &[u8] = &[9u8, 8];
1741        let eq_buf = IoBuf::from(vec![9u8, 8]);
1742        assert!(PartialEq::<[u8]>::eq(&eq_buf, expected));
1743
1744        let static_slice: &'static [u8] = b"static";
1745        assert_eq!(IoBuf::from(static_slice), b"static");
1746
1747        let mut native_mut = IoBufMut::with_capacity(3);
1748        native_mut.put_slice(b"xyz");
1749        let native = native_mut.freeze();
1750        let native_ptr = native.as_ptr();
1751        let vec_out: Vec<u8> = native.clone().into();
1752        let bytes_out: Bytes = native.into();
1753        assert_eq!(vec_out, b"xyz");
1754        assert_eq!(bytes_out.as_ref(), b"xyz");
1755
1756        // Native owners hand the allocation to `Bytes` without copying.
1757        assert_eq!(bytes_out.as_ptr(), native_ptr);
1758    }
1759
1760    #[test]
1761    fn test_iobuf_from_bytes_zero_copy_round_trip() {
1762        // Bytes -> IoBuf is zero-copy. The handle points into the payload.
1763        let bytes = Bytes::from(vec![1u8; 64]);
1764        let payload_ptr = bytes.as_ptr();
1765        let buf = IoBuf::from(bytes.clone());
1766        assert_eq!(buf.as_ptr(), payload_ptr);
1767        assert_eq!(buf, bytes.as_ref());
1768
1769        // IoBuf -> Bytes on an external backing uses slice_ref: same payload,
1770        // no copy, no extra owner box.
1771        let out: Bytes = buf.into();
1772        assert_eq!(out.as_ptr(), payload_ptr);
1773        assert_eq!(out, bytes);
1774
1775        // Sliced external views convert through slice_ref too.
1776        let sliced = IoBuf::from(bytes).slice(8..32);
1777        let sliced_ptr = sliced.as_ptr();
1778        let sliced_out: Bytes = sliced.into();
1779        assert_eq!(sliced_out.as_ptr(), sliced_ptr);
1780        assert_eq!(sliced_out.len(), 24);
1781    }
1782
1783    #[test]
1784    fn test_iobuf_from_bytes_mut_zero_copy() {
1785        let mut bytes = BytesMut::with_capacity(32);
1786        bytes.extend_from_slice(b"hello");
1787        let payload_ptr = bytes.as_ref().as_ptr();
1788        let buf = IoBuf::from(bytes);
1789        assert_eq!(buf.as_ptr(), payload_ptr);
1790        assert_eq!(buf, b"hello");
1791    }
1792
1793    #[test]
1794    fn test_iobuf_static_into_bytes_uses_from_static() {
1795        let buf = IoBuf::from(b"static-payload");
1796        let payload_ptr = buf.as_ptr();
1797        let bytes: Bytes = buf.into();
1798        assert_eq!(bytes.as_ptr(), payload_ptr);
1799        assert_eq!(bytes.as_ref(), b"static-payload");
1800    }
1801
1802    #[test]
1803    fn test_iobuf_vec_adoption_round_trip_zero_copy() {
1804        // Vec with spare capacity -> IoBuf adopts the allocation, and
1805        // try_into_mut recovers a writable handle at the same address.
1806        let mut vec = Vec::with_capacity(128);
1807        vec.extend_from_slice(b"adopted payload");
1808        let base = vec.as_ptr() as usize;
1809        let buf = IoBuf::from(vec);
1810        assert_eq!(buf.as_ref().as_ptr() as usize, base);
1811
1812        let mut recovered = buf
1813            .try_into_mut()
1814            .expect("adopted vec recovers mutability zero-copy");
1815        assert_eq!(recovered.as_mut_ptr() as usize, base);
1816        assert_eq!(recovered.as_ref(), b"adopted payload");
1817        assert!(recovered.capacity() > recovered.len());
1818        recovered.put_slice(b"!");
1819        assert_eq!(recovered.as_ref(), b"adopted payload!");
1820    }
1821
1822    #[test]
1823    fn test_iobufmut_decode_preserves_byte_fields() {
1824        let values = vec![Bytes::from_static(b"abc"), Bytes::from_static(b"def")];
1825        let source = IoBufMut::from(values.encode());
1826        let range = source.as_ref().as_ptr_range();
1827        let decoded = Vec::<Bytes>::decode_cfg(source, &((..).into(), (..).into())).unwrap();
1828        assert_eq!(decoded, values);
1829        assert!(decoded.iter().all(|field| range.contains(&field.as_ptr())));
1830    }
1831
1832    #[test]
1833    fn test_iobuf_read_cfg_zero_copy_from_iobuf_source() {
1834        // Decoding an IoBuf field from an IoBuf source must not copy the
1835        // payload. The source hands out a view sharing its owner, and the
1836        // field claims it as is.
1837        let cfg: RangeCfg<usize> = (0..=1024).into();
1838        let mut source = IoBuf::from(IoBuf::from(vec![7u8; 100]).encode());
1839        let prefix = source.len() - 100;
1840        let payload_ptr = source.as_ref()[prefix..].as_ptr();
1841        let decoded = IoBuf::read_cfg(&mut source, &cfg).unwrap();
1842        assert_eq!(decoded.len(), 100);
1843        assert_eq!(decoded.as_ptr(), payload_ptr);
1844        assert_eq!(decoded, [7u8; 100]);
1845
1846        // A native source hands out a view of its own owner rather than a wrapped `Bytes`.
1847        let encoded = IoBuf::from(vec![7u8; 100]).encode();
1848        let mut native = IoBufMut::with_capacity(encoded.len());
1849        native.put_slice(&encoded);
1850        let mut source = native.freeze();
1851        let payload_ptr = source.as_ref()[prefix..].as_ptr();
1852        let decoded = IoBuf::read_cfg(&mut source, &cfg).unwrap();
1853        assert_eq!(decoded.as_ptr(), payload_ptr);
1854
1855        // The field takes the rest of the source, so it keeps the source's
1856        // unshared native owner.
1857        assert!(is_unshared(&decoded));
1858    }
1859
1860    /// A `Bytes` field decoded from an `IoBuf` source receives the view's bytes, and an
1861    /// `IoBuf` field decoded from a multi-chunk `IoBufs` source is the prefix coalesced once.
1862    #[test]
1863    fn test_take_owned_other_containers() {
1864        let cfg: RangeCfg<usize> = (0..=1024).into();
1865        let mut source = IoBuf::from(IoBuf::from(vec![7u8; 100]).encode());
1866        let decoded = Bytes::read_cfg(&mut source, &cfg).unwrap();
1867        assert_eq!(decoded, vec![7u8; 100]);
1868
1869        let encoded = IoBuf::from(vec![7u8; 100]).encode();
1870        let mut source = IoBufs::from(IoBuf::copy_from_slice(&encoded[..50]));
1871        source.append(IoBuf::copy_from_slice(&encoded[50..]));
1872        let decoded = IoBuf::read_cfg(&mut source, &cfg).unwrap();
1873        assert_eq!(decoded, [7u8; 100]);
1874        assert!(source.is_empty());
1875    }
1876
1877    #[test]
1878    #[should_panic(expected = "cannot advance")]
1879    fn test_iobufmut_put_does_not_trust_lying_buf() {
1880        // `Buf` is a safe trait, so a misbehaving source may hand out chunks
1881        // larger than its reported remaining(). `put` must bound each copy by
1882        // its own capacity and panic instead of overflowing the buffer.
1883        struct LyingBuf;
1884        impl bytes::Buf for LyingBuf {
1885            fn remaining(&self) -> usize {
1886                1
1887            }
1888            fn chunk(&self) -> &[u8] {
1889                &[0xAB; 64]
1890            }
1891            fn advance(&mut self, _cnt: usize) {}
1892        }
1893
1894        let mut buf = IoBufMut::with_capacity(8);
1895        buf.put(LyingBuf);
1896    }
1897
1898    #[test]
1899    fn test_handle_auto_traits() {
1900        fn check<T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe>() {}
1901        check::<IoBuf>();
1902        check::<IoBufMut>();
1903    }
1904
1905    #[test]
1906    fn test_unshared_lifecycle_keeps_descriptor() {
1907        let mut buf = IoBufMut::with_capacity(16);
1908        let base = buf.as_mut_ptr();
1909        buf.put_slice(b"0123456789");
1910        let mut frozen = buf.freeze();
1911        assert!(is_unshared(&frozen));
1912
1913        // Advancing keeps the descriptor, and recovery returns the spare
1914        // capacity counted from the new view start.
1915        frozen.advance(3);
1916        let mut buf = frozen.try_into_mut().unwrap();
1917        // SAFETY: the view advanced by 3 bytes within the allocation.
1918        assert_eq!(buf.as_mut_ptr(), unsafe { base.add(3) });
1919        assert_eq!(buf.as_ref(), b"3456789");
1920        assert_eq!(buf.capacity(), 13);
1921        buf.put_slice(b"abcdef");
1922        assert_eq!(buf.capacity(), 13);
1923
1924        let frozen = buf.freeze();
1925        assert!(is_unshared(&frozen));
1926        let mut buf = frozen.try_into_mut().unwrap();
1927        assert_eq!(buf.as_ref(), b"3456789abcdef");
1928        buf.advance(13);
1929        assert_eq!(buf.capacity(), 0);
1930
1931        // A fully advanced view still owns the allocation until it drops.
1932        let mut frozen = IoBufMut::from(&b"xyz"[..]).freeze();
1933        frozen.advance(3);
1934        assert!(frozen.is_empty() && is_unshared(&frozen));
1935        let recovered = frozen.try_into_mut().unwrap();
1936        assert_eq!((recovered.len(), recovered.capacity()), (0, 0));
1937    }
1938
1939    #[test]
1940    fn test_first_share_promotes_once() {
1941        let buf = unshared(8, 12);
1942        let first = buf.clone();
1943        let (addr, refs) = record(&buf).unwrap();
1944        assert_eq!(refs, 2);
1945        assert_eq!(record(&first), Some((addr, 2)));
1946
1947        // Later shares reuse the record.
1948        let second = first.clone();
1949        let third = buf.slice(2..5);
1950        assert_eq!(record(&third), Some((addr, 4)));
1951        assert_eq!(third, [2u8, 3, 4]);
1952        assert_eq!(second, buf);
1953
1954        // Recovery declines while views live and keeps the tail afterwards.
1955        let buf = buf.try_into_mut().unwrap_err();
1956        drop((first, second, third));
1957        assert_eq!(refcount(&buf), Some(1));
1958        let recovered = buf.try_into_mut().unwrap();
1959        assert_eq!(recovered.as_ref(), [0u8, 1, 2, 3, 4, 5, 6, 7]);
1960        assert_eq!(recovered.capacity(), 12);
1961    }
1962
1963    #[test]
1964    fn test_slice_promotes_from_source_view() {
1965        // A view at offset 5 with a 28-byte tail promotes with the original
1966        // base and layout, so a slice that outlives it recovers capacity up to
1967        // the allocation end.
1968        let mut buf = unshared(100, 128);
1969        buf.advance(5);
1970        let slice = buf.slice(10..30);
1971        assert_eq!(refcount(&slice), Some(2));
1972        drop(buf);
1973        let recovered = slice.try_into_mut().unwrap();
1974        assert_eq!(recovered.as_ref(), (15u8..35).collect::<Vec<_>>());
1975        assert_eq!(recovered.capacity(), 128 - 15);
1976    }
1977
1978    #[test]
1979    fn test_aligned_descriptor_promotes_from_unaligned_view() {
1980        let alignment = NonZeroUsize::new(64).unwrap();
1981        let mut buf = IoBufMut::with_alignment(256, alignment);
1982        assert!((buf.as_mut_ptr() as usize).is_multiple_of(64));
1983        buf.put_bytes(9, 100);
1984
1985        // Promoting a view 3 bytes past the aligned base must recover the base
1986        // and layout of the original allocation.
1987        let mut frozen = buf.freeze();
1988        frozen.advance(3);
1989        let view = frozen.clone();
1990        drop(frozen);
1991
1992        // The recovered capacity reaches the end of the allocation, and all of
1993        // it is writable.
1994        let mut recovered = view.try_into_mut().unwrap();
1995        assert_eq!(recovered.capacity(), 253);
1996        recovered.put_bytes(1, 156);
1997        assert_eq!(recovered.len(), 253);
1998    }
1999
2000    #[test]
2001    fn test_clone_of_empty_cursor_keeps_allocation() {
2002        let mut buf = unshared(4, 8);
2003        buf.advance(4);
2004        assert!(buf.is_empty() && is_unshared(&buf));
2005
2006        // Unlike an empty slice or split, a clone of a fully advanced view
2007        // shares the allocation, so either handle can recover the tail once
2008        // the other drops.
2009        let clone = buf.clone();
2010        assert_eq!(refcount(&clone), Some(2));
2011        drop(clone);
2012
2013        let recovered = buf.try_into_mut().unwrap();
2014        assert_eq!((recovered.len(), recovered.capacity()), (0, 4));
2015    }
2016
2017    #[test]
2018    fn test_moves_and_empty_views_do_not_promote() {
2019        let mut buf = unshared(8, 8);
2020        let mut moved = std::mem::take(&mut buf);
2021        assert!(is_unshared(&moved));
2022        assert!(buf.is_empty() && !is_native(&buf));
2023
2024        // Empty halves and empty slices detach without touching the owner.
2025        assert!(moved.split_to(0).is_empty());
2026        assert!(moved.slice(3..3).is_empty());
2027        assert!(is_unshared(&moved));
2028
2029        // A whole split, a whole take, and a whole drain move the descriptor.
2030        let mut whole = moved.split_to(8);
2031        assert!(moved.is_empty() && is_unshared(&whole));
2032        let mut taken: IoBuf = whole.take_owned(8);
2033        assert!(is_unshared(&taken));
2034        let bytes = taken.copy_to_bytes(8);
2035        assert_eq!(bytes, [0u8, 1, 2, 3, 4, 5, 6, 7].as_slice());
2036    }
2037
2038    #[test]
2039    fn test_partial_split_promotes_and_shares() {
2040        // A partial split promotes the descriptor, and both halves share the
2041        // new record.
2042        let mut buf = unshared(10, 16);
2043        let prefix = buf.split_to(4);
2044        let (addr, refs) = record(&buf).unwrap();
2045        assert_eq!(refs, 2);
2046        assert_eq!(record(&prefix), Some((addr, 2)));
2047        assert_eq!(prefix, [0u8, 1, 2, 3]);
2048
2049        // Later partial splits and takes reuse that record.
2050        let middle = buf.split_to(2);
2051        let taken: IoBuf = buf.take_owned(1);
2052        assert_eq!(record(&taken), Some((addr, 4)));
2053        assert_eq!(
2054            (middle, taken),
2055            (IoBuf::from(&[4u8, 5]), IoBuf::from(&[6u8]))
2056        );
2057        assert_eq!(buf, [7u8, 8, 9]);
2058
2059        // Once the other views drop, the rest recovers the allocation tail.
2060        drop(prefix);
2061        let recovered = buf.try_into_mut().unwrap();
2062        assert_eq!(recovered.capacity(), 9);
2063    }
2064
2065    #[test]
2066    fn test_bytes_from_unshared_view_keeps_allocation() {
2067        // A conversion moves the advanced descriptor into the Bytes owner
2068        // without promoting it. The last Bytes clone frees the allocation
2069        // with its original layout, on another thread.
2070        let mut buf = unshared(10, 16);
2071        buf.advance(2);
2072        let bytes = Bytes::from(buf);
2073        let slice = bytes.slice(1..4);
2074        drop(bytes);
2075        std::thread::spawn(move || assert_eq!(slice, [3u8, 4, 5].as_slice()))
2076            .join()
2077            .unwrap();
2078
2079        // Direct boxing and a round trip through Bytes keep the view alive.
2080        let direct = Bytes::from_owner(unshared(3, 5));
2081        let round_trip = IoBuf::from(direct.clone());
2082        assert_eq!(round_trip, [0u8, 1, 2]);
2083        drop(direct);
2084        assert_eq!(round_trip, [0u8, 1, 2]);
2085
2086        // A partial drain shares the allocation with the drained Bytes.
2087        let mut buf = unshared(10, 16);
2088        let drained = buf.copy_to_bytes(4);
2089        assert_eq!(refcount(&buf), Some(2));
2090        drop(drained);
2091        assert_eq!(refcount(&buf), Some(1));
2092        assert_eq!(buf.try_into_mut().unwrap().capacity(), 12);
2093    }
2094
2095    #[test]
2096    fn test_freeze_tail_overflow_allocates_record() {
2097        // Spare capacity beyond every descriptor tail moves into a record at
2098        // freeze, which keeps the data pointer and the full capacity.
2099        let capacity = (1 << 17) + 1;
2100        let mut buf = IoBufMut::with_capacity(capacity);
2101        let base = buf.as_mut_ptr();
2102        buf.put_u8(1);
2103        let frozen = buf.freeze();
2104        let (addr, refs) = record(&frozen).unwrap();
2105        assert_eq!(refs, 1);
2106        let view = frozen.clone();
2107        assert_eq!(record(&view), Some((addr, 2)));
2108        drop(view);
2109        let mut recovered = frozen.try_into_mut().unwrap();
2110        assert_eq!(recovered.as_mut_ptr(), base);
2111        assert_eq!(recovered.capacity(), capacity);
2112    }
2113
2114    #[test]
2115    fn test_concurrent_first_clones_share_one_record() {
2116        // Four threads race the first share. Exactly one record is published,
2117        // and the threads that lose the race join it.
2118        let buf = unshared(32, 32);
2119        let views: Vec<IoBuf> = std::thread::scope(|scope| {
2120            let buf = &buf;
2121            let handles: Vec<_> = (0..4)
2122                .map(|i| {
2123                    scope.spawn(move || {
2124                        if i % 2 == 0 {
2125                            buf.clone()
2126                        } else {
2127                            buf.slice(1..31)
2128                        }
2129                    })
2130                })
2131                .collect();
2132            handles.into_iter().map(|h| h.join().unwrap()).collect()
2133        });
2134
2135        // The source and the four views hold the record's five references.
2136        let (addr, refs) = record(&buf).unwrap();
2137        assert_eq!(refs, 5);
2138        assert!(views.iter().all(|view| record(view).unwrap().0 == addr));
2139
2140        // Dropping the views leaves the source as the record's only holder.
2141        drop(views);
2142        assert_eq!(buf.try_into_mut().unwrap().capacity(), 32);
2143    }
2144
2145    #[test]
2146    fn test_iobufmut_from_iobuf() {
2147        // Unique buffers recover mutability without copying.
2148        let mut unique = IoBufMut::with_capacity(4);
2149        unique.put_slice(b"data");
2150        let unique_ptr = unique.as_mut_ptr();
2151        let mut recovered = IoBufMut::from(unique.freeze());
2152        assert_eq!(recovered.as_ref(), b"data");
2153        assert_eq!(recovered.as_mut_ptr(), unique_ptr);
2154
2155        // Shared buffers allocate and copy readable bytes.
2156        let mut shared = IoBufMut::with_capacity(4);
2157        shared.put_slice(b"copy");
2158        let shared = shared.freeze();
2159        let shared_ptr = shared.as_ptr();
2160        let _clone = shared.clone();
2161        let mut copied = IoBufMut::from(shared);
2162        assert_eq!(copied.as_ref(), b"copy");
2163        assert_ne!(copied.as_mut_ptr() as *const u8, shared_ptr);
2164
2165        // Recovery after transient slices are dropped is zero-copy and preserves
2166        // the full readable length and capacity.
2167        let mut mirror = IoBufMut::with_capacity(8);
2168        mirror.put_slice(b"abcdefgh");
2169        let mirror_cap = mirror.capacity();
2170        let mirror_ptr = mirror.as_mut_ptr();
2171        let frozen = mirror.freeze();
2172        let head = frozen.slice(0..3);
2173        let tail = frozen.slice(5..8);
2174        drop(head);
2175        drop(tail);
2176        let mut recovered = IoBufMut::from(frozen);
2177        assert_eq!(recovered.as_ref(), b"abcdefgh");
2178        assert_eq!(recovered.as_mut_ptr(), mirror_ptr);
2179        assert_eq!(recovered.capacity(), mirror_cap);
2180    }
2181
2182    #[test]
2183    fn test_iobufmut_additional_conversion_and_trait_paths() {
2184        // Basic mutable operations should keep readable bytes consistent.
2185        let mut buf = IoBufMut::from([1u8, 2, 3, 4]);
2186        assert!(!buf.is_empty());
2187        buf.truncate(2);
2188        assert_eq!(buf.as_ref(), &[1u8, 2]);
2189        buf.clear();
2190        assert!(buf.is_empty());
2191        buf.put_slice(b"xyz");
2192
2193        // Equality should work across slice, array, and byte-string forms.
2194        let expected: &[u8] = b"xyz";
2195        assert!(PartialEq::<[u8]>::eq(&buf, expected));
2196        assert!(buf == b"xyz"[..]);
2197        assert!(buf == *b"xyz");
2198        assert!(buf == b"xyz");
2199
2200        // Conversions from common owned/shared containers preserve contents.
2201        let from_array = IoBufMut::from([7u8, 8]);
2202        assert_eq!(from_array.as_ref(), &[7u8, 8]);
2203
2204        let from_bytesmut = IoBufMut::from(BytesMut::from(&b"hi"[..]));
2205        assert_eq!(from_bytesmut.as_ref(), b"hi");
2206
2207        let from_bytes = IoBufMut::from(Bytes::from_static(b"ok"));
2208        assert_eq!(from_bytes.as_ref(), b"ok");
2209
2210        // `Bytes::from_static` cannot be converted to mutable without copy.
2211        let from_iobuf = IoBufMut::from(IoBuf::from(Bytes::from_static(b"io")));
2212        assert_eq!(from_iobuf.as_ref(), b"io");
2213    }
2214
2215    #[test]
2216    fn test_iobufmut_from_bytesmut_preserves_capacity() {
2217        // A uniquely owned BytesMut hands over its allocation.
2218        let mut bytes = BytesMut::with_capacity(100);
2219        bytes.put_slice(b"abc");
2220        let cap = bytes.capacity();
2221        let base = bytes.as_ptr() as usize;
2222        let buf = IoBufMut::from(bytes);
2223        assert_eq!(buf.as_ref(), b"abc");
2224        assert_eq!(buf.capacity(), cap);
2225        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2226
2227        // A uniquely owned BytesMut advanced past its allocation's start is
2228        // reused, with the readable bytes moved to the front.
2229        let mut advanced = BytesMut::with_capacity(100);
2230        advanced.put_slice(b"prefix-payload");
2231        let base = advanced.as_ptr() as usize;
2232        advanced.advance(7);
2233        let buf = IoBufMut::from(advanced);
2234        assert_eq!(buf.as_ref(), b"payload");
2235        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2236        assert!(buf.capacity() >= 100);
2237
2238        // An empty reserved BytesMut keeps its reservation writable.
2239        let bytes = BytesMut::with_capacity(64);
2240        let cap = bytes.capacity();
2241        let mut buf = IoBufMut::from(bytes);
2242        assert!(buf.is_empty());
2243        assert_eq!(buf.capacity(), cap);
2244        buf.put_bytes(7, cap);
2245        assert_eq!(buf.len(), cap);
2246
2247        // A BytesMut whose allocation is shared with another handle copies,
2248        // keeping its reserved capacity.
2249        let mut shared = BytesMut::with_capacity(100);
2250        shared.put_slice(b"shared");
2251        let other = shared.split_to(3);
2252        let cap = shared.capacity();
2253        let base = shared.as_ptr() as usize;
2254        let buf = IoBufMut::from(shared);
2255        assert_eq!(buf.as_ref(), b"red");
2256        assert_ne!(buf.as_ref().as_ptr() as usize, base);
2257        assert_eq!(buf.capacity(), cap);
2258        assert_eq!(other.as_ref(), b"sha");
2259    }
2260
2261    /// `From<Bytes>` hands a uniquely owned allocation to the mutable buffer
2262    /// with the readable bytes moved to its front, and copies when the
2263    /// allocation is shared or not owned by the `Bytes`.
2264    #[test]
2265    fn test_iobufmut_from_bytes_reuses_unique_allocation() {
2266        // A vec with spare capacity freezes into shared-ownership Bytes whose
2267        // single handle hands over the vec's allocation and capacity.
2268        let mut vec = Vec::with_capacity(100);
2269        vec.extend_from_slice(b"abc");
2270        let base = vec.as_ptr() as usize;
2271        let buf = IoBufMut::from(Bytes::from(vec));
2272        assert_eq!(buf.as_ref(), b"abc");
2273        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2274        assert_eq!(buf.capacity(), 100);
2275
2276        // An exactly-sized vec takes the boxed-slice route in bytes and hands
2277        // over its allocation the same way.
2278        let vec = b"exact".to_vec();
2279        let base = vec.as_ptr() as usize;
2280        let buf = IoBufMut::from(Bytes::from(vec));
2281        assert_eq!(buf.as_ref(), b"exact");
2282        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2283        assert_eq!(buf.capacity(), 5);
2284
2285        // A unique view advanced past the allocation's start is reused with
2286        // the readable bytes moved to the front.
2287        let vec = b"prefix-payload".to_vec();
2288        let base = vec.as_ptr() as usize;
2289        let mut advanced = Bytes::from(vec);
2290        advanced.advance(7);
2291        let buf = IoBufMut::from(advanced);
2292        assert_eq!(buf.as_ref(), b"payload");
2293        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2294        assert_eq!(buf.capacity(), 14);
2295
2296        // A slice becomes unique again once its sibling views are gone.
2297        let vec = b"head-tail".to_vec();
2298        let base = vec.as_ptr() as usize;
2299        let whole = Bytes::from(vec);
2300        let tail = whole.slice(5..);
2301        drop(whole);
2302        let buf = IoBufMut::from(tail);
2303        assert_eq!(buf.as_ref(), b"tail");
2304        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2305        assert_eq!(buf.capacity(), 9);
2306
2307        // An empty Bytes that still owns spare capacity keeps it writable.
2308        let vec = Vec::<u8>::with_capacity(64);
2309        let base = vec.as_ptr() as usize;
2310        let mut buf = IoBufMut::from(Bytes::from(vec));
2311        assert!(buf.is_empty());
2312        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2313        assert_eq!(buf.capacity(), 64);
2314        buf.put_slice(b"ready");
2315        assert_eq!(buf.as_ref(), b"ready");
2316        assert_eq!(IoBufMut::from(Bytes::new()).capacity(), 0);
2317
2318        // A Bytes whose allocation another handle shares copies, and the other
2319        // handle keeps its bytes.
2320        let shared = Bytes::from(b"shared".to_vec());
2321        let base = shared.as_ptr() as usize;
2322        let other = shared.clone();
2323        let mut buf = IoBufMut::from(shared);
2324        assert_eq!(buf.as_ref(), b"shared");
2325        assert_ne!(buf.as_ref().as_ptr() as usize, base);
2326        assert_eq!(buf.capacity(), 6);
2327        buf.as_mut()[0] = b'S';
2328        assert_eq!(other.as_ref(), b"shared");
2329
2330        // Static and owner-backed Bytes do not own a vec allocation, so they
2331        // copy.
2332        let buf = IoBufMut::from(Bytes::from_static(b"static"));
2333        assert_eq!(buf.as_ref(), b"static");
2334        assert_ne!(buf.as_ref().as_ptr() as usize, b"static".as_ptr() as usize);
2335        let owned = Bytes::from_owner(b"owned".to_vec());
2336        let base = owned.as_ptr() as usize;
2337        let buf = IoBufMut::from(owned);
2338        assert_eq!(buf.as_ref(), b"owned");
2339        assert_ne!(buf.as_ref().as_ptr() as usize, base);
2340    }
2341
2342    #[test]
2343    fn test_iobufmut_from_vec_preserves_capacity() {
2344        let mut vec = Vec::with_capacity(100);
2345        vec.extend_from_slice(b"abc");
2346        let base = vec.as_ptr() as usize;
2347        let buf = IoBufMut::from(vec);
2348        assert_eq!(buf.as_ref(), b"abc");
2349        assert_eq!(buf.capacity(), 100);
2350
2351        // The vec's allocation is adopted, not copied.
2352        assert_eq!(buf.as_ref().as_ptr() as usize, base);
2353
2354        // An empty reservation converts to a writable buffer of the same
2355        // capacity.
2356        let mut buf = IoBufMut::from(Vec::with_capacity(64));
2357        assert!(buf.is_empty());
2358        assert_eq!(buf.capacity(), 64);
2359        buf.put_bytes(7, 64);
2360        assert_eq!(buf.len(), 64);
2361
2362        // A vec without an allocation becomes an empty buffer, and an
2363        // exactly-sized vec keeps its exact capacity.
2364        assert!(IoBufMut::from(Vec::new()).is_empty());
2365        let exact = IoBufMut::from(vec![1u8, 2, 3]);
2366        assert_eq!(exact.capacity(), 3);
2367        assert_eq!(
2368            exact
2369                .freeze()
2370                .try_into_mut()
2371                .expect("unique view must recover mutability")
2372                .as_ref(),
2373            &[1u8, 2, 3]
2374        );
2375    }
2376
2377    #[test]
2378    #[should_panic(expected = "size must fit a layout")]
2379    fn test_iobufmut_with_capacity_rejects_oversized_request() {
2380        // The layout check panics before any allocation. Sizes up to
2381        // isize::MAX are valid layouts and would fail as allocation errors.
2382        let _ = IoBufMut::with_capacity(isize::MAX as usize + 1);
2383    }
2384
2385    #[test]
2386    fn test_iobufmut_try_with_alignment_declines_impossible_layouts() {
2387        // Requests without a valid layout return None (the second size rounds
2388        // up past isize::MAX), and a valid one gets its exact size.
2389        let page = NonZeroUsize::new(4096).expect("non-zero alignment");
2390        assert!(IoBufMut::try_with_alignment(isize::MAX as usize + 1, page).is_none());
2391        assert!(IoBufMut::try_with_alignment(isize::MAX as usize - 4094, page).is_none());
2392        assert!(
2393            IoBufMut::try_with_alignment(64, NonZeroUsize::new(3).expect("non-zero")).is_none()
2394        );
2395        let mut buf =
2396            IoBufMut::try_with_alignment(100, page).expect("a page-aligned 100-byte layout");
2397        assert_eq!(buf.capacity(), 100);
2398        assert!((buf.as_mut_ptr() as usize).is_multiple_of(4096));
2399        assert_eq!(
2400            IoBufMut::try_with_alignment(0, page)
2401                .expect("empty")
2402                .capacity(),
2403            0
2404        );
2405    }
2406
2407    #[test]
2408    fn test_iobuf_aligned_public_paths() {
2409        // Exercise the public IoBuf/IoBufMut API on aligned allocations:
2410        // write, advance, copy_to_bytes, freeze, slice, split_to,
2411        // try_into_mut, and From/Into conversions.
2412        static ARRAY: &[u8; 4] = b"wxyz";
2413
2414        let alignment = NonZeroUsize::new(64).expect("non-zero alignment");
2415
2416        // Start from a non-zero aligned buffer to cover the public mutable API.
2417        let mut aligned_mut = IoBufMut::with_alignment(8, alignment);
2418        assert!(aligned_mut.is_empty());
2419        assert_eq!(aligned_mut.capacity(), 8);
2420        assert!((aligned_mut.as_mut_ptr() as usize).is_multiple_of(64));
2421
2422        aligned_mut.put_slice(b"abcdefgh");
2423        assert_eq!(aligned_mut.as_mut(), b"abcdefgh");
2424        assert_eq!(aligned_mut.chunk(), b"abcdefgh");
2425        aligned_mut.advance(2);
2426        assert_eq!(aligned_mut.chunk(), b"cdefgh");
2427
2428        let partial = aligned_mut.copy_to_bytes(2);
2429        assert_eq!(partial.as_ref(), b"cd");
2430        assert_eq!(aligned_mut.as_ref(), b"efgh");
2431        let empty = aligned_mut.copy_to_bytes(0);
2432        assert!(empty.is_empty());
2433        assert_eq!(aligned_mut.as_ref(), b"efgh");
2434
2435        aligned_mut.clear();
2436        assert!(aligned_mut.is_empty());
2437        aligned_mut.put_slice(ARRAY);
2438        assert!(aligned_mut == ARRAY);
2439
2440        // Full aligned drains should use the owner-transfer path, including len == 0 first.
2441        let mut fully_drained = IoBufMut::with_alignment(4, alignment);
2442        fully_drained.put_slice(b"lmno");
2443        let empty = fully_drained.copy_to_bytes(0);
2444        assert!(empty.is_empty());
2445        assert_eq!(fully_drained.as_ref(), b"lmno");
2446        let drained = fully_drained.copy_to_bytes(4);
2447        assert_eq!(drained.as_ref(), b"lmno");
2448        assert!(fully_drained.is_empty());
2449
2450        // Freeze to an immutable aligned `IoBuf` and exercise its view and Buf methods.
2451        let aligned = aligned_mut.freeze();
2452        assert_eq!(aligned.as_ref(), &ARRAY[..]);
2453        assert!(aligned == ARRAY);
2454        assert!(!aligned.as_ptr().is_null());
2455        assert_eq!(aligned.slice(..2), b"wx");
2456        assert_eq!(aligned.slice(1..), b"xyz");
2457        assert_eq!(aligned.slice(1..=2), b"xy");
2458        assert_eq!(aligned.chunk(), b"wxyz");
2459
2460        let mut split = aligned.clone();
2461        let prefix = split.split_to(2);
2462        assert_eq!(prefix, b"wx");
2463        assert_eq!(split, b"yz");
2464
2465        let mut advanced = aligned.clone();
2466        advanced.advance(2);
2467        assert_eq!(advanced.chunk(), b"yz");
2468
2469        // Empty, partial, and full immutable drains return the bytes in order.
2470        let mut drained = aligned.clone();
2471        let empty = drained.copy_to_bytes(0);
2472        assert!(empty.is_empty());
2473        assert_eq!(drained.as_ref(), &ARRAY[..]);
2474        let first = drained.copy_to_bytes(1);
2475        assert_eq!(first.as_ref(), b"w");
2476        let rest = drained.copy_to_bytes(3);
2477        assert_eq!(rest.as_ref(), b"xyz");
2478        assert_eq!(drained.remaining(), 0);
2479
2480        // Unique aligned immutable buffers can become mutable again.
2481        let mut unique_source = IoBufMut::zeroed_with_alignment(4, alignment);
2482        unique_source.as_mut().copy_from_slice(b"pqrs");
2483        let unique = unique_source.freeze();
2484        let recovered = unique
2485            .try_into_mut()
2486            .expect("unique aligned iobuf should recover mutability");
2487        assert_eq!(recovered.as_ref(), b"pqrs");
2488
2489        // Shared aligned immutable buffers must reject the mutable conversion.
2490        let mut shared_source = IoBufMut::zeroed_with_alignment(4, alignment);
2491        shared_source.as_mut().copy_from_slice(b"tuvw");
2492        let shared = shared_source.freeze();
2493        let _shared_clone = shared.clone();
2494        assert!(shared.try_into_mut().is_err());
2495
2496        // Conversions to `Vec` and `Bytes`, and from a static array, preserve the bytes.
2497        let vec_out: Vec<u8> = aligned.clone().into();
2498        let bytes_out: Bytes = aligned.into();
2499        assert_eq!(vec_out, ARRAY.to_vec());
2500        assert_eq!(bytes_out.as_ref(), &ARRAY[..]);
2501
2502        let from_array = IoBuf::from(ARRAY);
2503        assert_eq!(from_array, b"wxyz");
2504
2505        let iobufs = IoBufs::from(ARRAY);
2506        assert_eq!(iobufs.chunk(), b"wxyz");
2507    }
2508
2509    #[test]
2510    fn test_iobufmut_aligned_zero_length_constructors() {
2511        let alignment = NonZeroUsize::new(64).expect("non-zero alignment");
2512
2513        let with_alignment = IoBufMut::with_alignment(0, alignment);
2514        assert!(with_alignment.is_empty());
2515        assert_eq!(with_alignment.len(), 0);
2516        assert_eq!(with_alignment.capacity(), 0);
2517
2518        let zeroed = IoBufMut::zeroed_with_alignment(0, alignment);
2519        assert!(zeroed.is_empty());
2520        assert_eq!(zeroed.len(), 0);
2521        assert_eq!(zeroed.capacity(), 0);
2522
2523        // Zero-sized buffers do not allocate, so alignment is not validated.
2524        let invalid_alignment = NonZeroUsize::new(3).expect("non-zero alignment");
2525        assert_eq!(IoBufMut::with_alignment(0, invalid_alignment).capacity(), 0);
2526        assert_eq!(
2527            IoBufMut::zeroed_with_alignment(0, invalid_alignment).capacity(),
2528            0
2529        );
2530    }
2531
2532    #[test]
2533    fn test_iobufmut_aligned_capacity_stable_across_recovery() {
2534        // Aligned requests get their exact capacity, which survives a
2535        // freeze/try_into_mut round trip.
2536        let alignment = NonZeroUsize::new(4096).expect("non-zero alignment");
2537        let mut buf = IoBufMut::with_alignment(100, alignment);
2538        assert_eq!(buf.capacity(), 100);
2539        assert!((buf.as_mut_ptr() as usize).is_multiple_of(4096));
2540
2541        buf.put_slice(b"data");
2542        let recovered = buf
2543            .freeze()
2544            .try_into_mut()
2545            .expect("unique native view recovers");
2546        assert_eq!(recovered.capacity(), 100);
2547
2548        // The zeroed variant is fully readable and exactly sized.
2549        let zeroed = IoBufMut::zeroed_with_alignment(100, alignment);
2550        assert_eq!(zeroed.len(), 100);
2551        assert_eq!(zeroed.capacity(), 100);
2552    }
2553
2554    #[test]
2555    #[should_panic(expected = "set_len(9) exceeds capacity(8)")]
2556    fn test_iobufmut_set_len_overflow() {
2557        let mut buf = IoBufMut::with_capacity(8);
2558        // SAFETY: this will panic before any read.
2559        unsafe { buf.set_len(9) };
2560    }
2561}
2562
2563// Conformance fixture keys include the test's module path, so this test lives outside the module
2564// that the `miri` test group renames.
2565#[cfg(all(test, not(feature = "loom"), feature = "arbitrary"))]
2566mod tests {
2567    mod conformance {
2568        use super::super::IoBuf;
2569        use commonware_codec::conformance::CodecConformance;
2570
2571        commonware_conformance::conformance_tests! {
2572            CodecConformance<IoBuf>
2573        }
2574    }
2575}