Skip to main content

ntex_bytes/
pages.rs

1#![allow(clippy::missing_panics_doc, clippy::box_collection)]
2use std::{borrow::Borrow, cell::Cell, cmp, collections::VecDeque, fmt, io, mem, ops};
3
4use crate::{Buf, BufMut, BytePageSize, ByteString, Bytes, BytesMut};
5use crate::{buf::UninitSlice, stvec::StorageVec};
6
7/// A growable sequence of byte pages.
8///
9/// Data is stored in fixed-capacity pages selected by [`BytePageSize`]. This
10/// avoids reallocating and copying one large contiguous buffer as data grows.
11pub struct BytePages {
12    st: Option<Box<Inner>>,
13    current: Option<StorageVec>,
14}
15
16#[derive(Debug)]
17struct Inner {
18    size: BytePageSize,
19    /// Total length of `pages`, so `len()` does not walk them.
20    len: usize,
21    pages: VecDeque<BytePage>,
22}
23
24thread_local! {
25    static CACHE: Cell<Option<Box<Vec<Box<Inner>>>>> = Cell::new(Some(Box::default()));
26}
27const CACHE_SIZE: usize = 128;
28
29/// Appended data up to this size is copied into the current page.
30const APPEND_COPY_LIMIT: usize = 4096;
31
32impl BytePages {
33    /// Creates a new `BytePages` with the specified page size.
34    ///
35    /// Pages are allocated lazily using the specified capacity category. Like
36    /// [`BytesMut::with_page_size`], a new page can be a cached page of the
37    /// next smaller size.
38    ///
39    /// # Panics
40    ///
41    /// Panics if `size` is [`BytePageSize::Unset`].
42    pub fn new(size: BytePageSize) -> Self {
43        assert!(size != BytePageSize::Unset, "Page size cannot be Unset");
44
45        // the cache is unavailable while the thread-local is being destroyed
46        let cached = CACHE
47            .try_with(|c| {
48                let mut cache = c.take()?;
49                let item = cache.pop();
50                c.set(Some(cache));
51                item
52            })
53            .ok()
54            .flatten();
55
56        let st = if let Some(mut item) = cached {
57            item.size = size;
58            item
59        } else {
60            Box::new(Inner {
61                size,
62                len: 0,
63                pages: VecDeque::with_capacity(8),
64            })
65        };
66
67        BytePages {
68            st: Some(st),
69            current: None,
70        }
71    }
72
73    fn pages(&self) -> &VecDeque<BytePage> {
74        &self.st.as_ref().unwrap().pages
75    }
76
77    // Pages are only added and removed through these methods, which keep
78    // `Inner::len` up to date. A page in the list is never modified in place.
79    fn push_front(&mut self, page: BytePage) {
80        let st = self.st.as_mut().unwrap();
81        st.len += page.len();
82        st.pages.push_front(page);
83    }
84
85    fn pop_front(&mut self) -> Option<BytePage> {
86        let st = self.st.as_mut().unwrap();
87        let page = st.pages.pop_front()?;
88        st.len -= page.len();
89        Some(page)
90    }
91
92    fn push_back(&mut self, page: BytePage) {
93        let st = self.st.as_mut().unwrap();
94        st.len += page.len();
95        st.pages.push_back(page);
96    }
97
98    /// Returns the capacity category used for new pages.
99    pub fn page_size(&self) -> BytePageSize {
100        self.st.as_ref().unwrap().size
101    }
102
103    /// Sets the page size for new pages.
104    ///
105    /// # Panics
106    ///
107    /// Panics if `size` is [`BytePageSize::Unset`].
108    pub fn set_page_size(&mut self, size: BytePageSize) {
109        assert!(size != BytePageSize::Unset, "Page size cannot be Unset");
110        self.st.as_mut().unwrap().size = size;
111    }
112
113    /// Inserts a non-empty page at the front of the collection.
114    ///
115    /// Returns whether a page was inserted.
116    pub fn prepend<T>(&mut self, buf: T) -> bool
117    where
118        BytePage: From<T>,
119    {
120        let p = BytePage::from(buf);
121        if p.is_empty() {
122            false
123        } else {
124            self.push_front(p);
125            true
126        }
127    }
128
129    /// Appends a page to the back of the collection.
130    ///
131    /// Empty pages are ignored. If the current page holds no data and `buf` is
132    /// a unique buffer with spare capacity, it becomes the new current page.
133    /// Data of up to 4 KiB is copied into the current page, and into new
134    /// pages as needed. Larger data is added as a separate page without
135    /// copying: the filled part of the current page is split off in front of
136    /// it, and the spare capacity of the current page stays available for
137    /// later writes.
138    pub fn append<T>(&mut self, buf: T)
139    where
140        BytePage: From<T>,
141    {
142        let p = BytePage::from(buf);
143        if !p.is_empty() {
144            if self.current_len() == 0 {
145                match p.into_storage() {
146                    Ok(st) => {
147                        self.current = Some(st);
148                    }
149                    Err(page) => {
150                        // add buffer to the page list
151                        self.push_back(page);
152                    }
153                }
154            } else if p.len() <= APPEND_COPY_LIMIT {
155                self.put_slice(p.as_ref());
156            } else {
157                // the spare capacity of the current page is kept
158                if let Some(st) = self.current.as_mut() {
159                    let head = st.split_to(st.len());
160                    self.push_back(<BytePage as From<Bytes>>::from(Bytes { storage: head }));
161                }
162                self.push_back(p);
163            }
164        }
165    }
166
167    #[inline]
168    /// Appends the given bytes to this page object.
169    ///
170    /// Tries to write the data into the current page first. If there
171    /// is insufficient space, one or more new pages are allocated as
172    /// needed, and the remaining data is copied into them.
173    pub fn extend_from_slice(&mut self, extend: &[u8]) {
174        self.put_slice(extend);
175    }
176
177    #[inline]
178    /// Returns the total number of buffered bytes.
179    pub fn len(&self) -> usize {
180        self.st.as_ref().unwrap().len + self.current_len()
181    }
182
183    fn current_len(&self) -> usize {
184        self.current
185            .as_ref()
186            .map(StorageVec::len)
187            .unwrap_or_default()
188    }
189
190    // spare capacity of the current page
191    fn spare(&self) -> usize {
192        self.current
193            .as_ref()
194            .map(StorageVec::remaining)
195            .unwrap_or_default()
196    }
197
198    #[inline]
199    /// Returns `true` if no bytes are buffered.
200    pub fn is_empty(&self) -> bool {
201        self.len() == 0
202    }
203
204    #[inline]
205    /// Returns the number of allocated pages containing buffered data.
206    pub fn num_pages(&self) -> usize {
207        if self.current_len() == 0 {
208            self.pages().len()
209        } else {
210            self.pages().len() + 1
211        }
212    }
213
214    /// Removes and returns the first page from the collection.
215    ///
216    /// The current writable page is returned last. Returns `None` if there are
217    /// no pages with data, an empty current page keeps its spare capacity for
218    /// later writes.
219    pub fn take(&mut self) -> Option<BytePage> {
220        if let Some(page) = self.pop_front() {
221            Some(page)
222        } else if self.current_len() == 0 {
223            None
224        } else {
225            self.current.take().map(BytePage::from)
226        }
227    }
228
229    /// Returns an iterator over the buffered data, one slice per page.
230    ///
231    /// The current writable page is yielded last, if it holds data. The
232    /// pages are stored on the heap, so their data stays at the same address
233    /// when `self` is moved, until `self` is modified or dropped.
234    pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
235        self.pages().iter().map(BytePage::as_ref).chain(
236            self.current
237                .as_ref()
238                .map(StorageVec::as_ref)
239                .filter(|s| !s.is_empty()),
240        )
241    }
242
243    /// Advance the internal cursor.
244    ///
245    /// Afterwards `self` contains elements `[cnt, len)`. Fully consumed pages
246    /// are dropped, the current writable page keeps its spare capacity.
247    ///
248    /// # Panics
249    ///
250    /// Panics if `cnt > len`.
251    pub fn advance_to(&mut self, mut cnt: usize) {
252        assert!(
253            cnt <= self.len(),
254            "cannot advance past the end of the buffer, cnt:{cnt} len:{}",
255            self.len()
256        );
257
258        while cnt > 0
259            && let Some(mut page) = self.pop_front()
260        {
261            if cnt < page.len() {
262                page.advance_to(cnt);
263                self.push_front(page);
264                return;
265            }
266            cnt -= page.len();
267        }
268        if cnt > 0
269            && let Some(st) = self.current.as_mut()
270        {
271            // SAFETY: `cnt <= st.len()`, checked by the assert above
272            unsafe { st.set_start(cnt) };
273        }
274    }
275
276    #[inline]
277    /// Appends all buffered data to another [`BytePages`] value, `self` is
278    /// left unchanged.
279    ///
280    /// Pages are shared with `pages` rather than copied, unless they are small
281    /// enough to be copied into the current page of `pages`, see
282    /// [`append`](Self::append).
283    pub fn copy_to(&self, pages: &mut BytePages) {
284        for p in self.pages() {
285            pages.append(p.clone());
286        }
287
288        if let Some(st) = &self.current {
289            // an immutable view, `st` stays the only handle that can write
290            // to the spare capacity
291            pages.append(Bytes {
292                storage: st.shallow_freeze(),
293            });
294        }
295    }
296
297    /// Moves all buffered data to the back of another [`BytePages`] value,
298    /// leaving `self` empty.
299    ///
300    /// Pages are moved as is, without copying or merging. The filled part of
301    /// the current page of `pages` is split off in front of the moved data,
302    /// its spare capacity stays available for later writes. The current page
303    /// of `self` is moved as a regular page.
304    pub fn move_to(&mut self, pages: &mut BytePages) {
305        if self.is_empty() {
306            return;
307        }
308
309        if let Some(st) = pages.current.as_mut()
310            && st.len() != 0
311        {
312            let head = st.split_to(st.len());
313            pages.push_back(<BytePage as From<Bytes>>::from(Bytes { storage: head }));
314        }
315
316        let src = self.st.as_mut().unwrap();
317        let dst = pages.st.as_mut().unwrap();
318        if dst.pages.is_empty() {
319            mem::swap(&mut src.pages, &mut dst.pages);
320            dst.len = mem::take(&mut src.len);
321        } else {
322            while let Some(page) = self.pop_front() {
323                pages.push_back(page);
324            }
325        }
326
327        if self.current_len() != 0
328            && let Some(st) = self.current.take()
329        {
330            pages.push_back(BytePage::from(st));
331        }
332    }
333
334    /// Splits the buffer into two at the given index.
335    ///
336    /// Afterwards, `self` contains elements `[at, len)`, and the returned [`BytePages`]
337    /// contains elements `[0, at)`. If `at > len`, all data is moved.
338    ///
339    /// Unlike [`Bytes::split_to`] and [`BytesMut::split_to`](crate::BytesMut::split_to),
340    /// which panic when `at` is out of bounds, this clamps `at` to the length.
341    /// Check `len()` first if a short split is an error.
342    ///
343    /// Depending on the underlying storage, this operation might be `O(1)` or could
344    /// involve a memory copy.
345    #[must_use]
346    pub fn split_to(&mut self, at: usize) -> BytePages {
347        let mut pages = BytePages::new(self.page_size());
348        self.split_into(at, &mut pages);
349        pages
350    }
351
352    /// Splits the buffer, adding the resulting items to the supplied pages object.
353    ///
354    /// Afterwards, `self` contains elements `[at, len)`, and elements `[0, at)`
355    /// are appended to `to`. If `at > len`, all data is moved.
356    ///
357    /// Like [`split_to`](Self::split_to), this clamps `at` to the length instead
358    /// of panicking.
359    ///
360    /// Depending on the underlying storage, this operation might be `O(1)` or could
361    /// involve a memory copy.
362    pub fn split_into(&mut self, mut at: usize, to: &mut BytePages) {
363        while let Some(mut page) = self.pop_front() {
364            let len = cmp::min(page.len(), at);
365            to.append(page.split_to(len));
366
367            if !page.is_empty() {
368                self.push_front(page);
369                return;
370            }
371            at -= len;
372        }
373        if at > 0
374            && let Some(mut st) = self.current.take()
375        {
376            if at < st.len() {
377                // the remainder stays writable, so its spare capacity is kept
378                to.append(Bytes {
379                    storage: st.split_to(at),
380                });
381                self.current = Some(st);
382            } else if st.len() == 0 {
383                self.current = Some(st);
384            } else {
385                to.append(BytePage::from(st));
386            }
387        }
388    }
389
390    /// Clears the buffer, removing all data.
391    #[inline]
392    pub fn clear(&mut self) {
393        while self.take().is_some() {}
394    }
395
396    /// Drains all pages into one immutable [`Bytes`] value.
397    #[inline]
398    #[must_use]
399    pub fn freeze(&mut self) -> Bytes {
400        let pages = self.num_pages();
401        if pages == 0 || self.is_empty() {
402            Bytes::new()
403        } else if pages == 1 {
404            self.take().unwrap().freeze()
405        } else {
406            let mut buf = BytesMut::with_capacity(self.len());
407            while let Some(p) = self.take() {
408                buf.extend_from_slice(&p);
409            }
410            buf.freeze()
411        }
412    }
413
414    #[inline]
415    /// Moves the current writable page from `pages` if this value is empty.
416    pub fn try_get_current_from(&mut self, pages: &mut BytePages) {
417        if self.pages().is_empty()
418            && self.current.is_none()
419            && let Some(st) = pages.current.take()
420        {
421            self.current = Some(st);
422        }
423    }
424
425    /// Passes a new mutable buffer to `f` and appends its contents.
426    ///
427    /// After `f` returns, the buffer is appended according to the rules of
428    /// [`append`](Self::append). Existing pages and the current writable page
429    /// are not exposed to `f`. If `f` panics, the temporary buffer is dropped
430    /// and this collection is unchanged.
431    ///
432    /// This is a low-level API intended for ntex internals and may change
433    /// without notice.
434    #[doc(hidden)]
435    #[deprecated(
436        since = "1.10.0",
437        note = "use the `BufMut` methods of `BytePages` instead"
438    )]
439    pub fn with_bytes_mut<F, R>(&mut self, f: F) -> R
440    where
441        F: FnOnce(&mut BytesMut) -> R,
442    {
443        let mut buf = BytesMut::new();
444        let res = f(&mut buf);
445        self.append(buf);
446        res
447    }
448
449    // Returns the current page if it has spare capacity, or a new page.
450    //
451    // The current page can be full, for example after `advance_mut` filled
452    // it, a full page moves to the page list, an empty one is dropped.
453    fn take_writable(&mut self) -> StorageVec {
454        match self.current.take() {
455            Some(st) if !st.is_full() => st,
456            Some(st) => {
457                if st.len() != 0 {
458                    self.push_back(BytePage::from(st));
459                }
460                StorageVec::sized(self.page_size())
461            }
462            None => StorageVec::sized(self.page_size()),
463        }
464    }
465
466    fn with_current<F, R>(&mut self, f: F) -> R
467    where
468        F: FnOnce(&mut StorageVec) -> R,
469    {
470        let mut st = self.take_writable();
471        let result = f(&mut st);
472
473        // a full page moves to the page list
474        if st.is_full() {
475            self.push_back(BytePage::from(st));
476        } else {
477            self.current = Some(st);
478        }
479
480        result
481    }
482}
483
484impl Drop for BytePages {
485    fn drop(&mut self) {
486        if let Some(mut st) = self.st.take() {
487            st.pages.clear();
488            // a large write must not pin its page list in the cache
489            st.pages.shrink_to(8);
490            st.len = 0;
491            // the cache is unavailable while the thread-local is being destroyed
492            let _ = CACHE.try_with(move |c| {
493                if let Some(mut cache) = c.take() {
494                    if cache.len() < CACHE_SIZE {
495                        cache.push(st);
496                    }
497                    c.set(Some(cache));
498                }
499            });
500        }
501    }
502}
503
504impl fmt::Debug for BytePages {
505    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
506        let mut f = fmt.debug_tuple("BytePages");
507        for p in self.pages() {
508            f.field(p);
509        }
510        if let Some(st) = &self.current {
511            f.field(&crate::debug::BsDebug(st.as_ref()));
512        }
513        f.finish()
514    }
515}
516
517impl Default for BytePages {
518    fn default() -> Self {
519        BytePages::new(BytePageSize::Size16)
520    }
521}
522
523/// Pages are allocated on demand, so `remaining_mut()` reports
524/// `usize::MAX - len` and `chunk_mut()` is never empty. The chunk covers the
525/// spare capacity of the current page only.
526impl BufMut for BytePages {
527    #[inline]
528    fn remaining_mut(&self) -> usize {
529        usize::MAX - self.len()
530    }
531
532    #[inline]
533    unsafe fn advance_mut(&mut self, cnt: usize) {
534        if cnt == 0 {
535            return;
536        }
537        let spare = self.spare();
538        assert!(
539            cnt <= spare,
540            "cannot advance past the current page: {cnt:?} <= {spare:?}"
541        );
542        let st = self.current.as_mut().unwrap();
543        st.set_len(st.len() + cnt);
544    }
545
546    #[inline]
547    fn chunk_mut(&mut self) -> &mut UninitSlice {
548        let st = self.take_writable();
549        self.current.insert(st).spare_mut()
550    }
551
552    fn put<T: Buf>(&mut self, mut src: T)
553    where
554        Self: Sized,
555    {
556        while src.has_remaining() {
557            let chunk = src.chunk();
558            let len = chunk.len();
559            self.put_slice(chunk);
560            src.advance(len);
561        }
562    }
563
564    #[inline]
565    fn put_slice(&mut self, src: &[u8]) {
566        // a small write that leaves the current page non-full is copied in place
567        if let Some(st) = self.current.as_mut()
568            && st.remaining() > src.len()
569        {
570            st.put_slice_partial(src);
571        } else {
572            self.put_slice_pages(src);
573        }
574    }
575
576    #[inline]
577    fn put_u8(&mut self, n: u8) {
578        self.with_current(|st| st.put_u8(n));
579    }
580
581    #[inline]
582    fn put_i8(&mut self, n: i8) {
583        self.put_u8(n as u8);
584    }
585}
586
587impl BytePages {
588    fn put_slice_pages(&mut self, mut src: &[u8]) {
589        while !src.is_empty() {
590            let amount = self.with_current(|st| st.put_slice_partial(src));
591
592            src = &src[amount..];
593        }
594    }
595}
596
597impl Clone for BytePages {
598    fn clone(&self) -> Self {
599        let size = self.page_size();
600        let mut pages = BytePages::new(size);
601        self.copy_to(&mut pages);
602        pages
603    }
604}
605
606impl io::Write for BytePages {
607    fn write(&mut self, src: &[u8]) -> Result<usize, io::Error> {
608        self.put_slice(src);
609        Ok(src.len())
610    }
611
612    fn flush(&mut self) -> Result<(), io::Error> {
613        Ok(())
614    }
615}
616
617impl From<BytePages> for Bytes {
618    /// A single page is converted without copying.
619    fn from(mut pages: BytePages) -> Bytes {
620        pages.freeze()
621    }
622}
623
624impl From<BytePages> for BytesMut {
625    /// A single page is converted without copying if nothing else refers to
626    /// its buffer.
627    fn from(mut pages: BytePages) -> BytesMut {
628        if pages.num_pages() == 1 {
629            return BytesMut::from(pages.take().unwrap());
630        }
631
632        let mut buf = BytesMut::with_capacity(pages.len());
633        while let Some(p) = pages.take() {
634            buf.extend_from_slice(&p);
635        }
636        buf
637    }
638}
639
640/// A contiguous chunk stored by [`BytePages`].
641pub struct BytePage {
642    inner: StorageType,
643}
644
645enum StorageType {
646    Bytes(Bytes),
647    Storage(StorageVec),
648    Vec(Vec<u8>),
649}
650
651impl BytePage {
652    #[inline]
653    /// Returns the number of bytes contained in this `BytePage`.
654    pub fn len(&self) -> usize {
655        self.as_ref().len()
656    }
657
658    #[inline]
659    /// Returns `true` if the page is empty.
660    pub fn is_empty(&self) -> bool {
661        self.len() == 0
662    }
663
664    #[inline]
665    /// Returns a raw pointer to the data.
666    ///
667    /// # Safety
668    ///
669    /// The returned pointer may only be dereferenced while the page is neither
670    /// moved, modified nor dropped, and only for [`len`](Self::len) bytes. An
671    /// inline page stores its data inside the `BytePage` itself, so moving the
672    /// page invalidates the pointer, see [`is_inline`](Self::is_inline).
673    pub unsafe fn as_ptr(&self) -> *const u8 {
674        // SAFETY: Each variant keeps its backing storage alive; the caller must preserve the page
675        // while using the pointer.
676        unsafe {
677            match &self.inner {
678                StorageType::Bytes(b) => b.storage.as_ptr(),
679                StorageType::Storage(b) => b.as_ptr(),
680                StorageType::Vec(b) => b.as_ptr(),
681            }
682        }
683    }
684
685    #[inline]
686    #[doc(hidden)]
687    /// Returns the kind of storage backing this page.
688    pub fn info(&self) -> crate::info::PageKind {
689        match &self.inner {
690            StorageType::Bytes(_) => crate::info::PageKind::Bytes,
691            StorageType::Storage(_) => crate::info::PageKind::Storage,
692            StorageType::Vec(_) => crate::info::PageKind::Vec,
693        }
694    }
695
696    #[inline]
697    #[doc(hidden)]
698    /// Returns `true` if the data is stored inside the `BytePage` itself.
699    ///
700    /// Moving an inline page moves its data, pointers from `as_ptr()` do not
701    /// survive the move.
702    pub fn is_inline(&self) -> bool {
703        matches!(&self.inner, StorageType::Bytes(b) if b.is_inline())
704    }
705
706    /// Splits the buffer into two at the given index.
707    ///
708    /// Afterwards, `self` contains elements `[at, len)`, and the returned `BytePage`
709    /// contains elements `[0, at)`. If `at > len`, all data is moved.
710    ///
711    /// Unlike [`Bytes::split_to`] and [`BytesMut::split_to`](crate::BytesMut::split_to),
712    /// which panic when `at` is out of bounds, this clamps `at` to the length.
713    /// Check `len()` first if a short split is an error.
714    ///
715    /// Depending on the underlying storage, this operation might be `O(1)` or could
716    /// involve a memory copy.
717    #[must_use]
718    pub fn split_to(&mut self, at: usize) -> BytePage {
719        match &mut self.inner {
720            StorageType::Bytes(b) => {
721                let buf = b.split_to(cmp::min(at, b.len()));
722                BytePage {
723                    inner: StorageType::Bytes(buf),
724                }
725            }
726            StorageType::Storage(_) => {
727                let inner = mem::replace(&mut self.inner, StorageType::Bytes(Bytes::new()));
728                if let StorageType::Storage(st) = inner {
729                    self.inner = StorageType::Bytes(Bytes {
730                        storage: st.freeze(),
731                    });
732                    self.split_to(at)
733                } else {
734                    unreachable!()
735                }
736            }
737            StorageType::Vec(_) => {
738                let inner = mem::replace(&mut self.inner, StorageType::Bytes(Bytes::new()));
739                if let StorageType::Vec(b) = inner {
740                    self.inner = StorageType::Bytes(Bytes::copy_from_slice(&b));
741                    self.split_to(at)
742                } else {
743                    unreachable!()
744                }
745            }
746        }
747    }
748
749    /// Advance the internal cursor.
750    ///
751    /// Afterwards `self` contains elements `[cnt, len)`.
752    /// This is an `O(1)` operation, except for pages backed by a `Vec<u8>`,
753    /// whose remaining data is copied.
754    ///
755    /// # Panics
756    ///
757    /// Panics if `cnt > len`.
758    #[inline]
759    pub fn advance_to(&mut self, cnt: usize) {
760        match &mut self.inner {
761            StorageType::Bytes(b) => b.advance_to(cnt),
762            // SAFETY: `set_start` checks `cnt <= len` before advancing within the live, exclusively
763            // owned view.
764            StorageType::Storage(b) => unsafe { b.set_start(cnt) },
765            StorageType::Vec(b) => {
766                assert!(
767                    cnt <= b.len(),
768                    "cannot advance past the end of the buffer, cnt:{cnt} len:{}",
769                    b.len()
770                );
771                self.inner = StorageType::Bytes(Bytes::copy_from_slice(&b[cnt..]));
772            }
773        }
774    }
775
776    /// Converts `self` into an immutable `Bytes`.
777    #[inline]
778    #[must_use]
779    pub fn freeze(self) -> Bytes {
780        match self.inner {
781            StorageType::Bytes(b) => b,
782            StorageType::Storage(st) => Bytes {
783                storage: st.freeze(),
784            },
785            StorageType::Vec(v) => Bytes::from(v),
786        }
787    }
788
789    fn into_storage(self) -> Result<StorageVec, Self> {
790        if let StorageType::Storage(st) = self.inner {
791            // Converting back to `StorageVec` requires uniqueness.
792            if !st.is_full() && st.is_unique() {
793                Ok(st)
794            } else {
795                Err(Self {
796                    inner: StorageType::Storage(st),
797                })
798            }
799        } else {
800            Err(self)
801        }
802    }
803}
804
805impl Clone for BytePage {
806    fn clone(&self) -> Self {
807        let inner = match &self.inner {
808            StorageType::Bytes(b) => StorageType::Bytes(b.clone()),
809            // The clone is an immutable view, `st` must stay the only
810            // handle that can modify the shared header and spare capacity
811            StorageType::Storage(st) => StorageType::Bytes(Bytes {
812                storage: st.shallow_freeze(),
813            }),
814            StorageType::Vec(b) => StorageType::Bytes(Bytes::copy_from_slice(b)),
815        };
816
817        Self { inner }
818    }
819}
820
821impl AsRef<[u8]> for BytePage {
822    #[inline]
823    fn as_ref(&self) -> &[u8] {
824        match &self.inner {
825            StorageType::Bytes(b) => b.as_ref(),
826            StorageType::Storage(b) => b.as_ref(),
827            StorageType::Vec(b) => b.as_ref(),
828        }
829    }
830}
831
832impl Borrow<[u8]> for BytePage {
833    #[inline]
834    fn borrow(&self) -> &[u8] {
835        self.as_ref()
836    }
837}
838
839impl From<Bytes> for BytePage {
840    fn from(buf: Bytes) -> Self {
841        BytePage {
842            inner: StorageType::Bytes(buf),
843        }
844    }
845}
846
847impl<'a> From<&'a Bytes> for BytePage {
848    fn from(buf: &'a Bytes) -> Self {
849        BytePage {
850            inner: StorageType::Bytes(buf.clone()),
851        }
852    }
853}
854
855impl From<BytesMut> for BytePage {
856    fn from(buf: BytesMut) -> Self {
857        BytePage {
858            inner: StorageType::Storage(buf.storage),
859        }
860    }
861}
862
863impl From<ByteString> for BytePage {
864    fn from(s: ByteString) -> Self {
865        s.into_bytes().into()
866    }
867}
868
869impl<'a> From<&'a ByteString> for BytePage {
870    fn from(s: &'a ByteString) -> Self {
871        s.clone().into_bytes().into()
872    }
873}
874
875impl From<StorageVec> for BytePage {
876    fn from(buf: StorageVec) -> Self {
877        BytePage {
878            inner: StorageType::Storage(buf),
879        }
880    }
881}
882
883impl From<Vec<u8>> for BytePage {
884    fn from(buf: Vec<u8>) -> Self {
885        BytePage {
886            inner: StorageType::Vec(buf),
887        }
888    }
889}
890
891impl From<&'static str> for BytePage {
892    fn from(buf: &'static str) -> Self {
893        Bytes::from_static(buf.as_bytes()).into()
894    }
895}
896
897impl From<&'static [u8]> for BytePage {
898    fn from(buf: &'static [u8]) -> Self {
899        Bytes::from_static(buf).into()
900    }
901}
902
903impl<const N: usize> From<&'static [u8; N]> for BytePage {
904    fn from(buf: &'static [u8; N]) -> Self {
905        Bytes::from_static(buf).into()
906    }
907}
908
909impl From<BytePage> for Bytes {
910    fn from(page: BytePage) -> Self {
911        match page.inner {
912            StorageType::Bytes(b) => b,
913            StorageType::Storage(storage) => BytesMut { storage }.freeze(),
914            StorageType::Vec(v) => Bytes::copy_from_slice(&v),
915        }
916    }
917}
918
919impl From<BytePage> for BytesMut {
920    fn from(page: BytePage) -> Self {
921        match page.inner {
922            StorageType::Bytes(b) => b.into(),
923            // clones of the page may still read the data
924            StorageType::Storage(storage) => {
925                if storage.is_unique() {
926                    BytesMut { storage }
927                } else {
928                    BytesMut::copy_from_slice(storage.as_ref())
929                }
930            }
931            StorageType::Vec(v) => BytesMut::copy_from_slice(&v),
932        }
933    }
934}
935
936impl PartialEq for BytePage {
937    fn eq(&self, other: &BytePage) -> bool {
938        self.as_ref() == other.as_ref()
939    }
940}
941
942impl_partial_eq!(BytePage);
943
944impl_read!(BytePage);
945
946impl ops::Deref for BytePage {
947    type Target = [u8];
948
949    #[inline]
950    fn deref(&self) -> &[u8] {
951        self.as_ref()
952    }
953}
954
955impl fmt::Debug for BytePage {
956    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
957        fmt::Debug::fmt(&crate::debug::BsDebug(self.as_ref()), fmt)
958    }
959}
960
961#[cfg(test)]
962mod tests {
963    use rand::Rng;
964
965    use super::*;
966
967    /// STATEMENT: `BytePage` compares by content across byte and string types and supports consuming reads.
968    ///
969    /// HOW:
970    /// - Compare a vector-backed `hello` page with borrowed and owned equivalents in both directions and reject `hell`.
971    /// - Read it into a three-byte array, checking `hel`, the `lo` remainder, short and exhausted reads, and final emptiness.
972    #[test]
973    #[allow(clippy::op_ref, clippy::cmp_owned)]
974    fn page_eq_and_read() {
975        use std::io::Read;
976
977        let mut page = BytePage::from(Vec::from(&b"hello"[..]));
978        assert_eq!(page, b"hello"[..]);
979        assert_eq!(page, *b"hello");
980        assert_eq!(page, b"hello");
981        assert_eq!(page, &b"hello"[..]);
982        assert_eq!(page, "hello");
983        assert_eq!(page, *"hello");
984        assert_eq!(page, b"hello".to_vec());
985        assert_eq!(page, String::from("hello"));
986        assert_eq!(b"hello"[..], page);
987        assert_eq!(*b"hello", page);
988        assert_eq!(b"hello", page);
989        assert_eq!(&b"hello"[..], page);
990        assert_eq!("hello", page);
991        assert_eq!(*"hello", page);
992        assert_eq!(b"hello".to_vec(), page);
993        assert_eq!(String::from("hello"), page);
994        assert_ne!(page, "hell");
995
996        let mut buf = [0u8; 3];
997        assert_eq!(page.read(&mut buf).unwrap(), 3);
998        assert_eq!(&buf, b"hel");
999        assert_eq!(page, "lo");
1000        assert_eq!(page.read(&mut buf).unwrap(), 2);
1001        assert_eq!(page.read(&mut buf).unwrap(), 0);
1002        assert!(page.is_empty());
1003    }
1004
1005    /// STATEMENT: Page metadata distinguishes bytes, mutable storage, vector storage, and inline bytes.
1006    ///
1007    /// HOW:
1008    /// - Convert 64-byte `Bytes`, `BytesMut`, and vectors and check their `PageKind` values.
1009    /// - Compare inline status for four-byte and 64-byte immutable buffers and a short mutable buffer.
1010    #[test]
1011    fn page_info() {
1012        use crate::info::PageKind;
1013
1014        let p = BytePage::from(Bytes::copy_from_slice(&[1; 64]));
1015        assert_eq!(p.info(), PageKind::Bytes);
1016        let p = BytePage::from(BytesMut::copy_from_slice(&[1; 64][..]));
1017        assert_eq!(p.info(), PageKind::Storage);
1018        let p = BytePage::from(vec![1; 64]);
1019        assert_eq!(p.info(), PageKind::Vec);
1020        assert!(!p.is_inline());
1021
1022        assert!(BytePage::from(Bytes::copy_from_slice(&[1; 4])).is_inline());
1023        assert!(!BytePage::from(Bytes::copy_from_slice(&[1; 64])).is_inline());
1024        assert!(!BytePage::from(BytesMut::copy_from_slice(&[1; 4][..])).is_inline());
1025    }
1026
1027    /// STATEMENT: Appending small data copies into the current page while large data retains its own allocation.
1028    ///
1029    /// HOW:
1030    /// - Append payloads at and above `APPEND_COPY_LIMIT` after a header and check page counts, spare capacity, and storage pointers.
1031    /// - Add a trailer, drain pages in order, compare concatenated contents, and verify an empty current page remains reusable.
1032    #[test]
1033    fn append_copies_small_and_splits_for_large() {
1034        let cap = BytePageSize::Size16.capacity();
1035        let mut pages = BytePages::new(BytePageSize::Size16);
1036        pages.extend_from_slice(b"head\r\n");
1037
1038        // small data is copied into the current page
1039        pages.append(Bytes::copy_from_slice(&[1; APPEND_COPY_LIMIT]));
1040        assert_eq!(pages.num_pages(), 1);
1041        assert_eq!(pages.current_len(), 6 + APPEND_COPY_LIMIT);
1042
1043        // large data is not copied, the filled part is split off in front
1044        let body = Bytes::copy_from_slice(&[2; APPEND_COPY_LIMIT + 1]);
1045        let body_ptr = body.as_ptr();
1046        pages.append(body.clone());
1047        assert_eq!(pages.num_pages(), 2);
1048        assert_eq!(pages.current_len(), 0);
1049        assert_eq!(pages.spare(), cap - 6 - APPEND_COPY_LIMIT);
1050
1051        // later writes use the spare capacity of the current page
1052        let page_ptr = pages.pages()[0].as_ref().as_ptr();
1053        pages.extend_from_slice(b"\r\n");
1054        assert_eq!(pages.num_pages(), 3);
1055        let tail_ptr = pages.current.as_ref().unwrap().as_ref().as_ptr();
1056        assert_eq!(tail_ptr, page_ptr.wrapping_add(6 + APPEND_COPY_LIMIT));
1057
1058        let mut expected = b"head\r\n".to_vec();
1059        expected.extend_from_slice(&[1; APPEND_COPY_LIMIT]);
1060        expected.extend_from_slice(&body);
1061        expected.extend_from_slice(b"\r\n");
1062        assert_eq!(pages.len(), expected.len());
1063
1064        let first = pages.take().unwrap();
1065        let second = pages.take().unwrap();
1066        assert_eq!(second.as_ref().as_ptr(), body_ptr);
1067        let third = pages.take().unwrap();
1068        assert!(pages.take().is_none());
1069        let data = [first.as_ref(), second.as_ref(), third.as_ref()].concat();
1070        assert_eq!(data, expected);
1071
1072        // draining the pages keeps an empty current page for later writes
1073        pages.extend_from_slice(b"x");
1074        pages.append(body);
1075        assert_eq!(pages.take().unwrap().as_ref(), b"x");
1076        assert_eq!(pages.take().unwrap().len(), APPEND_COPY_LIMIT + 1);
1077        assert!(pages.take().is_none());
1078        assert_eq!(pages.num_pages(), 0);
1079        assert_eq!(pages.spare(), cap - 1);
1080    }
1081
1082    /// STATEMENT: Pages track lengths, page boundaries, conversions, and debug output as data is added and removed.
1083    ///
1084    /// HOW:
1085    /// - Fill `Size8` buffers across page boundaries, append a full page, and check lengths, page counts, spare capacity, and drained contents.
1086    /// - Convert byte and string inputs to pages, advance and freeze them, then check debug ordering and clearing.
1087    #[test]
1088    fn pages() {
1089        let cap = BytePageSize::Size8.capacity();
1090        // SAFETY: Each `as_ptr` result is compared immediately while its page remains live,
1091        // unmoved, and unchanged.
1092        unsafe {
1093            // pages
1094            let mut pages = BytePages::new(BytePageSize::Size8);
1095            assert!(pages.is_empty());
1096            assert_eq!(pages.len(), 0);
1097            assert_eq!(pages.num_pages(), 0);
1098            pages.extend_from_slice(b"b");
1099            assert_eq!(pages.len(), 1);
1100            assert_eq!(pages.num_pages(), 1);
1101            pages.extend_from_slice("a".repeat(9 * 1024).as_bytes());
1102            assert_eq!(pages.len(), 9217);
1103            assert_eq!(pages.num_pages(), 2);
1104            assert!(!pages.is_empty());
1105
1106            let mut pgs = BytePages::new(BytePageSize::Size8);
1107            pgs.put_i8(b'a' as i8);
1108            let p = pgs.take().unwrap();
1109            assert_eq!(p.len(), 1);
1110            assert_eq!(p.as_ref(), b"a");
1111
1112            pgs.extend_from_slice("a".repeat(cap - 1).as_bytes());
1113            assert_eq!(pgs.num_pages(), 1);
1114            pgs.put_u8(b'a');
1115            assert_eq!(pgs.num_pages(), 1);
1116            assert!(pgs.current.is_none());
1117
1118            pgs.put_u8(b'a');
1119            assert_eq!(pgs.num_pages(), 2);
1120
1121            pgs.append(Bytes::copy_from_slice("a".repeat(cap).as_bytes()));
1122            assert_eq!(pgs.num_pages(), 3);
1123            assert_eq!(pgs.current_len(), 0);
1124            assert_eq!(pgs.spare(), cap - 1);
1125
1126            // page
1127            let p = pages.take().unwrap();
1128            assert_eq!(p.len(), cap);
1129            let p = pages.take().unwrap();
1130            assert_eq!(p.len(), 9217 - cap);
1131            assert!(!p.is_empty());
1132            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1133            assert_eq!(p.as_ref(), "a".repeat(9217 - cap).as_bytes());
1134            assert!(pages.take().is_none());
1135
1136            let p = BytePage::from(Bytes::copy_from_slice(b"123"));
1137            assert_eq!(p.len(), 3);
1138            assert!(!p.is_empty());
1139            assert_eq!(p.as_ref(), b"123");
1140            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1141
1142            let p = BytePage::from(&b"123"[..]);
1143            assert_eq!(p.len(), 3);
1144            assert!(!p.is_empty());
1145            assert_eq!(p.as_ref(), b"123");
1146            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1147
1148            let p = BytePage::from(b"123");
1149            assert_eq!(p.len(), 3);
1150            assert!(!p.is_empty());
1151            assert_eq!(p.as_ref(), b"123");
1152            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1153
1154            let p = BytePage::from("123");
1155            assert_eq!(p.len(), 3);
1156            assert!(!p.is_empty());
1157            assert_eq!(p.as_ref(), b"123");
1158            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1159            assert_eq!(p.freeze(), b"123");
1160
1161            let p = BytePage::from(vec![b'1', b'2', b'3']);
1162            assert_eq!(p.len(), 3);
1163            assert!(!p.is_empty());
1164            assert_eq!(p.as_ref(), b"123");
1165            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1166            assert_eq!(p.freeze(), b"123");
1167
1168            let mut p = BytePage::from(vec![b'1', b'2', b'3']);
1169            p.advance_to(1);
1170            assert_eq!(p.len(), 2);
1171            assert!(!p.is_empty());
1172            assert_eq!(p.as_ref(), b"23");
1173
1174            // debug
1175            let mut pages = BytePages::new(BytePageSize::Size8);
1176            pages.extend_from_slice(b"b");
1177            assert_eq!(format!("{pages:?}"), "BytePages(b\"b\")");
1178            let p = pages.take().unwrap();
1179            assert_eq!(p.as_ref(), b"b");
1180
1181            let mut pages = BytePages::new(BytePageSize::Size8);
1182            pages.extend_from_slice(b"a");
1183            pages.append(Bytes::copy_from_slice(b"123"));
1184            pages.push_back(p);
1185            assert_eq!(format!("{pages:?}"), "BytePages(b\"b\", b\"a123\")");
1186
1187            assert_eq!(pages.len(), 5);
1188            pages.clear();
1189            assert_eq!(pages.len(), 0);
1190        }
1191    }
1192
1193    /// Checks the tracked length against the pages.
1194    fn assert_len(pages: &BytePages) {
1195        let len = pages.pages().iter().map(BytePage::len).sum::<usize>() + pages.current_len();
1196        assert_eq!(pages.len(), len);
1197        assert_eq!(pages.is_empty(), len == 0);
1198    }
1199
1200    /// STATEMENT: Tracked total length stays consistent with page contents across mixed buffer operations.
1201    ///
1202    /// HOW:
1203    /// - Randomly extend, append, prepend, take, split, move, copy, and write bytes while maintaining an expected length.
1204    /// - Check both buffers against their constituent page lengths after each operation, then verify freezing and clearing leave empty buffers.
1205    #[test]
1206    fn pages_len_tracking() {
1207        let mut rng = rand::rng();
1208        let mut pages = BytePages::new(BytePageSize::Size8);
1209        let mut other = BytePages::new(BytePageSize::Size8);
1210        let mut expected = 0;
1211
1212        let (iters, max) = if cfg!(miri) { (100, 256) } else { (2000, 12 * 1024) };
1213        for _ in 0..iters {
1214            let n = rng.random_range(0..max);
1215            match rng.random_range(0..9) {
1216                0 => {
1217                    pages.extend_from_slice(&vec![1; n]);
1218                    expected += n;
1219                }
1220                1 => {
1221                    pages.append(Bytes::copy_from_slice(&vec![2; n]));
1222                    expected += n;
1223                }
1224                2 => {
1225                    if pages.prepend(BytesMut::copy_from_slice(vec![3; n])) {
1226                        expected += n;
1227                    }
1228                }
1229                3 => {
1230                    if let Some(p) = pages.take() {
1231                        expected -= p.len();
1232                    }
1233                }
1234                4 => {
1235                    let at = cmp::min(n, expected);
1236                    let split = pages.split_to(at);
1237                    assert_len(&split);
1238                    assert_eq!(split.len(), at);
1239                    expected -= at;
1240                }
1241                5 => {
1242                    let at = cmp::min(n, expected);
1243                    pages.split_into(at, &mut other);
1244                    expected -= at;
1245                }
1246                6 => {
1247                    other.move_to(&mut pages);
1248                    assert_len(&other);
1249                    assert!(other.is_empty());
1250                    expected = pages.len();
1251                }
1252                7 => {
1253                    let mut copy = BytePages::new(BytePageSize::Size8);
1254                    pages.copy_to(&mut copy);
1255                    assert_len(&copy);
1256                    assert_eq!(copy.len(), expected);
1257                }
1258                _ => {
1259                    pages.put_u8(4);
1260                    expected += 1;
1261                }
1262            }
1263            assert_len(&pages);
1264            assert_len(&other);
1265            assert_eq!(pages.len(), expected);
1266        }
1267
1268        let len = pages.len();
1269        assert_eq!(pages.freeze().len(), len);
1270        assert_len(&pages);
1271        assert!(pages.is_empty());
1272        pages.extend_from_slice(b"123");
1273        pages.clear();
1274        assert_len(&pages);
1275        assert!(pages.is_empty());
1276    }
1277
1278    /// STATEMENT: A cached page-list allocation does not retain the previous buffer's tracked length.
1279    ///
1280    /// HOW:
1281    /// - Append two 64-byte buffers, drop the `BytePages`, then create another and verify empty length and page consistency.
1282    #[test]
1283    fn pages_len_after_cache_reuse() {
1284        let mut pages = BytePages::new(BytePageSize::Size8);
1285        pages.append(Bytes::copy_from_slice(&[1; 64]));
1286        pages.append(Bytes::copy_from_slice(&[1; 64]));
1287        drop(pages);
1288
1289        let pages = BytePages::new(BytePageSize::Size8);
1290        assert_len(&pages);
1291        assert!(pages.is_empty());
1292    }
1293
1294    /// STATEMENT: Copying the current page shares its data while allowing the source to append safely.
1295    ///
1296    /// HOW:
1297    /// - Copy 64 bytes with `copy_to`, take the copied page, and verify it shares the current page's pointer.
1298    /// - Append another 64 bytes to the source and drop it, checking the copied page stays unchanged and valid.
1299    #[test]
1300    fn pages_copy_to_shares_current() {
1301        let mut pages = BytePages::new(BytePageSize::Size8);
1302        pages.put_slice(&[1; 64]);
1303        // SAFETY: The current page owns a live allocation; the pointer is only recorded for
1304        // comparison.
1305        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1306
1307        let mut copy = BytePages::new(BytePageSize::Size8);
1308        pages.copy_to(&mut copy);
1309        let page = copy.take().unwrap();
1310        // SAFETY: The page is live and unmoved during this pointer comparison.
1311        assert_eq!(unsafe { page.as_ptr() }, ptr.cast_const());
1312        assert_eq!(page.as_ref(), &[1; 64][..]);
1313
1314        // the source keeps writing to the page it shares with the copy
1315        pages.put_slice(&[2; 64]);
1316        // SAFETY: The current page owns a live allocation; the pointer is only compared, not
1317        // dereferenced.
1318        assert_eq!(unsafe { pages.current.as_ref().unwrap().as_ptr() }, ptr);
1319        assert_eq!(page.as_ref(), &[1; 64][..]);
1320        assert_eq!(pages.len(), 128);
1321
1322        drop(pages);
1323        assert_eq!(page.as_ref(), &[1; 64][..]);
1324    }
1325
1326    /// STATEMENT: Splitting pages preserves writable spare capacity and transfers a whole current page without copying.
1327    ///
1328    /// HOW:
1329    /// - Split a 40-byte head from 64 bytes, append to the tail, and check lengths, contents, spare capacity, and pointer offsets.
1330    /// - Repeat with an inline head, a whole-page split, and an empty current page, checking ownership and retained capacity.
1331    #[test]
1332    fn pages_split_keeps_current_writable() {
1333        let mut pages = BytePages::new(BytePageSize::Size8);
1334        pages.put_slice(&[1; 64]);
1335        // SAFETY: The current page owns a live allocation; the pointer is only recorded for
1336        // comparison.
1337        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1338        let remaining = pages.spare();
1339
1340        let mut head = pages.split_to(40);
1341        assert_eq!(head.len(), 40);
1342        assert_eq!(pages.len(), 24);
1343        assert_eq!(pages.num_pages(), 1);
1344        assert_eq!(pages.spare(), remaining);
1345
1346        // new data goes into the same page
1347        pages.put_slice(&[2; 16]);
1348        assert_eq!(pages.num_pages(), 1);
1349        assert_eq!(
1350            // SAFETY: The current page owns a live allocation; the pointer is only compared, not
1351            // dereferenced.
1352            unsafe { pages.current.as_ref().unwrap().as_ptr() },
1353            ptr.wrapping_add(40)
1354        );
1355        let mut expected = vec![1; 24];
1356        expected.extend_from_slice(&[2; 16]);
1357        assert_eq!(&pages.freeze()[..], &expected[..]);
1358        assert_eq!(&head.freeze()[..], &[1; 40][..]);
1359
1360        // an inline head
1361        let mut pages = BytePages::new(BytePageSize::Size8);
1362        pages.put_slice(&[1; 64]);
1363        let mut to = BytePages::new(BytePageSize::Size8);
1364        pages.split_into(2, &mut to);
1365        assert_eq!(&to.freeze()[..], &[1; 2][..]);
1366        assert_eq!(pages.spare(), remaining);
1367        assert_eq!(pages.len(), 62);
1368
1369        // the whole current page moves with its spare capacity
1370        let mut pages = BytePages::new(BytePageSize::Size8);
1371        pages.put_slice(&[1; 64]);
1372        // SAFETY: The current page owns a live allocation; the pointer is only recorded for
1373        // comparison.
1374        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1375        let to = pages.split_to(64);
1376        assert!(pages.is_empty());
1377        assert_eq!(pages.num_pages(), 0);
1378        assert_eq!(to.spare(), remaining);
1379        // SAFETY: The destination owns the moved allocation; its pointer is only compared, not
1380        // dereferenced.
1381        assert_eq!(unsafe { to.current.as_ref().unwrap().as_ptr() }, ptr);
1382
1383        // an empty current page stays in place
1384        let mut pages = BytePages::new(BytePageSize::Size8);
1385        let _ = pages.chunk_mut();
1386        let head = pages.split_to(10);
1387        assert!(head.is_empty());
1388        assert_eq!(pages.spare(), remaining + 64);
1389    }
1390
1391    /// STATEMENT: `BytePages` mutable-buffer writes grow across page boundaries and account for all written bytes.
1392    ///
1393    /// HOW:
1394    /// - Fill a writable chunk, advance it, write into the next page, and check page counts, length, and remaining writable space.
1395    /// - Combine `put` and `Write::write_all`, then freeze and compare the complete payload.
1396    #[test]
1397    fn pages_buf_mut_grows() {
1398        let mut pages = BytePages::new(BytePageSize::Size8);
1399        assert!(pages.has_remaining_mut());
1400        assert_eq!(pages.remaining_mut(), usize::MAX);
1401        // SAFETY: Advancing by zero exposes no uninitialized bytes.
1402        unsafe { pages.advance_mut(0) };
1403
1404        // filling the current page through `chunk_mut` starts a new one
1405        let n = pages.chunk_mut().len();
1406        // SAFETY: The writable chunk has `n` bytes, all initialized by `write_bytes` before
1407        // advancing.
1408        unsafe {
1409            std::ptr::write_bytes(pages.chunk_mut().as_mut_ptr(), 1, n);
1410            pages.advance_mut(n);
1411        }
1412        assert!(pages.chunk_mut().len() > 0);
1413        // the new current page holds no data yet
1414        assert_eq!(pages.num_pages(), 1);
1415        // SAFETY: The nonempty writable chunk receives one initialized byte before advancing by
1416        // one.
1417        unsafe {
1418            *pages.chunk_mut().as_mut_ptr() = 2;
1419            pages.advance_mut(1);
1420        }
1421        assert_eq!(pages.len(), n + 1);
1422        assert_eq!(pages.remaining_mut(), usize::MAX - n - 1);
1423
1424        // `put` and `io::Write` are not limited to the current page
1425        let mut pages = BytePages::new(BytePageSize::Size8);
1426        pages.put(&[3u8; 200][..]);
1427        pages.put(Bytes::from_static(b"abcd"));
1428        io::Write::write_all(&mut pages, &[4; 100]).unwrap();
1429        let mut expected = vec![3; 200];
1430        expected.extend_from_slice(b"abcd");
1431        expected.extend_from_slice(&[4; 100]);
1432        assert_eq!(&pages.freeze()[..], &expected[..]);
1433    }
1434
1435    /// STATEMENT: Single-byte writes after a full current page allocate usable storage without adding empty pages.
1436    ///
1437    /// HOW:
1438    /// - Fill a current page through `advance_mut`, append signed and unsigned bytes, and check page counts and frozen data.
1439    /// - Repeat after appending a large separate payload, verifying all listed pages are nonempty and byte order is preserved.
1440    #[test]
1441    fn pages_byte_write_after_full_page() {
1442        // `advance_mut` leaves a full current page
1443        let mut pages = BytePages::new(BytePageSize::Size8);
1444        let n = pages.chunk_mut().len();
1445        // SAFETY: The writable chunk has `n` bytes, all initialized by `write_bytes` before
1446        // advancing.
1447        unsafe {
1448            std::ptr::write_bytes(pages.chunk_mut().as_mut_ptr(), 1, n);
1449            pages.advance_mut(n);
1450        }
1451        pages.put_u8(2);
1452        pages.put_i8(3);
1453        assert_eq!(pages.num_pages(), 2);
1454        assert_eq!(pages.pages()[0].len(), n);
1455        let mut expected = vec![1; n];
1456        expected.extend_from_slice(&[2, 3]);
1457        assert_eq!(&pages.freeze()[..], &expected[..]);
1458
1459        // splitting off a full current page leaves an empty page without capacity
1460        let mut pages = BytePages::new(BytePageSize::Size8);
1461        let n = pages.chunk_mut().len();
1462        // SAFETY: The writable chunk has `n` bytes, all initialized by `write_bytes` before
1463        // advancing.
1464        unsafe {
1465            std::ptr::write_bytes(pages.chunk_mut().as_mut_ptr(), 1, n);
1466            pages.advance_mut(n);
1467        }
1468        pages.append(vec![2; APPEND_COPY_LIMIT + 1]);
1469        pages.put_u8(3);
1470        assert!(pages.pages().iter().all(|p| !p.is_empty()));
1471        let mut expected = vec![1; n];
1472        expected.extend_from_slice(&[2; APPEND_COPY_LIMIT + 1]);
1473        expected.push(3);
1474        assert_eq!(&pages.freeze()[..], &expected[..]);
1475    }
1476
1477    /// STATEMENT: Mutable advancement cannot exceed the current page's writable chunk.
1478    ///
1479    /// HOW:
1480    /// - Get a `Size8` buffer's chunk length, advance by one more, and expect the current-page bounds panic.
1481    #[test]
1482    #[should_panic(expected = "cannot advance past the current page")]
1483    fn pages_advance_past_current_page() {
1484        let mut pages = BytePages::new(BytePageSize::Size8);
1485        let n = pages.chunk_mut().len();
1486        // SAFETY: The implementation checks the chunk bound and panics before exposing any
1487        // uninitialized bytes.
1488        unsafe { pages.advance_mut(n + 1) };
1489    }
1490
1491    /// STATEMENT: Creating `BytePages` with an unspecified page size is rejected.
1492    ///
1493    /// HOW:
1494    /// - Call `BytePages::new` with `BytePageSize::Unset` and expect the page-size panic.
1495    #[test]
1496    #[should_panic(expected = "Page size cannot be Unset")]
1497    fn pages_new_unset() {
1498        let _ = BytePages::new(BytePageSize::Unset);
1499    }
1500
1501    /// STATEMENT: An existing `BytePages` buffer cannot switch to an unspecified page size.
1502    ///
1503    /// HOW:
1504    /// - Create a `Size8` buffer, set its page size to `Unset`, and expect the page-size panic.
1505    #[test]
1506    #[should_panic(expected = "Page size cannot be Unset")]
1507    fn pages_set_page_size_unset() {
1508        let mut pages = BytePages::new(BytePageSize::Size8);
1509        pages.set_page_size(BytePageSize::Unset);
1510    }
1511
1512    /// STATEMENT: Caching a large page list does not retain its excessive list capacity.
1513    ///
1514    /// HOW:
1515    /// - Append 200 pages, check list capacity is at least 200, drop the buffer, and verify a new list has smaller capacity.
1516    #[test]
1517    fn cached_pages_list_is_shrunk() {
1518        let mut pages = BytePages::new(BytePageSize::Size4);
1519        for _ in 0..200 {
1520            pages.append(Bytes::from_static(b"page"));
1521        }
1522        assert!(pages.pages().capacity() >= 200);
1523        drop(pages);
1524
1525        let pages = BytePages::new(BytePageSize::Size4);
1526        assert!(
1527            pages.pages().capacity() < 200,
1528            "{}",
1529            pages.pages().capacity()
1530        );
1531    }
1532
1533    /// STATEMENT: Single-page conversion reuses unique storage but copies shared storage before making it mutable.
1534    ///
1535    /// HOW:
1536    /// - Convert current pages to `BytesMut` and `Bytes`, checking pointer reuse, contents, and writable spare capacity.
1537    /// - Convert unique and shared prepended `Bytes`, checking reuse only for the unique case and source isolation after mutation.
1538    #[test]
1539    fn pages_into_bytes_single_page() {
1540        // the current page
1541        let mut pages = BytePages::new(BytePageSize::Size8);
1542        pages.put_slice(&[1; 64]);
1543        // SAFETY: The current page owns a live allocation; the pointer is only recorded for
1544        // comparison.
1545        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1546        let mut buf = BytesMut::from(pages);
1547        assert_eq!(buf.as_ptr(), ptr.cast_const());
1548        assert_eq!(&buf[..], &[1; 64][..]);
1549        // the rest of the page is spare capacity
1550        assert_eq!(buf.capacity(), BytePageSize::Size8.capacity());
1551        buf.extend_from_slice(&[2; 64]);
1552        assert_eq!(buf.as_ptr(), ptr.cast_const());
1553
1554        let mut pages = BytePages::new(BytePageSize::Size8);
1555        pages.put_slice(&[1; 64]);
1556        // SAFETY: The current page owns a live allocation; the pointer is only recorded for
1557        // comparison.
1558        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1559        let b = Bytes::from(pages);
1560        assert_eq!(b.as_ptr(), ptr.cast_const());
1561        assert_eq!(&b[..], &[1; 64][..]);
1562
1563        // a `Bytes` page
1564        let src = Bytes::copy_from_slice(&[3; 64]);
1565        let ptr = src.as_ptr();
1566        let mut pages = BytePages::new(BytePageSize::Size8);
1567        pages.prepend(src);
1568        assert_eq!(BytesMut::from(pages).as_ptr(), ptr);
1569
1570        // a shared page is copied
1571        let src = Bytes::copy_from_slice(&[3; 64]);
1572        let mut pages = BytePages::new(BytePageSize::Size8);
1573        pages.prepend(&src);
1574        let mut buf = BytesMut::from(pages);
1575        assert_ne!(buf.as_ptr(), src.as_ptr());
1576        buf[0] = 4;
1577        assert_eq!(&src[..], &[3; 64][..]);
1578    }
1579
1580    /// STATEMENT: Converting multiple pages concatenates them in order and converting an empty buffer stays empty.
1581    ///
1582    /// HOW:
1583    /// - Prepend 64 ones and write 64 twos, convert separately to `BytesMut` and `Bytes`, and check each half.
1584    /// - Convert empty `Size8` buffers to both destination types and verify emptiness.
1585    #[test]
1586    fn pages_into_bytes_multiple_pages() {
1587        let mut pages = BytePages::new(BytePageSize::Size8);
1588        pages.prepend(Bytes::copy_from_slice(&[1; 64]));
1589        pages.put_slice(&[2; 64]);
1590        assert_eq!(pages.num_pages(), 2);
1591        let buf = BytesMut::from(pages);
1592        assert_eq!(&buf[..64], &[1; 64][..]);
1593        assert_eq!(&buf[64..], &[2; 64][..]);
1594
1595        let mut pages = BytePages::new(BytePageSize::Size8);
1596        pages.prepend(Bytes::copy_from_slice(&[1; 64]));
1597        pages.put_slice(&[2; 64]);
1598        let b = Bytes::from(pages);
1599        assert_eq!(&b[..64], &[1; 64][..]);
1600        assert_eq!(&b[64..], &[2; 64][..]);
1601
1602        assert!(BytesMut::from(BytePages::new(BytePageSize::Size8)).is_empty());
1603        assert!(Bytes::from(BytePages::new(BytePageSize::Size8)).is_empty());
1604    }
1605
1606    /// STATEMENT: Copied and cloned page buffers retain their original contents when the source grows.
1607    ///
1608    /// HOW:
1609    /// - Copy pages containing `123456`, freeze both, and compare their contents.
1610    /// - Repeat with `copy_to` and `clone`, append `7` only to the source, and verify each destination remains `123456`.
1611    #[test]
1612    fn pages_copy_to() {
1613        let mut pages = BytePages::default();
1614        let mut pages2 = BytePages::default();
1615        pages.put_slice(b"456");
1616        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1617        pages.copy_to(&mut pages2);
1618        let p = pages.freeze();
1619        assert_eq!(p, b"123456");
1620        let p2 = pages2.freeze();
1621        assert_eq!(p2, b"123456");
1622
1623        let mut pages = BytePages::default();
1624        let mut pages2 = BytePages::default();
1625        pages.put_slice(b"456");
1626        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1627        pages.copy_to(&mut pages2);
1628        pages.put_u8(b'7');
1629        let p = pages.freeze();
1630        assert_eq!(p, b"1234567");
1631        let p2 = pages2.freeze();
1632        assert_eq!(p2, b"123456");
1633
1634        let mut pages = BytePages::default();
1635        pages.put_slice(b"456");
1636        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1637        let mut pages2 = pages.clone();
1638        pages.put_u8(b'7');
1639        let p = pages.freeze();
1640        assert_eq!(p, b"1234567");
1641        let p2 = pages2.freeze();
1642        assert_eq!(p2, b"123456");
1643    }
1644
1645    /// STATEMENT: Page splitting, mutable access, borrowing, and byte conversion preserve the requested data ranges.
1646    ///
1647    /// HOW:
1648    /// - Split `123456` within and across pages with `split_to` and `split_into` and check both resulting buffers.
1649    /// - Write short and 65,536-byte payloads through `with_bytes_mut`, then check lengths, frozen bytes, and page borrowing/conversion.
1650    #[test]
1651    fn pages_methods() {
1652        // .split_to()
1653        let mut pages = BytePages::default();
1654        pages.put_slice(b"456");
1655        pages.prepend(BytePage::from(&Bytes::copy_from_slice(b"123")));
1656        let mut pages2 = pages.split_to(1);
1657        let p = pages.freeze();
1658        assert_eq!(p, b"23456");
1659        let p2 = pages2.freeze();
1660        assert_eq!(p2, b"1");
1661
1662        let mut pages = BytePages::default();
1663        pages.put_slice(b"456");
1664        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1665        let mut pages2 = pages.split_to(4);
1666        let p = pages.freeze();
1667        assert_eq!(p, b"56");
1668        let p2 = pages2.freeze();
1669        assert_eq!(p2, b"1234");
1670
1671        // .split_into()
1672        let mut pages = BytePages::default();
1673        pages.put_slice(b"456");
1674        pages.prepend(BytePage::from(crate::ByteString::from_static("123")));
1675        let mut pages2 = BytePages::default();
1676        pages.split_into(1, &mut pages2);
1677        let p = pages.freeze();
1678        assert_eq!(p, b"23456");
1679        let p2 = pages2.freeze();
1680        assert_eq!(p2, b"1");
1681
1682        // .with_bytes_mut()
1683        let mut pages = BytePages::default();
1684        #[allow(deprecated)]
1685        pages.with_bytes_mut(|buf| buf.extend_from_slice(b"123"));
1686        assert_eq!(pages.len(), 3);
1687        let p = pages.freeze();
1688        assert_eq!(p, b"123");
1689
1690        let data = b"0123456789abcdef".repeat(4096);
1691
1692        let mut pages = BytePages::default();
1693        #[allow(deprecated)]
1694        pages.with_bytes_mut(|buf| buf.extend_from_slice(&data));
1695        assert_eq!(pages.len(), 65_536);
1696        let p = pages.freeze();
1697        assert_eq!(p, data[..]);
1698
1699        // into bytes
1700        let page = BytePage::from(Bytes::copy_from_slice(b"123"));
1701        assert_eq!(page, b"123");
1702        assert_eq!(<BytePage as Borrow<[u8]>>::borrow(&page), b"123");
1703        let b = Bytes::from(page);
1704        assert_eq!(b, b"123");
1705    }
1706
1707    /// STATEMENT: A slice written into the current page stays there until it fills the page, which then moves to the page list.
1708    ///
1709    /// HOW:
1710    /// - Write slices that leave one spare byte in the first page and check that no page was pushed.
1711    /// - Write the last byte and check that the full page is pushed with no current page left.
1712    /// - Write a slice larger than the page and check the page count, length, and frozen bytes.
1713    #[test]
1714    fn put_slice_current_page() {
1715        let mut pages = BytePages::new(BytePageSize::Size4);
1716        pages.put_slice(b"abc");
1717        let spare = pages.spare();
1718        assert!(spare > 1);
1719        assert_eq!(pages.pages().len(), 0);
1720        assert_eq!(pages.num_pages(), 1);
1721
1722        let fill = vec![b'x'; spare - 1];
1723        pages.put_slice(&fill);
1724        assert_eq!(pages.spare(), 1);
1725        assert_eq!(pages.pages().len(), 0);
1726
1727        pages.put_slice(b"y");
1728        assert_eq!(pages.pages().len(), 1);
1729        assert!(pages.current.is_none());
1730        assert_eq!(pages.num_pages(), 1);
1731        let page_len = pages.len();
1732        assert_eq!(page_len, spare + 3);
1733
1734        let large = vec![b'z'; page_len + 10];
1735        pages.put_slice(&large);
1736        assert_eq!(pages.len(), page_len * 2 + 10);
1737        assert_eq!(pages.num_pages(), 3);
1738
1739        let mut expected = b"abc".to_vec();
1740        expected.extend_from_slice(&fill);
1741        expected.push(b'y');
1742        expected.extend_from_slice(&large);
1743        assert_eq!(pages.freeze(), expected[..]);
1744    }
1745
1746    /// STATEMENT: Cloned pages preserve contents while short storage is copied inline and longer mutable storage is shared.
1747    ///
1748    /// HOW:
1749    /// - Clone `Bytes`, short and long `BytesMut`, and vector-backed pages, checking equal contents and resulting storage variants.
1750    /// - Inspect mutable-storage uniqueness before and after cloning and dropping the longer clone.
1751    #[test]
1752    fn page_clone() {
1753        // Bytes storage
1754        let p = BytePage::from(Bytes::copy_from_slice(b"123"));
1755        let p2 = p.clone();
1756        assert_eq!(p, p2);
1757
1758        // StorageVec
1759        let mut p = BytePage::from(BytesMut::copy_from_slice(b"123"));
1760        if let StorageType::Storage(ref mut st) = p.inner {
1761            assert!(st.is_unique());
1762        } else {
1763            panic!()
1764        }
1765        let p2 = p.clone();
1766        assert_eq!(p, p2);
1767        // short data is copied into an inline view
1768        assert!(matches!(p2.inner, StorageType::Bytes(_)));
1769        if let StorageType::Storage(st) = p.inner {
1770            assert!(st.is_unique());
1771        } else {
1772            panic!()
1773        }
1774
1775        let mut p = BytePage::from(BytesMut::copy_from_slice([b'1'; 64]));
1776        let p2 = p.clone();
1777        assert_eq!(p, p2);
1778        assert!(matches!(p2.inner, StorageType::Bytes(_)));
1779        if let StorageType::Storage(ref mut st) = p.inner {
1780            assert!(!st.is_unique());
1781        } else {
1782            panic!()
1783        }
1784        drop(p2);
1785        if let StorageType::Storage(st) = p.inner {
1786            assert!(st.is_unique());
1787        } else {
1788            panic!()
1789        }
1790
1791        // Vec<u8> storage
1792        let p = BytePage::from(vec![b'1', b'2', b'3']);
1793        let p2 = p.clone();
1794        assert_eq!(p, p2);
1795        if let StorageType::Bytes(_) = p2.inner {
1796        } else {
1797            panic!()
1798        }
1799    }
1800
1801    /// STATEMENT: Splitting a page behaves consistently for immutable, mutable-storage, and vector-backed data.
1802    ///
1803    /// HOW:
1804    /// - Create `123` pages from each storage type, split one byte, and verify the returned `1` and retained `23`.
1805    #[test]
1806    fn page_split_to() {
1807        // Bytes storage
1808        let mut p = BytePage::from(Bytes::copy_from_slice(b"123"));
1809        let p2 = p.split_to(1);
1810        assert_eq!(p, b"23");
1811        assert_eq!(p2, b"1");
1812
1813        // StorageVec
1814        let mut p = BytePage::from(BytesMut::copy_from_slice(b"123"));
1815        let p2 = p.split_to(1);
1816        assert_eq!(p, b"23");
1817        assert_eq!(p2, b"1");
1818
1819        // Vec<u8> storage
1820        let mut p = BytePage::from(vec![b'1', b'2', b'3']);
1821        let p2 = p.split_to(1);
1822        assert_eq!(p, b"23");
1823        assert_eq!(p2, b"1");
1824    }
1825
1826    /// STATEMENT: Reading a page consumes its bytes and leaves unused destination bytes untouched.
1827    ///
1828    /// HOW:
1829    /// - Read `123` into a ten-byte zeroed array and verify a three-byte read, zero remaining length, and seven trailing zeros.
1830    #[test]
1831    fn page_read() {
1832        use std::io::Read;
1833
1834        let mut page = BytePage::from(Bytes::copy_from_slice(b"123"));
1835
1836        let mut buf = [0; 10];
1837        assert_eq!(page.read(&mut buf).unwrap(), 3);
1838        assert_eq!(page.len(), 0);
1839        assert_eq!(buf, [49, 50, 51, 0, 0, 0, 0, 0, 0, 0]);
1840    }
1841
1842    /// STATEMENT: Page-size updates, prepending, stream writes, and borrowed string appends preserve data.
1843    ///
1844    /// HOW:
1845    /// - Change `Size4` to `Size8`, reject an empty prepend, prepend `a`, write and flush `bc`, and freeze to `abc`.
1846    /// - Append a borrowed `ByteString` afterward and verify the next frozen value is `str`.
1847    #[test]
1848    fn pages_misc() {
1849        let mut pages = BytePages::new(BytePageSize::Size4);
1850        pages.set_page_size(BytePageSize::Size8);
1851        assert_eq!(pages.page_size(), BytePageSize::Size8);
1852        assert!(!pages.prepend(Bytes::new()));
1853        assert!(pages.prepend(Bytes::from_static(b"a")));
1854
1855        io::Write::write_all(&mut pages, b"bc").unwrap();
1856        io::Write::flush(&mut pages).unwrap();
1857        assert_eq!(pages.freeze(), "abc");
1858
1859        let s = ByteString::from_static("str");
1860        pages.append(&s);
1861        assert_eq!(pages.freeze(), "str");
1862    }
1863
1864    /// STATEMENT: Current-page transfer only fills an empty destination and leaves transferred storage writable.
1865    ///
1866    /// HOW:
1867    /// - Attempt to transfer `data` into a destination containing `x` and check both lengths stay unchanged.
1868    /// - Transfer into an empty buffer, check source and destination lengths, append `!`, and freeze to `data!`.
1869    #[test]
1870    fn pages_try_get_current_from() {
1871        let mut src = BytePages::new(BytePageSize::Size4);
1872        src.extend_from_slice(b"data");
1873
1874        // the target already holds data
1875        let mut dst = BytePages::new(BytePageSize::Size4);
1876        dst.append(Bytes::from_static(b"x"));
1877        dst.try_get_current_from(&mut src);
1878        assert_eq!(src.len(), 4);
1879        assert_eq!(dst.len(), 1);
1880
1881        let mut dst = BytePages::new(BytePageSize::Size4);
1882        dst.try_get_current_from(&mut src);
1883        assert_eq!(src.len(), 0);
1884        assert_eq!(dst.len(), 4);
1885        dst.extend_from_slice(b"!");
1886        assert_eq!(dst.freeze(), "data!");
1887    }
1888
1889    /// STATEMENT: The page-list cache enforces its maximum size and skips caching while already in use.
1890    ///
1891    /// HOW:
1892    /// - Drop `CACHE_SIZE + 1` buffers into an empty cache and verify exactly `CACHE_SIZE` lists are retained.
1893    /// - Take the cache while dropping another buffer, restore it, and verify no list was added.
1894    #[test]
1895    fn pages_drop_cache() {
1896        CACHE.with(|c| c.set(Some(Box::default())));
1897        let cached = || {
1898            CACHE.with(|c| {
1899                let cache = c.take().unwrap();
1900                let len = cache.len();
1901                c.set(Some(cache));
1902                len
1903            })
1904        };
1905
1906        // the cache is full
1907        let pages: Vec<_> = (0..=CACHE_SIZE)
1908            .map(|_| BytePages::new(BytePageSize::Size4))
1909            .collect();
1910        drop(pages);
1911        assert_eq!(cached(), CACHE_SIZE);
1912
1913        // the cache is in use
1914        CACHE.with(|c| c.set(Some(Box::default())));
1915        let pages = BytePages::new(BytePageSize::Size4);
1916        let cache = CACHE.with(Cell::take);
1917        drop(pages);
1918        CACHE.with(|c| c.set(cache));
1919        assert_eq!(cached(), 0);
1920    }
1921
1922    /// STATEMENT: Page conversions preserve payloads from mutable storage, vectors, and borrowed byte strings.
1923    ///
1924    /// HOW:
1925    /// - Check `Storage` and `Vec` page kinds, convert their 64-byte payloads to `Bytes` or `BytesMut`, and compare contents.
1926    /// - Convert a borrowed `ByteString` to a page and check `string` remains intact.
1927    #[test]
1928    fn page_conversions() {
1929        let page = BytePage::from(BytesMut::copy_from_slice([1; 64]));
1930        assert_eq!(page.info(), crate::info::PageKind::Storage);
1931        assert_eq!(Bytes::from(page), &[1; 64][..]);
1932
1933        let page = BytePage::from(vec![2; 64]);
1934        assert_eq!(page.info(), crate::info::PageKind::Vec);
1935        assert_eq!(Bytes::from(page), &[2; 64][..]);
1936
1937        let page = BytePage::from(vec![3; 64]);
1938        assert_eq!(BytesMut::from(page), &[3; 64][..]);
1939
1940        let s = ByteString::from_static("string");
1941        assert_eq!(BytePage::from(&s), "string");
1942    }
1943
1944    /// STATEMENT: Appending unique storage with spare capacity makes it writable current storage, unlike full or shared pages.
1945    ///
1946    /// HOW:
1947    /// - Append a mutable page containing `a`, write `b`, and check a single page freezes to `ab`.
1948    /// - Append full and shared pages followed by `z`, checking page count, total length, and unchanged shared contents.
1949    #[test]
1950    fn append_storage_page() {
1951        // a page with spare capacity becomes the current page
1952        let mut pages = BytePages::new(BytePageSize::Size4);
1953        let mut buf = BytesMut::with_capacity(64);
1954        buf.extend_from_slice(b"a");
1955        pages.append(buf);
1956        pages.extend_from_slice(b"b");
1957        assert_eq!(pages.num_pages(), 1);
1958        assert_eq!(pages.freeze(), "ab");
1959
1960        // full and shared pages are added to the page list
1961        let mut buf = BytesMut::with_capacity(64);
1962        let cap = buf.capacity();
1963        buf.resize(cap, b'x');
1964        pages.append(buf);
1965        let page = BytePage::from(BytesMut::copy_from_slice([b'y'; 64]));
1966        let shared = page.clone();
1967        pages.append(page);
1968        pages.extend_from_slice(b"z");
1969        assert_eq!(pages.num_pages(), 3);
1970        assert_eq!(pages.len(), cap + 65);
1971        assert_eq!(shared, &[b'y'; 64][..]);
1972    }
1973
1974    // inline page, shared page, then the current page
1975    fn mixed_pages() -> BytePages {
1976        let mut pages = BytePages::new(BytePageSize::Size4);
1977        pages.extend_from_slice(b"current");
1978        pages.prepend(Bytes::copy_from_slice(&[b'b'; 64]));
1979        pages.prepend(Bytes::copy_from_slice(b"ab"));
1980        assert!(pages.pages()[0].is_inline());
1981        pages
1982    }
1983
1984    /// STATEMENT: Page iteration preserves order and addresses across moves and omits empty current pages.
1985    ///
1986    /// HOW:
1987    /// - Check empty iteration, then collect inline, shared, and current chunks from `mixed_pages` and compare their order and count.
1988    /// - Move the buffer into a box and compare chunk addresses, then consume another buffer and verify empty iteration.
1989    #[test]
1990    fn iter_pages() {
1991        let pages = BytePages::default();
1992        assert_eq!(pages.iter().count(), 0);
1993
1994        let pages = mixed_pages();
1995        let chunks: Vec<&[u8]> = pages.iter().collect();
1996        assert_eq!(chunks, [&b"ab"[..], &[b'b'; 64][..], &b"current"[..]]);
1997        assert_eq!(chunks.len(), pages.num_pages());
1998
1999        // data, including inline pages, does not move with `self`
2000        let ptrs: Vec<*const u8> = pages.iter().map(<[u8]>::as_ptr).collect();
2001        let moved = Box::new(pages);
2002        let moved_ptrs: Vec<*const u8> = moved.iter().map(<[u8]>::as_ptr).collect();
2003        assert_eq!(ptrs, moved_ptrs);
2004
2005        // an empty current page is skipped
2006        let mut pages = BytePages::new(BytePageSize::Size4);
2007        pages.extend_from_slice(b"data");
2008        pages.prepend(Bytes::copy_from_slice(b"ab"));
2009        pages.advance_to(6);
2010        assert_eq!(pages.iter().count(), 0);
2011        assert_eq!(pages.num_pages(), 0);
2012    }
2013
2014    /// STATEMENT: Advancing across pages drops consumed data while retaining the current page's reusable spare capacity.
2015    ///
2016    /// HOW:
2017    /// - Advance by zero, whole pages, and partial pages in `mixed_pages`, checking lengths, page counts, and remaining chunks.
2018    /// - Consume the current page, write `next`, and verify its pointer continues after the consumed bytes.
2019    #[test]
2020    fn advance_to_pages() {
2021        let mut pages = mixed_pages();
2022        let len = pages.len();
2023        pages.advance_to(0);
2024        assert_eq!(pages.len(), len);
2025
2026        // whole page
2027        pages.advance_to(2);
2028        assert_eq!(pages.len(), len - 2);
2029        assert_eq!(pages.num_pages(), 2);
2030        assert_eq!(pages.iter().next().unwrap(), &[b'b'; 64][..]);
2031
2032        // part of a page
2033        pages.advance_to(60);
2034        assert_eq!(pages.len(), 11);
2035        assert_eq!(pages.num_pages(), 2);
2036        let chunks: Vec<&[u8]> = pages.iter().collect();
2037        assert_eq!(chunks, [&b"bbbb"[..], &b"current"[..]]);
2038
2039        // rest of the page and part of the current page
2040        pages.advance_to(6);
2041        assert_eq!(pages.len(), 5);
2042        assert_eq!(pages.num_pages(), 1);
2043        assert_eq!(pages.iter().next().unwrap(), b"rrent");
2044
2045        // the current page keeps its spare capacity
2046        let ptr = pages.iter().next().unwrap().as_ptr();
2047        pages.advance_to(5);
2048        assert!(pages.is_empty());
2049        assert_eq!(pages.num_pages(), 0);
2050        pages.extend_from_slice(b"next");
2051        assert_eq!(pages.num_pages(), 1);
2052        assert_eq!(pages.iter().next().unwrap().as_ptr(), ptr.wrapping_add(5));
2053        assert_eq!(pages.freeze(), "next");
2054    }
2055
2056    /// STATEMENT: Moving buffers retains page allocations, destination size settings, and page order without merging small pages.
2057    ///
2058    /// HOW:
2059    /// - Move `mixed_pages` into an empty `Size16` buffer and check source emptiness, destination size, lengths, and non-inline pointers.
2060    /// - Move into a populated buffer, compare all chunk boundaries, append again, and verify moving an empty source changes nothing.
2061    #[test]
2062    fn move_to_keeps_pages() {
2063        // into empty pages, the page list is swapped
2064        let mut src = mixed_pages();
2065        let ptrs: Vec<*const u8> = src
2066            .iter()
2067            .filter(|s| s.len() > 2)
2068            .map(<[u8]>::as_ptr)
2069            .collect();
2070        let mut dst = BytePages::new(BytePageSize::Size16);
2071        src.move_to(&mut dst);
2072        assert!(src.is_empty());
2073        assert_eq!(src.num_pages(), 0);
2074        assert_eq!(dst.page_size(), BytePageSize::Size16);
2075        assert_eq!(dst.num_pages(), 3);
2076        assert_eq!(dst.len(), 73);
2077        let moved: Vec<*const u8> = dst
2078            .iter()
2079            .filter(|s| s.len() > 2)
2080            .map(<[u8]>::as_ptr)
2081            .collect();
2082        assert_eq!(ptrs, moved);
2083
2084        // small pages are not merged, the filled part of the current page
2085        // of `dst` goes first and its spare capacity is kept
2086        let mut dst = BytePages::new(BytePageSize::Size4);
2087        dst.extend_from_slice(b"first");
2088        dst.prepend(Bytes::copy_from_slice(b"0"));
2089        let mut src = mixed_pages();
2090        src.move_to(&mut dst);
2091        let chunks: Vec<&[u8]> = dst.iter().collect();
2092        assert_eq!(
2093            chunks,
2094            [
2095                &b"0"[..],
2096                &b"first"[..],
2097                &b"ab"[..],
2098                &[b'b'; 64][..],
2099                &b"current"[..]
2100            ]
2101        );
2102        assert_eq!(dst.len(), 79);
2103        dst.extend_from_slice(b"last");
2104        assert_eq!(dst.num_pages(), 6);
2105
2106        // empty source
2107        let mut src = BytePages::default();
2108        src.move_to(&mut dst);
2109        assert_eq!(dst.num_pages(), 6);
2110    }
2111
2112    /// STATEMENT: Advancing a vector-backed page retains the correct suffix.
2113    ///
2114    /// HOW:
2115    /// - Prepend a vector containing `hello world`, advance six bytes, and verify length five and frozen `world`.
2116    #[test]
2117    fn advance_to_vec_page() {
2118        let mut pages = BytePages::default();
2119        pages.prepend(b"hello world".to_vec());
2120        pages.advance_to(6);
2121        assert_eq!(pages.len(), 5);
2122        assert_eq!(pages.freeze(), "world");
2123    }
2124
2125    /// STATEMENT: Advancing beyond total page-buffer length panics.
2126    ///
2127    /// HOW:
2128    /// - Build `mixed_pages`, advance by its length plus one, and expect the end-of-buffer bounds panic.
2129    #[test]
2130    #[should_panic(expected = "cannot advance past the end of the buffer")]
2131    fn advance_to_past_end() {
2132        let mut pages = mixed_pages();
2133        pages.advance_to(pages.len() + 1);
2134    }
2135}