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    fn put_slice(&mut self, mut src: &[u8]) {
565        while !src.is_empty() {
566            let amount = self.with_current(|st| st.put_slice_partial(src));
567
568            src = &src[amount..];
569        }
570    }
571
572    #[inline]
573    fn put_u8(&mut self, n: u8) {
574        self.with_current(|st| st.put_u8(n));
575    }
576
577    #[inline]
578    fn put_i8(&mut self, n: i8) {
579        self.put_u8(n as u8);
580    }
581}
582
583impl Clone for BytePages {
584    fn clone(&self) -> Self {
585        let size = self.page_size();
586        let mut pages = BytePages::new(size);
587        self.copy_to(&mut pages);
588        pages
589    }
590}
591
592impl io::Write for BytePages {
593    fn write(&mut self, src: &[u8]) -> Result<usize, io::Error> {
594        self.put_slice(src);
595        Ok(src.len())
596    }
597
598    fn flush(&mut self) -> Result<(), io::Error> {
599        Ok(())
600    }
601}
602
603impl From<BytePages> for Bytes {
604    /// A single page is converted without copying.
605    fn from(mut pages: BytePages) -> Bytes {
606        pages.freeze()
607    }
608}
609
610impl From<BytePages> for BytesMut {
611    /// A single page is converted without copying if nothing else refers to
612    /// its buffer.
613    fn from(mut pages: BytePages) -> BytesMut {
614        if pages.num_pages() == 1 {
615            return BytesMut::from(pages.take().unwrap());
616        }
617
618        let mut buf = BytesMut::with_capacity(pages.len());
619        while let Some(p) = pages.take() {
620            buf.extend_from_slice(&p);
621        }
622        buf
623    }
624}
625
626/// A contiguous chunk stored by [`BytePages`].
627pub struct BytePage {
628    inner: StorageType,
629}
630
631enum StorageType {
632    Bytes(Bytes),
633    Storage(StorageVec),
634    Vec(Vec<u8>),
635}
636
637impl BytePage {
638    #[inline]
639    /// Returns the number of bytes contained in this `BytePage`.
640    pub fn len(&self) -> usize {
641        self.as_ref().len()
642    }
643
644    #[inline]
645    /// Returns `true` if the page is empty.
646    pub fn is_empty(&self) -> bool {
647        self.len() == 0
648    }
649
650    #[inline]
651    /// Returns a raw pointer to the data.
652    ///
653    /// # Safety
654    ///
655    /// The returned pointer may only be dereferenced while the page is neither
656    /// moved, modified nor dropped, and only for [`len`](Self::len) bytes. An
657    /// inline page stores its data inside the `BytePage` itself, so moving the
658    /// page invalidates the pointer, see [`is_inline`](Self::is_inline).
659    pub unsafe fn as_ptr(&self) -> *const u8 {
660        unsafe {
661            match &self.inner {
662                StorageType::Bytes(b) => b.storage.as_ptr(),
663                StorageType::Storage(b) => b.as_ptr(),
664                StorageType::Vec(b) => b.as_ptr(),
665            }
666        }
667    }
668
669    #[inline]
670    #[doc(hidden)]
671    /// Returns the kind of storage backing this page.
672    pub fn info(&self) -> crate::info::PageKind {
673        match &self.inner {
674            StorageType::Bytes(_) => crate::info::PageKind::Bytes,
675            StorageType::Storage(_) => crate::info::PageKind::Storage,
676            StorageType::Vec(_) => crate::info::PageKind::Vec,
677        }
678    }
679
680    #[inline]
681    #[doc(hidden)]
682    /// Returns `true` if the data is stored inside the `BytePage` itself.
683    ///
684    /// Moving an inline page moves its data, pointers from `as_ptr()` do not
685    /// survive the move.
686    pub fn is_inline(&self) -> bool {
687        matches!(&self.inner, StorageType::Bytes(b) if b.is_inline())
688    }
689
690    /// Splits the buffer into two at the given index.
691    ///
692    /// Afterwards, `self` contains elements `[at, len)`, and the returned `BytePage`
693    /// contains elements `[0, at)`. If `at > len`, all data is moved.
694    ///
695    /// Unlike [`Bytes::split_to`] and [`BytesMut::split_to`](crate::BytesMut::split_to),
696    /// which panic when `at` is out of bounds, this clamps `at` to the length.
697    /// Check `len()` first if a short split is an error.
698    ///
699    /// Depending on the underlying storage, this operation might be `O(1)` or could
700    /// involve a memory copy.
701    #[must_use]
702    pub fn split_to(&mut self, at: usize) -> BytePage {
703        match &mut self.inner {
704            StorageType::Bytes(b) => {
705                let buf = b.split_to(cmp::min(at, b.len()));
706                BytePage {
707                    inner: StorageType::Bytes(buf),
708                }
709            }
710            StorageType::Storage(_) => {
711                let inner = mem::replace(&mut self.inner, StorageType::Bytes(Bytes::new()));
712                if let StorageType::Storage(st) = inner {
713                    self.inner = StorageType::Bytes(Bytes {
714                        storage: st.freeze(),
715                    });
716                    self.split_to(at)
717                } else {
718                    unreachable!()
719                }
720            }
721            StorageType::Vec(_) => {
722                let inner = mem::replace(&mut self.inner, StorageType::Bytes(Bytes::new()));
723                if let StorageType::Vec(b) = inner {
724                    self.inner = StorageType::Bytes(Bytes::copy_from_slice(&b));
725                    self.split_to(at)
726                } else {
727                    unreachable!()
728                }
729            }
730        }
731    }
732
733    /// Advance the internal cursor.
734    ///
735    /// Afterwards `self` contains elements `[cnt, len)`.
736    /// This is an `O(1)` operation, except for pages backed by a `Vec<u8>`,
737    /// whose remaining data is copied.
738    ///
739    /// # Panics
740    ///
741    /// Panics if `cnt > len`.
742    #[inline]
743    pub fn advance_to(&mut self, cnt: usize) {
744        match &mut self.inner {
745            StorageType::Bytes(b) => b.advance_to(cnt),
746            StorageType::Storage(b) => unsafe { b.set_start(cnt) },
747            StorageType::Vec(b) => {
748                assert!(
749                    cnt <= b.len(),
750                    "cannot advance past the end of the buffer, cnt:{cnt} len:{}",
751                    b.len()
752                );
753                self.inner = StorageType::Bytes(Bytes::copy_from_slice(&b[cnt..]));
754            }
755        }
756    }
757
758    /// Converts `self` into an immutable `Bytes`.
759    #[inline]
760    #[must_use]
761    pub fn freeze(self) -> Bytes {
762        match self.inner {
763            StorageType::Bytes(b) => b,
764            StorageType::Storage(st) => Bytes {
765                storage: st.freeze(),
766            },
767            StorageType::Vec(v) => Bytes::from(v),
768        }
769    }
770
771    fn into_storage(self) -> Result<StorageVec, Self> {
772        if let StorageType::Storage(st) = self.inner {
773            // SAFETY: Converting back to `StorageVec` requires uniqueness.
774            if !st.is_full() && st.is_unique() {
775                Ok(st)
776            } else {
777                Err(Self {
778                    inner: StorageType::Storage(st),
779                })
780            }
781        } else {
782            Err(self)
783        }
784    }
785}
786
787impl Clone for BytePage {
788    fn clone(&self) -> Self {
789        let inner = match &self.inner {
790            StorageType::Bytes(b) => StorageType::Bytes(b.clone()),
791            // The clone is an immutable view, `st` must stay the only
792            // handle that can modify the shared header and spare capacity
793            StorageType::Storage(st) => StorageType::Bytes(Bytes {
794                storage: st.shallow_freeze(),
795            }),
796            StorageType::Vec(b) => StorageType::Bytes(Bytes::copy_from_slice(b)),
797        };
798
799        Self { inner }
800    }
801}
802
803impl AsRef<[u8]> for BytePage {
804    #[inline]
805    fn as_ref(&self) -> &[u8] {
806        match &self.inner {
807            StorageType::Bytes(b) => b.as_ref(),
808            StorageType::Storage(b) => b.as_ref(),
809            StorageType::Vec(b) => b.as_ref(),
810        }
811    }
812}
813
814impl Borrow<[u8]> for BytePage {
815    #[inline]
816    fn borrow(&self) -> &[u8] {
817        self.as_ref()
818    }
819}
820
821impl From<Bytes> for BytePage {
822    fn from(buf: Bytes) -> Self {
823        BytePage {
824            inner: StorageType::Bytes(buf),
825        }
826    }
827}
828
829impl<'a> From<&'a Bytes> for BytePage {
830    fn from(buf: &'a Bytes) -> Self {
831        BytePage {
832            inner: StorageType::Bytes(buf.clone()),
833        }
834    }
835}
836
837impl From<BytesMut> for BytePage {
838    fn from(buf: BytesMut) -> Self {
839        BytePage {
840            inner: StorageType::Storage(buf.storage),
841        }
842    }
843}
844
845impl From<ByteString> for BytePage {
846    fn from(s: ByteString) -> Self {
847        s.into_bytes().into()
848    }
849}
850
851impl<'a> From<&'a ByteString> for BytePage {
852    fn from(s: &'a ByteString) -> Self {
853        s.clone().into_bytes().into()
854    }
855}
856
857impl From<StorageVec> for BytePage {
858    fn from(buf: StorageVec) -> Self {
859        BytePage {
860            inner: StorageType::Storage(buf),
861        }
862    }
863}
864
865impl From<Vec<u8>> for BytePage {
866    fn from(buf: Vec<u8>) -> Self {
867        BytePage {
868            inner: StorageType::Vec(buf),
869        }
870    }
871}
872
873impl From<&'static str> for BytePage {
874    fn from(buf: &'static str) -> Self {
875        Bytes::from_static(buf.as_bytes()).into()
876    }
877}
878
879impl From<&'static [u8]> for BytePage {
880    fn from(buf: &'static [u8]) -> Self {
881        Bytes::from_static(buf).into()
882    }
883}
884
885impl<const N: usize> From<&'static [u8; N]> for BytePage {
886    fn from(buf: &'static [u8; N]) -> Self {
887        Bytes::from_static(buf).into()
888    }
889}
890
891impl From<BytePage> for Bytes {
892    fn from(page: BytePage) -> Self {
893        match page.inner {
894            StorageType::Bytes(b) => b,
895            StorageType::Storage(storage) => BytesMut { storage }.freeze(),
896            StorageType::Vec(v) => Bytes::copy_from_slice(&v),
897        }
898    }
899}
900
901impl From<BytePage> for BytesMut {
902    fn from(page: BytePage) -> Self {
903        match page.inner {
904            StorageType::Bytes(b) => b.into(),
905            // clones of the page may still read the data
906            StorageType::Storage(storage) => {
907                if storage.is_unique() {
908                    BytesMut { storage }
909                } else {
910                    BytesMut::copy_from_slice(storage.as_ref())
911                }
912            }
913            StorageType::Vec(v) => BytesMut::copy_from_slice(&v),
914        }
915    }
916}
917
918impl PartialEq for BytePage {
919    fn eq(&self, other: &BytePage) -> bool {
920        self.as_ref() == other.as_ref()
921    }
922}
923
924impl_partial_eq!(BytePage);
925
926impl_read!(BytePage);
927
928impl ops::Deref for BytePage {
929    type Target = [u8];
930
931    #[inline]
932    fn deref(&self) -> &[u8] {
933        self.as_ref()
934    }
935}
936
937impl fmt::Debug for BytePage {
938    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
939        fmt::Debug::fmt(&crate::debug::BsDebug(self.as_ref()), fmt)
940    }
941}
942
943#[cfg(test)]
944mod tests {
945    use rand::Rng;
946
947    use super::*;
948
949    /// STATEMENT: `BytePage` compares by content across byte and string types and supports consuming reads.
950    ///
951    /// HOW:
952    /// - Compare a vector-backed `hello` page with borrowed and owned equivalents in both directions and reject `hell`.
953    /// - Read it into a three-byte array, checking `hel`, the `lo` remainder, short and exhausted reads, and final emptiness.
954    #[test]
955    #[allow(clippy::op_ref, clippy::cmp_owned)]
956    fn page_eq_and_read() {
957        use std::io::Read;
958
959        let mut page = BytePage::from(Vec::from(&b"hello"[..]));
960        assert_eq!(page, b"hello"[..]);
961        assert_eq!(page, *b"hello");
962        assert_eq!(page, b"hello");
963        assert_eq!(page, &b"hello"[..]);
964        assert_eq!(page, "hello");
965        assert_eq!(page, *"hello");
966        assert_eq!(page, b"hello".to_vec());
967        assert_eq!(page, String::from("hello"));
968        assert_eq!(b"hello"[..], page);
969        assert_eq!(*b"hello", page);
970        assert_eq!(b"hello", page);
971        assert_eq!(&b"hello"[..], page);
972        assert_eq!("hello", page);
973        assert_eq!(*"hello", page);
974        assert_eq!(b"hello".to_vec(), page);
975        assert_eq!(String::from("hello"), page);
976        assert_ne!(page, "hell");
977
978        let mut buf = [0u8; 3];
979        assert_eq!(page.read(&mut buf).unwrap(), 3);
980        assert_eq!(&buf, b"hel");
981        assert_eq!(page, "lo");
982        assert_eq!(page.read(&mut buf).unwrap(), 2);
983        assert_eq!(page.read(&mut buf).unwrap(), 0);
984        assert!(page.is_empty());
985    }
986
987    /// STATEMENT: Page metadata distinguishes bytes, mutable storage, vector storage, and inline bytes.
988    ///
989    /// HOW:
990    /// - Convert 64-byte `Bytes`, `BytesMut`, and vectors and check their `PageKind` values.
991    /// - Compare inline status for four-byte and 64-byte immutable buffers and a short mutable buffer.
992    #[test]
993    fn page_info() {
994        use crate::info::PageKind;
995
996        let p = BytePage::from(Bytes::copy_from_slice(&[1; 64]));
997        assert_eq!(p.info(), PageKind::Bytes);
998        let p = BytePage::from(BytesMut::copy_from_slice(&[1; 64][..]));
999        assert_eq!(p.info(), PageKind::Storage);
1000        let p = BytePage::from(vec![1; 64]);
1001        assert_eq!(p.info(), PageKind::Vec);
1002        assert!(!p.is_inline());
1003
1004        assert!(BytePage::from(Bytes::copy_from_slice(&[1; 4])).is_inline());
1005        assert!(!BytePage::from(Bytes::copy_from_slice(&[1; 64])).is_inline());
1006        assert!(!BytePage::from(BytesMut::copy_from_slice(&[1; 4][..])).is_inline());
1007    }
1008
1009    /// STATEMENT: Appending small data copies into the current page while large data retains its own allocation.
1010    ///
1011    /// HOW:
1012    /// - Append payloads at and above `APPEND_COPY_LIMIT` after a header and check page counts, spare capacity, and storage pointers.
1013    /// - Add a trailer, drain pages in order, compare concatenated contents, and verify an empty current page remains reusable.
1014    #[test]
1015    fn append_copies_small_and_splits_for_large() {
1016        let cap = BytePageSize::Size16.capacity();
1017        let mut pages = BytePages::new(BytePageSize::Size16);
1018        pages.extend_from_slice(b"head\r\n");
1019
1020        // small data is copied into the current page
1021        pages.append(Bytes::copy_from_slice(&[1; APPEND_COPY_LIMIT]));
1022        assert_eq!(pages.num_pages(), 1);
1023        assert_eq!(pages.current_len(), 6 + APPEND_COPY_LIMIT);
1024
1025        // large data is not copied, the filled part is split off in front
1026        let body = Bytes::copy_from_slice(&[2; APPEND_COPY_LIMIT + 1]);
1027        let body_ptr = body.as_ptr();
1028        pages.append(body.clone());
1029        assert_eq!(pages.num_pages(), 2);
1030        assert_eq!(pages.current_len(), 0);
1031        assert_eq!(pages.spare(), cap - 6 - APPEND_COPY_LIMIT);
1032
1033        // later writes use the spare capacity of the current page
1034        let page_ptr = pages.pages()[0].as_ref().as_ptr();
1035        pages.extend_from_slice(b"\r\n");
1036        assert_eq!(pages.num_pages(), 3);
1037        let tail_ptr = pages.current.as_ref().unwrap().as_ref().as_ptr();
1038        assert_eq!(tail_ptr, page_ptr.wrapping_add(6 + APPEND_COPY_LIMIT));
1039
1040        let mut expected = b"head\r\n".to_vec();
1041        expected.extend_from_slice(&[1; APPEND_COPY_LIMIT]);
1042        expected.extend_from_slice(&body);
1043        expected.extend_from_slice(b"\r\n");
1044        assert_eq!(pages.len(), expected.len());
1045
1046        let first = pages.take().unwrap();
1047        let second = pages.take().unwrap();
1048        assert_eq!(second.as_ref().as_ptr(), body_ptr);
1049        let third = pages.take().unwrap();
1050        assert!(pages.take().is_none());
1051        let data = [first.as_ref(), second.as_ref(), third.as_ref()].concat();
1052        assert_eq!(data, expected);
1053
1054        // draining the pages keeps an empty current page for later writes
1055        pages.extend_from_slice(b"x");
1056        pages.append(body);
1057        assert_eq!(pages.take().unwrap().as_ref(), b"x");
1058        assert_eq!(pages.take().unwrap().len(), APPEND_COPY_LIMIT + 1);
1059        assert!(pages.take().is_none());
1060        assert_eq!(pages.num_pages(), 0);
1061        assert_eq!(pages.spare(), cap - 1);
1062    }
1063
1064    /// STATEMENT: Pages track lengths, page boundaries, conversions, and debug output as data is added and removed.
1065    ///
1066    /// HOW:
1067    /// - Fill `Size8` buffers across page boundaries, append a full page, and check lengths, page counts, spare capacity, and drained contents.
1068    /// - Convert byte and string inputs to pages, advance and freeze them, then check debug ordering and clearing.
1069    #[test]
1070    fn pages() {
1071        let cap = BytePageSize::Size8.capacity();
1072        unsafe {
1073            // pages
1074            let mut pages = BytePages::new(BytePageSize::Size8);
1075            assert!(pages.is_empty());
1076            assert_eq!(pages.len(), 0);
1077            assert_eq!(pages.num_pages(), 0);
1078            pages.extend_from_slice(b"b");
1079            assert_eq!(pages.len(), 1);
1080            assert_eq!(pages.num_pages(), 1);
1081            pages.extend_from_slice("a".repeat(9 * 1024).as_bytes());
1082            assert_eq!(pages.len(), 9217);
1083            assert_eq!(pages.num_pages(), 2);
1084            assert!(!pages.is_empty());
1085
1086            let mut pgs = BytePages::new(BytePageSize::Size8);
1087            pgs.put_i8(b'a' as i8);
1088            let p = pgs.take().unwrap();
1089            assert_eq!(p.len(), 1);
1090            assert_eq!(p.as_ref(), b"a");
1091
1092            pgs.extend_from_slice("a".repeat(cap - 1).as_bytes());
1093            assert_eq!(pgs.num_pages(), 1);
1094            pgs.put_u8(b'a');
1095            assert_eq!(pgs.num_pages(), 1);
1096            assert!(pgs.current.is_none());
1097
1098            pgs.put_u8(b'a');
1099            assert_eq!(pgs.num_pages(), 2);
1100
1101            pgs.append(Bytes::copy_from_slice("a".repeat(cap).as_bytes()));
1102            assert_eq!(pgs.num_pages(), 3);
1103            assert_eq!(pgs.current_len(), 0);
1104            assert_eq!(pgs.spare(), cap - 1);
1105
1106            // page
1107            let p = pages.take().unwrap();
1108            assert_eq!(p.len(), cap);
1109            let p = pages.take().unwrap();
1110            assert_eq!(p.len(), 9217 - cap);
1111            assert!(!p.is_empty());
1112            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1113            assert_eq!(p.as_ref(), "a".repeat(9217 - cap).as_bytes());
1114            assert!(pages.take().is_none());
1115
1116            let p = BytePage::from(Bytes::copy_from_slice(b"123"));
1117            assert_eq!(p.len(), 3);
1118            assert!(!p.is_empty());
1119            assert_eq!(p.as_ref(), b"123");
1120            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1121
1122            let p = BytePage::from(&b"123"[..]);
1123            assert_eq!(p.len(), 3);
1124            assert!(!p.is_empty());
1125            assert_eq!(p.as_ref(), b"123");
1126            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1127
1128            let p = BytePage::from(b"123");
1129            assert_eq!(p.len(), 3);
1130            assert!(!p.is_empty());
1131            assert_eq!(p.as_ref(), b"123");
1132            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1133
1134            let p = BytePage::from("123");
1135            assert_eq!(p.len(), 3);
1136            assert!(!p.is_empty());
1137            assert_eq!(p.as_ref(), b"123");
1138            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1139            assert_eq!(p.freeze(), b"123");
1140
1141            let p = BytePage::from(vec![b'1', b'2', b'3']);
1142            assert_eq!(p.len(), 3);
1143            assert!(!p.is_empty());
1144            assert_eq!(p.as_ref(), b"123");
1145            assert_eq!(p.as_ref().as_ptr(), p.as_ptr());
1146            assert_eq!(p.freeze(), b"123");
1147
1148            let mut p = BytePage::from(vec![b'1', b'2', b'3']);
1149            p.advance_to(1);
1150            assert_eq!(p.len(), 2);
1151            assert!(!p.is_empty());
1152            assert_eq!(p.as_ref(), b"23");
1153
1154            // debug
1155            let mut pages = BytePages::new(BytePageSize::Size8);
1156            pages.extend_from_slice(b"b");
1157            assert_eq!(format!("{pages:?}"), "BytePages(b\"b\")");
1158            let p = pages.take().unwrap();
1159            assert_eq!(p.as_ref(), b"b");
1160
1161            let mut pages = BytePages::new(BytePageSize::Size8);
1162            pages.extend_from_slice(b"a");
1163            pages.append(Bytes::copy_from_slice(b"123"));
1164            pages.push_back(p);
1165            assert_eq!(format!("{pages:?}"), "BytePages(b\"b\", b\"a123\")");
1166
1167            assert_eq!(pages.len(), 5);
1168            pages.clear();
1169            assert_eq!(pages.len(), 0);
1170        }
1171    }
1172
1173    /// Checks the tracked length against the pages.
1174    fn assert_len(pages: &BytePages) {
1175        let len = pages.pages().iter().map(BytePage::len).sum::<usize>() + pages.current_len();
1176        assert_eq!(pages.len(), len);
1177        assert_eq!(pages.is_empty(), len == 0);
1178    }
1179
1180    /// STATEMENT: Tracked total length stays consistent with page contents across mixed buffer operations.
1181    ///
1182    /// HOW:
1183    /// - Randomly extend, append, prepend, take, split, move, copy, and write bytes while maintaining an expected length.
1184    /// - Check both buffers against their constituent page lengths after each operation, then verify freezing and clearing leave empty buffers.
1185    #[test]
1186    fn pages_len_tracking() {
1187        let mut rng = rand::rng();
1188        let mut pages = BytePages::new(BytePageSize::Size8);
1189        let mut other = BytePages::new(BytePageSize::Size8);
1190        let mut expected = 0;
1191
1192        let (iters, max) = if cfg!(miri) { (100, 256) } else { (2000, 12 * 1024) };
1193        for _ in 0..iters {
1194            let n = rng.random_range(0..max);
1195            match rng.random_range(0..9) {
1196                0 => {
1197                    pages.extend_from_slice(&vec![1; n]);
1198                    expected += n;
1199                }
1200                1 => {
1201                    pages.append(Bytes::copy_from_slice(&vec![2; n]));
1202                    expected += n;
1203                }
1204                2 => {
1205                    if pages.prepend(BytesMut::copy_from_slice(vec![3; n])) {
1206                        expected += n;
1207                    }
1208                }
1209                3 => {
1210                    if let Some(p) = pages.take() {
1211                        expected -= p.len();
1212                    }
1213                }
1214                4 => {
1215                    let at = cmp::min(n, expected);
1216                    let split = pages.split_to(at);
1217                    assert_len(&split);
1218                    assert_eq!(split.len(), at);
1219                    expected -= at;
1220                }
1221                5 => {
1222                    let at = cmp::min(n, expected);
1223                    pages.split_into(at, &mut other);
1224                    expected -= at;
1225                }
1226                6 => {
1227                    other.move_to(&mut pages);
1228                    assert_len(&other);
1229                    assert!(other.is_empty());
1230                    expected = pages.len();
1231                }
1232                7 => {
1233                    let mut copy = BytePages::new(BytePageSize::Size8);
1234                    pages.copy_to(&mut copy);
1235                    assert_len(&copy);
1236                    assert_eq!(copy.len(), expected);
1237                }
1238                _ => {
1239                    pages.put_u8(4);
1240                    expected += 1;
1241                }
1242            }
1243            assert_len(&pages);
1244            assert_len(&other);
1245            assert_eq!(pages.len(), expected);
1246        }
1247
1248        let len = pages.len();
1249        assert_eq!(pages.freeze().len(), len);
1250        assert_len(&pages);
1251        assert!(pages.is_empty());
1252        pages.extend_from_slice(b"123");
1253        pages.clear();
1254        assert_len(&pages);
1255        assert!(pages.is_empty());
1256    }
1257
1258    /// STATEMENT: A cached page-list allocation does not retain the previous buffer's tracked length.
1259    ///
1260    /// HOW:
1261    /// - Append two 64-byte buffers, drop the `BytePages`, then create another and verify empty length and page consistency.
1262    #[test]
1263    fn pages_len_after_cache_reuse() {
1264        let mut pages = BytePages::new(BytePageSize::Size8);
1265        pages.append(Bytes::copy_from_slice(&[1; 64]));
1266        pages.append(Bytes::copy_from_slice(&[1; 64]));
1267        drop(pages);
1268
1269        let pages = BytePages::new(BytePageSize::Size8);
1270        assert_len(&pages);
1271        assert!(pages.is_empty());
1272    }
1273
1274    /// STATEMENT: Copying the current page shares its data while allowing the source to append safely.
1275    ///
1276    /// HOW:
1277    /// - Copy 64 bytes with `copy_to`, take the copied page, and verify it shares the current page's pointer.
1278    /// - Append another 64 bytes to the source and drop it, checking the copied page stays unchanged and valid.
1279    #[test]
1280    fn pages_copy_to_shares_current() {
1281        let mut pages = BytePages::new(BytePageSize::Size8);
1282        pages.put_slice(&[1; 64]);
1283        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1284
1285        let mut copy = BytePages::new(BytePageSize::Size8);
1286        pages.copy_to(&mut copy);
1287        let page = copy.take().unwrap();
1288        assert_eq!(unsafe { page.as_ptr() }, ptr.cast_const());
1289        assert_eq!(page.as_ref(), &[1; 64][..]);
1290
1291        // the source keeps writing to the page it shares with the copy
1292        pages.put_slice(&[2; 64]);
1293        assert_eq!(unsafe { pages.current.as_ref().unwrap().as_ptr() }, ptr);
1294        assert_eq!(page.as_ref(), &[1; 64][..]);
1295        assert_eq!(pages.len(), 128);
1296
1297        drop(pages);
1298        assert_eq!(page.as_ref(), &[1; 64][..]);
1299    }
1300
1301    /// STATEMENT: Splitting pages preserves writable spare capacity and transfers a whole current page without copying.
1302    ///
1303    /// HOW:
1304    /// - Split a 40-byte head from 64 bytes, append to the tail, and check lengths, contents, spare capacity, and pointer offsets.
1305    /// - Repeat with an inline head, a whole-page split, and an empty current page, checking ownership and retained capacity.
1306    #[test]
1307    fn pages_split_keeps_current_writable() {
1308        let mut pages = BytePages::new(BytePageSize::Size8);
1309        pages.put_slice(&[1; 64]);
1310        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1311        let remaining = pages.spare();
1312
1313        let mut head = pages.split_to(40);
1314        assert_eq!(head.len(), 40);
1315        assert_eq!(pages.len(), 24);
1316        assert_eq!(pages.num_pages(), 1);
1317        assert_eq!(pages.spare(), remaining);
1318
1319        // new data goes into the same page
1320        pages.put_slice(&[2; 16]);
1321        assert_eq!(pages.num_pages(), 1);
1322        assert_eq!(
1323            unsafe { pages.current.as_ref().unwrap().as_ptr() },
1324            ptr.wrapping_add(40)
1325        );
1326        let mut expected = vec![1; 24];
1327        expected.extend_from_slice(&[2; 16]);
1328        assert_eq!(&pages.freeze()[..], &expected[..]);
1329        assert_eq!(&head.freeze()[..], &[1; 40][..]);
1330
1331        // an inline head
1332        let mut pages = BytePages::new(BytePageSize::Size8);
1333        pages.put_slice(&[1; 64]);
1334        let mut to = BytePages::new(BytePageSize::Size8);
1335        pages.split_into(2, &mut to);
1336        assert_eq!(&to.freeze()[..], &[1; 2][..]);
1337        assert_eq!(pages.spare(), remaining);
1338        assert_eq!(pages.len(), 62);
1339
1340        // the whole current page moves with its spare capacity
1341        let mut pages = BytePages::new(BytePageSize::Size8);
1342        pages.put_slice(&[1; 64]);
1343        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1344        let to = pages.split_to(64);
1345        assert!(pages.is_empty());
1346        assert_eq!(pages.num_pages(), 0);
1347        assert_eq!(to.spare(), remaining);
1348        assert_eq!(unsafe { to.current.as_ref().unwrap().as_ptr() }, ptr);
1349
1350        // an empty current page stays in place
1351        let mut pages = BytePages::new(BytePageSize::Size8);
1352        let _ = pages.chunk_mut();
1353        let head = pages.split_to(10);
1354        assert!(head.is_empty());
1355        assert_eq!(pages.spare(), remaining + 64);
1356    }
1357
1358    /// STATEMENT: `BytePages` mutable-buffer writes grow across page boundaries and account for all written bytes.
1359    ///
1360    /// HOW:
1361    /// - Fill a writable chunk, advance it, write into the next page, and check page counts, length, and remaining writable space.
1362    /// - Combine `put` and `Write::write_all`, then freeze and compare the complete payload.
1363    #[test]
1364    fn pages_buf_mut_grows() {
1365        let mut pages = BytePages::new(BytePageSize::Size8);
1366        assert!(pages.has_remaining_mut());
1367        assert_eq!(pages.remaining_mut(), usize::MAX);
1368        unsafe { pages.advance_mut(0) };
1369
1370        // filling the current page through `chunk_mut` starts a new one
1371        let n = pages.chunk_mut().len();
1372        unsafe {
1373            std::ptr::write_bytes(pages.chunk_mut().as_mut_ptr(), 1, n);
1374            pages.advance_mut(n);
1375        }
1376        assert!(pages.chunk_mut().len() > 0);
1377        // the new current page holds no data yet
1378        assert_eq!(pages.num_pages(), 1);
1379        unsafe {
1380            *pages.chunk_mut().as_mut_ptr() = 2;
1381            pages.advance_mut(1);
1382        }
1383        assert_eq!(pages.len(), n + 1);
1384        assert_eq!(pages.remaining_mut(), usize::MAX - n - 1);
1385
1386        // `put` and `io::Write` are not limited to the current page
1387        let mut pages = BytePages::new(BytePageSize::Size8);
1388        pages.put(&[3u8; 200][..]);
1389        pages.put(Bytes::from_static(b"abcd"));
1390        io::Write::write_all(&mut pages, &[4; 100]).unwrap();
1391        let mut expected = vec![3; 200];
1392        expected.extend_from_slice(b"abcd");
1393        expected.extend_from_slice(&[4; 100]);
1394        assert_eq!(&pages.freeze()[..], &expected[..]);
1395    }
1396
1397    /// STATEMENT: Single-byte writes after a full current page allocate usable storage without adding empty pages.
1398    ///
1399    /// HOW:
1400    /// - Fill a current page through `advance_mut`, append signed and unsigned bytes, and check page counts and frozen data.
1401    /// - Repeat after appending a large separate payload, verifying all listed pages are nonempty and byte order is preserved.
1402    #[test]
1403    fn pages_byte_write_after_full_page() {
1404        // `advance_mut` leaves a full current page
1405        let mut pages = BytePages::new(BytePageSize::Size8);
1406        let n = pages.chunk_mut().len();
1407        unsafe {
1408            std::ptr::write_bytes(pages.chunk_mut().as_mut_ptr(), 1, n);
1409            pages.advance_mut(n);
1410        }
1411        pages.put_u8(2);
1412        pages.put_i8(3);
1413        assert_eq!(pages.num_pages(), 2);
1414        assert_eq!(pages.pages()[0].len(), n);
1415        let mut expected = vec![1; n];
1416        expected.extend_from_slice(&[2, 3]);
1417        assert_eq!(&pages.freeze()[..], &expected[..]);
1418
1419        // splitting off a full current page leaves an empty page without capacity
1420        let mut pages = BytePages::new(BytePageSize::Size8);
1421        let n = pages.chunk_mut().len();
1422        unsafe {
1423            std::ptr::write_bytes(pages.chunk_mut().as_mut_ptr(), 1, n);
1424            pages.advance_mut(n);
1425        }
1426        pages.append(vec![2; APPEND_COPY_LIMIT + 1]);
1427        pages.put_u8(3);
1428        assert!(pages.pages().iter().all(|p| !p.is_empty()));
1429        let mut expected = vec![1; n];
1430        expected.extend_from_slice(&[2; APPEND_COPY_LIMIT + 1]);
1431        expected.push(3);
1432        assert_eq!(&pages.freeze()[..], &expected[..]);
1433    }
1434
1435    /// STATEMENT: Mutable advancement cannot exceed the current page's writable chunk.
1436    ///
1437    /// HOW:
1438    /// - Get a `Size8` buffer's chunk length, advance by one more, and expect the current-page bounds panic.
1439    #[test]
1440    #[should_panic(expected = "cannot advance past the current page")]
1441    fn pages_advance_past_current_page() {
1442        let mut pages = BytePages::new(BytePageSize::Size8);
1443        let n = pages.chunk_mut().len();
1444        unsafe { pages.advance_mut(n + 1) };
1445    }
1446
1447    /// STATEMENT: Creating `BytePages` with an unspecified page size is rejected.
1448    ///
1449    /// HOW:
1450    /// - Call `BytePages::new` with `BytePageSize::Unset` and expect the page-size panic.
1451    #[test]
1452    #[should_panic(expected = "Page size cannot be Unset")]
1453    fn pages_new_unset() {
1454        let _ = BytePages::new(BytePageSize::Unset);
1455    }
1456
1457    /// STATEMENT: An existing `BytePages` buffer cannot switch to an unspecified page size.
1458    ///
1459    /// HOW:
1460    /// - Create a `Size8` buffer, set its page size to `Unset`, and expect the page-size panic.
1461    #[test]
1462    #[should_panic(expected = "Page size cannot be Unset")]
1463    fn pages_set_page_size_unset() {
1464        let mut pages = BytePages::new(BytePageSize::Size8);
1465        pages.set_page_size(BytePageSize::Unset);
1466    }
1467
1468    /// STATEMENT: Caching a large page list does not retain its excessive list capacity.
1469    ///
1470    /// HOW:
1471    /// - Append 200 pages, check list capacity is at least 200, drop the buffer, and verify a new list has smaller capacity.
1472    #[test]
1473    fn cached_pages_list_is_shrunk() {
1474        let mut pages = BytePages::new(BytePageSize::Size4);
1475        for _ in 0..200 {
1476            pages.append(Bytes::from_static(b"page"));
1477        }
1478        assert!(pages.pages().capacity() >= 200);
1479        drop(pages);
1480
1481        let pages = BytePages::new(BytePageSize::Size4);
1482        assert!(
1483            pages.pages().capacity() < 200,
1484            "{}",
1485            pages.pages().capacity()
1486        );
1487    }
1488
1489    /// STATEMENT: Single-page conversion reuses unique storage but copies shared storage before making it mutable.
1490    ///
1491    /// HOW:
1492    /// - Convert current pages to `BytesMut` and `Bytes`, checking pointer reuse, contents, and writable spare capacity.
1493    /// - Convert unique and shared prepended `Bytes`, checking reuse only for the unique case and source isolation after mutation.
1494    #[test]
1495    fn pages_into_bytes_single_page() {
1496        // the current page
1497        let mut pages = BytePages::new(BytePageSize::Size8);
1498        pages.put_slice(&[1; 64]);
1499        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1500        let mut buf = BytesMut::from(pages);
1501        assert_eq!(buf.as_ptr(), ptr.cast_const());
1502        assert_eq!(&buf[..], &[1; 64][..]);
1503        // the rest of the page is spare capacity
1504        assert_eq!(buf.capacity(), BytePageSize::Size8.capacity());
1505        buf.extend_from_slice(&[2; 64]);
1506        assert_eq!(buf.as_ptr(), ptr.cast_const());
1507
1508        let mut pages = BytePages::new(BytePageSize::Size8);
1509        pages.put_slice(&[1; 64]);
1510        let ptr = unsafe { pages.current.as_ref().unwrap().as_ptr() };
1511        let b = Bytes::from(pages);
1512        assert_eq!(b.as_ptr(), ptr.cast_const());
1513        assert_eq!(&b[..], &[1; 64][..]);
1514
1515        // a `Bytes` page
1516        let src = Bytes::copy_from_slice(&[3; 64]);
1517        let ptr = src.as_ptr();
1518        let mut pages = BytePages::new(BytePageSize::Size8);
1519        pages.prepend(src);
1520        assert_eq!(BytesMut::from(pages).as_ptr(), ptr);
1521
1522        // a shared page is copied
1523        let src = Bytes::copy_from_slice(&[3; 64]);
1524        let mut pages = BytePages::new(BytePageSize::Size8);
1525        pages.prepend(&src);
1526        let mut buf = BytesMut::from(pages);
1527        assert_ne!(buf.as_ptr(), src.as_ptr());
1528        buf[0] = 4;
1529        assert_eq!(&src[..], &[3; 64][..]);
1530    }
1531
1532    /// STATEMENT: Converting multiple pages concatenates them in order and converting an empty buffer stays empty.
1533    ///
1534    /// HOW:
1535    /// - Prepend 64 ones and write 64 twos, convert separately to `BytesMut` and `Bytes`, and check each half.
1536    /// - Convert empty `Size8` buffers to both destination types and verify emptiness.
1537    #[test]
1538    fn pages_into_bytes_multiple_pages() {
1539        let mut pages = BytePages::new(BytePageSize::Size8);
1540        pages.prepend(Bytes::copy_from_slice(&[1; 64]));
1541        pages.put_slice(&[2; 64]);
1542        assert_eq!(pages.num_pages(), 2);
1543        let buf = BytesMut::from(pages);
1544        assert_eq!(&buf[..64], &[1; 64][..]);
1545        assert_eq!(&buf[64..], &[2; 64][..]);
1546
1547        let mut pages = BytePages::new(BytePageSize::Size8);
1548        pages.prepend(Bytes::copy_from_slice(&[1; 64]));
1549        pages.put_slice(&[2; 64]);
1550        let b = Bytes::from(pages);
1551        assert_eq!(&b[..64], &[1; 64][..]);
1552        assert_eq!(&b[64..], &[2; 64][..]);
1553
1554        assert!(BytesMut::from(BytePages::new(BytePageSize::Size8)).is_empty());
1555        assert!(Bytes::from(BytePages::new(BytePageSize::Size8)).is_empty());
1556    }
1557
1558    /// STATEMENT: Copied and cloned page buffers retain their original contents when the source grows.
1559    ///
1560    /// HOW:
1561    /// - Copy pages containing `123456`, freeze both, and compare their contents.
1562    /// - Repeat with `copy_to` and `clone`, append `7` only to the source, and verify each destination remains `123456`.
1563    #[test]
1564    fn pages_copy_to() {
1565        let mut pages = BytePages::default();
1566        let mut pages2 = BytePages::default();
1567        pages.put_slice(b"456");
1568        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1569        pages.copy_to(&mut pages2);
1570        let p = pages.freeze();
1571        assert_eq!(p, b"123456");
1572        let p2 = pages2.freeze();
1573        assert_eq!(p2, b"123456");
1574
1575        let mut pages = BytePages::default();
1576        let mut pages2 = BytePages::default();
1577        pages.put_slice(b"456");
1578        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1579        pages.copy_to(&mut pages2);
1580        pages.put_u8(b'7');
1581        let p = pages.freeze();
1582        assert_eq!(p, b"1234567");
1583        let p2 = pages2.freeze();
1584        assert_eq!(p2, b"123456");
1585
1586        let mut pages = BytePages::default();
1587        pages.put_slice(b"456");
1588        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1589        let mut pages2 = pages.clone();
1590        pages.put_u8(b'7');
1591        let p = pages.freeze();
1592        assert_eq!(p, b"1234567");
1593        let p2 = pages2.freeze();
1594        assert_eq!(p2, b"123456");
1595    }
1596
1597    /// STATEMENT: Page splitting, mutable access, borrowing, and byte conversion preserve the requested data ranges.
1598    ///
1599    /// HOW:
1600    /// - Split `123456` within and across pages with `split_to` and `split_into` and check both resulting buffers.
1601    /// - Write short and 65,536-byte payloads through `with_bytes_mut`, then check lengths, frozen bytes, and page borrowing/conversion.
1602    #[test]
1603    fn pages_methods() {
1604        // .split_to()
1605        let mut pages = BytePages::default();
1606        pages.put_slice(b"456");
1607        pages.prepend(BytePage::from(&Bytes::copy_from_slice(b"123")));
1608        let mut pages2 = pages.split_to(1);
1609        let p = pages.freeze();
1610        assert_eq!(p, b"23456");
1611        let p2 = pages2.freeze();
1612        assert_eq!(p2, b"1");
1613
1614        let mut pages = BytePages::default();
1615        pages.put_slice(b"456");
1616        pages.prepend(BytePage::from(Bytes::copy_from_slice(b"123")));
1617        let mut pages2 = pages.split_to(4);
1618        let p = pages.freeze();
1619        assert_eq!(p, b"56");
1620        let p2 = pages2.freeze();
1621        assert_eq!(p2, b"1234");
1622
1623        // .split_into()
1624        let mut pages = BytePages::default();
1625        pages.put_slice(b"456");
1626        pages.prepend(BytePage::from(crate::ByteString::from_static("123")));
1627        let mut pages2 = BytePages::default();
1628        pages.split_into(1, &mut pages2);
1629        let p = pages.freeze();
1630        assert_eq!(p, b"23456");
1631        let p2 = pages2.freeze();
1632        assert_eq!(p2, b"1");
1633
1634        // .with_bytes_mut()
1635        let mut pages = BytePages::default();
1636        #[allow(deprecated)]
1637        pages.with_bytes_mut(|buf| buf.extend_from_slice(b"123"));
1638        assert_eq!(pages.len(), 3);
1639        let p = pages.freeze();
1640        assert_eq!(p, b"123");
1641
1642        let data = b"0123456789abcdef".repeat(4096);
1643
1644        let mut pages = BytePages::default();
1645        #[allow(deprecated)]
1646        pages.with_bytes_mut(|buf| buf.extend_from_slice(&data));
1647        assert_eq!(pages.len(), 65_536);
1648        let p = pages.freeze();
1649        assert_eq!(p, data[..]);
1650
1651        // into bytes
1652        let page = BytePage::from(Bytes::copy_from_slice(b"123"));
1653        assert_eq!(page, b"123");
1654        assert_eq!(<BytePage as Borrow<[u8]>>::borrow(&page), b"123");
1655        let b = Bytes::from(page);
1656        assert_eq!(b, b"123");
1657    }
1658
1659    /// STATEMENT: Cloned pages preserve contents while short storage is copied inline and longer mutable storage is shared.
1660    ///
1661    /// HOW:
1662    /// - Clone `Bytes`, short and long `BytesMut`, and vector-backed pages, checking equal contents and resulting storage variants.
1663    /// - Inspect mutable-storage uniqueness before and after cloning and dropping the longer clone.
1664    #[test]
1665    fn page_clone() {
1666        // Bytes storage
1667        let p = BytePage::from(Bytes::copy_from_slice(b"123"));
1668        let p2 = p.clone();
1669        assert_eq!(p, p2);
1670
1671        // StorageVec
1672        let mut p = BytePage::from(BytesMut::copy_from_slice(b"123"));
1673        if let StorageType::Storage(ref mut st) = p.inner {
1674            assert!(st.is_unique());
1675        } else {
1676            panic!()
1677        }
1678        let p2 = p.clone();
1679        assert_eq!(p, p2);
1680        // short data is copied into an inline view
1681        assert!(matches!(p2.inner, StorageType::Bytes(_)));
1682        if let StorageType::Storage(st) = p.inner {
1683            assert!(st.is_unique());
1684        } else {
1685            panic!()
1686        }
1687
1688        let mut p = BytePage::from(BytesMut::copy_from_slice([b'1'; 64]));
1689        let p2 = p.clone();
1690        assert_eq!(p, p2);
1691        assert!(matches!(p2.inner, StorageType::Bytes(_)));
1692        if let StorageType::Storage(ref mut st) = p.inner {
1693            assert!(!st.is_unique());
1694        } else {
1695            panic!()
1696        }
1697        drop(p2);
1698        if let StorageType::Storage(st) = p.inner {
1699            assert!(st.is_unique());
1700        } else {
1701            panic!()
1702        }
1703
1704        // Vec<u8> storage
1705        let p = BytePage::from(vec![b'1', b'2', b'3']);
1706        let p2 = p.clone();
1707        assert_eq!(p, p2);
1708        if let StorageType::Bytes(_) = p2.inner {
1709        } else {
1710            panic!()
1711        }
1712    }
1713
1714    /// STATEMENT: Splitting a page behaves consistently for immutable, mutable-storage, and vector-backed data.
1715    ///
1716    /// HOW:
1717    /// - Create `123` pages from each storage type, split one byte, and verify the returned `1` and retained `23`.
1718    #[test]
1719    fn page_split_to() {
1720        // Bytes storage
1721        let mut p = BytePage::from(Bytes::copy_from_slice(b"123"));
1722        let p2 = p.split_to(1);
1723        assert_eq!(p, b"23");
1724        assert_eq!(p2, b"1");
1725
1726        // StorageVec
1727        let mut p = BytePage::from(BytesMut::copy_from_slice(b"123"));
1728        let p2 = p.split_to(1);
1729        assert_eq!(p, b"23");
1730        assert_eq!(p2, b"1");
1731
1732        // Vec<u8> storage
1733        let mut p = BytePage::from(vec![b'1', b'2', b'3']);
1734        let p2 = p.split_to(1);
1735        assert_eq!(p, b"23");
1736        assert_eq!(p2, b"1");
1737    }
1738
1739    /// STATEMENT: Reading a page consumes its bytes and leaves unused destination bytes untouched.
1740    ///
1741    /// HOW:
1742    /// - Read `123` into a ten-byte zeroed array and verify a three-byte read, zero remaining length, and seven trailing zeros.
1743    #[test]
1744    fn page_read() {
1745        use std::io::Read;
1746
1747        let mut page = BytePage::from(Bytes::copy_from_slice(b"123"));
1748
1749        let mut buf = [0; 10];
1750        assert_eq!(page.read(&mut buf).unwrap(), 3);
1751        assert_eq!(page.len(), 0);
1752        assert_eq!(buf, [49, 50, 51, 0, 0, 0, 0, 0, 0, 0]);
1753    }
1754
1755    /// STATEMENT: Page-size updates, prepending, stream writes, and borrowed string appends preserve data.
1756    ///
1757    /// HOW:
1758    /// - Change `Size4` to `Size8`, reject an empty prepend, prepend `a`, write and flush `bc`, and freeze to `abc`.
1759    /// - Append a borrowed `ByteString` afterward and verify the next frozen value is `str`.
1760    #[test]
1761    fn pages_misc() {
1762        let mut pages = BytePages::new(BytePageSize::Size4);
1763        pages.set_page_size(BytePageSize::Size8);
1764        assert_eq!(pages.page_size(), BytePageSize::Size8);
1765        assert!(!pages.prepend(Bytes::new()));
1766        assert!(pages.prepend(Bytes::from_static(b"a")));
1767
1768        io::Write::write_all(&mut pages, b"bc").unwrap();
1769        io::Write::flush(&mut pages).unwrap();
1770        assert_eq!(pages.freeze(), "abc");
1771
1772        let s = ByteString::from_static("str");
1773        pages.append(&s);
1774        assert_eq!(pages.freeze(), "str");
1775    }
1776
1777    /// STATEMENT: Current-page transfer only fills an empty destination and leaves transferred storage writable.
1778    ///
1779    /// HOW:
1780    /// - Attempt to transfer `data` into a destination containing `x` and check both lengths stay unchanged.
1781    /// - Transfer into an empty buffer, check source and destination lengths, append `!`, and freeze to `data!`.
1782    #[test]
1783    fn pages_try_get_current_from() {
1784        let mut src = BytePages::new(BytePageSize::Size4);
1785        src.extend_from_slice(b"data");
1786
1787        // the target already holds data
1788        let mut dst = BytePages::new(BytePageSize::Size4);
1789        dst.append(Bytes::from_static(b"x"));
1790        dst.try_get_current_from(&mut src);
1791        assert_eq!(src.len(), 4);
1792        assert_eq!(dst.len(), 1);
1793
1794        let mut dst = BytePages::new(BytePageSize::Size4);
1795        dst.try_get_current_from(&mut src);
1796        assert_eq!(src.len(), 0);
1797        assert_eq!(dst.len(), 4);
1798        dst.extend_from_slice(b"!");
1799        assert_eq!(dst.freeze(), "data!");
1800    }
1801
1802    /// STATEMENT: The page-list cache enforces its maximum size and skips caching while already in use.
1803    ///
1804    /// HOW:
1805    /// - Drop `CACHE_SIZE + 1` buffers into an empty cache and verify exactly `CACHE_SIZE` lists are retained.
1806    /// - Take the cache while dropping another buffer, restore it, and verify no list was added.
1807    #[test]
1808    fn pages_drop_cache() {
1809        CACHE.with(|c| c.set(Some(Box::default())));
1810        let cached = || {
1811            CACHE.with(|c| {
1812                let cache = c.take().unwrap();
1813                let len = cache.len();
1814                c.set(Some(cache));
1815                len
1816            })
1817        };
1818
1819        // the cache is full
1820        let pages: Vec<_> = (0..=CACHE_SIZE)
1821            .map(|_| BytePages::new(BytePageSize::Size4))
1822            .collect();
1823        drop(pages);
1824        assert_eq!(cached(), CACHE_SIZE);
1825
1826        // the cache is in use
1827        CACHE.with(|c| c.set(Some(Box::default())));
1828        let pages = BytePages::new(BytePageSize::Size4);
1829        let cache = CACHE.with(Cell::take);
1830        drop(pages);
1831        CACHE.with(|c| c.set(cache));
1832        assert_eq!(cached(), 0);
1833    }
1834
1835    /// STATEMENT: Page conversions preserve payloads from mutable storage, vectors, and borrowed byte strings.
1836    ///
1837    /// HOW:
1838    /// - Check `Storage` and `Vec` page kinds, convert their 64-byte payloads to `Bytes` or `BytesMut`, and compare contents.
1839    /// - Convert a borrowed `ByteString` to a page and check `string` remains intact.
1840    #[test]
1841    fn page_conversions() {
1842        let page = BytePage::from(BytesMut::copy_from_slice([1; 64]));
1843        assert_eq!(page.info(), crate::info::PageKind::Storage);
1844        assert_eq!(Bytes::from(page), &[1; 64][..]);
1845
1846        let page = BytePage::from(vec![2; 64]);
1847        assert_eq!(page.info(), crate::info::PageKind::Vec);
1848        assert_eq!(Bytes::from(page), &[2; 64][..]);
1849
1850        let page = BytePage::from(vec![3; 64]);
1851        assert_eq!(BytesMut::from(page), &[3; 64][..]);
1852
1853        let s = ByteString::from_static("string");
1854        assert_eq!(BytePage::from(&s), "string");
1855    }
1856
1857    /// STATEMENT: Appending unique storage with spare capacity makes it writable current storage, unlike full or shared pages.
1858    ///
1859    /// HOW:
1860    /// - Append a mutable page containing `a`, write `b`, and check a single page freezes to `ab`.
1861    /// - Append full and shared pages followed by `z`, checking page count, total length, and unchanged shared contents.
1862    #[test]
1863    fn append_storage_page() {
1864        // a page with spare capacity becomes the current page
1865        let mut pages = BytePages::new(BytePageSize::Size4);
1866        let mut buf = BytesMut::with_capacity(64);
1867        buf.extend_from_slice(b"a");
1868        pages.append(buf);
1869        pages.extend_from_slice(b"b");
1870        assert_eq!(pages.num_pages(), 1);
1871        assert_eq!(pages.freeze(), "ab");
1872
1873        // full and shared pages are added to the page list
1874        let mut buf = BytesMut::with_capacity(64);
1875        let cap = buf.capacity();
1876        buf.resize(cap, b'x');
1877        pages.append(buf);
1878        let page = BytePage::from(BytesMut::copy_from_slice([b'y'; 64]));
1879        let shared = page.clone();
1880        pages.append(page);
1881        pages.extend_from_slice(b"z");
1882        assert_eq!(pages.num_pages(), 3);
1883        assert_eq!(pages.len(), cap + 65);
1884        assert_eq!(shared, &[b'y'; 64][..]);
1885    }
1886
1887    // inline page, shared page, then the current page
1888    fn mixed_pages() -> BytePages {
1889        let mut pages = BytePages::new(BytePageSize::Size4);
1890        pages.extend_from_slice(b"current");
1891        pages.prepend(Bytes::copy_from_slice(&[b'b'; 64]));
1892        pages.prepend(Bytes::copy_from_slice(b"ab"));
1893        assert!(pages.pages()[0].is_inline());
1894        pages
1895    }
1896
1897    /// STATEMENT: Page iteration preserves order and addresses across moves and omits empty current pages.
1898    ///
1899    /// HOW:
1900    /// - Check empty iteration, then collect inline, shared, and current chunks from `mixed_pages` and compare their order and count.
1901    /// - Move the buffer into a box and compare chunk addresses, then consume another buffer and verify empty iteration.
1902    #[test]
1903    fn iter_pages() {
1904        let pages = BytePages::default();
1905        assert_eq!(pages.iter().count(), 0);
1906
1907        let pages = mixed_pages();
1908        let chunks: Vec<&[u8]> = pages.iter().collect();
1909        assert_eq!(chunks, [&b"ab"[..], &[b'b'; 64][..], &b"current"[..]]);
1910        assert_eq!(chunks.len(), pages.num_pages());
1911
1912        // data, including inline pages, does not move with `self`
1913        let ptrs: Vec<*const u8> = pages.iter().map(<[u8]>::as_ptr).collect();
1914        let moved = Box::new(pages);
1915        let moved_ptrs: Vec<*const u8> = moved.iter().map(<[u8]>::as_ptr).collect();
1916        assert_eq!(ptrs, moved_ptrs);
1917
1918        // an empty current page is skipped
1919        let mut pages = BytePages::new(BytePageSize::Size4);
1920        pages.extend_from_slice(b"data");
1921        pages.prepend(Bytes::copy_from_slice(b"ab"));
1922        pages.advance_to(6);
1923        assert_eq!(pages.iter().count(), 0);
1924        assert_eq!(pages.num_pages(), 0);
1925    }
1926
1927    /// STATEMENT: Advancing across pages drops consumed data while retaining the current page's reusable spare capacity.
1928    ///
1929    /// HOW:
1930    /// - Advance by zero, whole pages, and partial pages in `mixed_pages`, checking lengths, page counts, and remaining chunks.
1931    /// - Consume the current page, write `next`, and verify its pointer continues after the consumed bytes.
1932    #[test]
1933    fn advance_to_pages() {
1934        let mut pages = mixed_pages();
1935        let len = pages.len();
1936        pages.advance_to(0);
1937        assert_eq!(pages.len(), len);
1938
1939        // whole page
1940        pages.advance_to(2);
1941        assert_eq!(pages.len(), len - 2);
1942        assert_eq!(pages.num_pages(), 2);
1943        assert_eq!(pages.iter().next().unwrap(), &[b'b'; 64][..]);
1944
1945        // part of a page
1946        pages.advance_to(60);
1947        assert_eq!(pages.len(), 11);
1948        assert_eq!(pages.num_pages(), 2);
1949        let chunks: Vec<&[u8]> = pages.iter().collect();
1950        assert_eq!(chunks, [&b"bbbb"[..], &b"current"[..]]);
1951
1952        // rest of the page and part of the current page
1953        pages.advance_to(6);
1954        assert_eq!(pages.len(), 5);
1955        assert_eq!(pages.num_pages(), 1);
1956        assert_eq!(pages.iter().next().unwrap(), b"rrent");
1957
1958        // the current page keeps its spare capacity
1959        let ptr = pages.iter().next().unwrap().as_ptr();
1960        pages.advance_to(5);
1961        assert!(pages.is_empty());
1962        assert_eq!(pages.num_pages(), 0);
1963        pages.extend_from_slice(b"next");
1964        assert_eq!(pages.num_pages(), 1);
1965        assert_eq!(pages.iter().next().unwrap().as_ptr(), ptr.wrapping_add(5));
1966        assert_eq!(pages.freeze(), "next");
1967    }
1968
1969    /// STATEMENT: Moving buffers retains page allocations, destination size settings, and page order without merging small pages.
1970    ///
1971    /// HOW:
1972    /// - Move `mixed_pages` into an empty `Size16` buffer and check source emptiness, destination size, lengths, and non-inline pointers.
1973    /// - Move into a populated buffer, compare all chunk boundaries, append again, and verify moving an empty source changes nothing.
1974    #[test]
1975    fn move_to_keeps_pages() {
1976        // into empty pages, the page list is swapped
1977        let mut src = mixed_pages();
1978        let ptrs: Vec<*const u8> = src
1979            .iter()
1980            .filter(|s| s.len() > 2)
1981            .map(<[u8]>::as_ptr)
1982            .collect();
1983        let mut dst = BytePages::new(BytePageSize::Size16);
1984        src.move_to(&mut dst);
1985        assert!(src.is_empty());
1986        assert_eq!(src.num_pages(), 0);
1987        assert_eq!(dst.page_size(), BytePageSize::Size16);
1988        assert_eq!(dst.num_pages(), 3);
1989        assert_eq!(dst.len(), 73);
1990        let moved: Vec<*const u8> = dst
1991            .iter()
1992            .filter(|s| s.len() > 2)
1993            .map(<[u8]>::as_ptr)
1994            .collect();
1995        assert_eq!(ptrs, moved);
1996
1997        // small pages are not merged, the filled part of the current page
1998        // of `dst` goes first and its spare capacity is kept
1999        let mut dst = BytePages::new(BytePageSize::Size4);
2000        dst.extend_from_slice(b"first");
2001        dst.prepend(Bytes::copy_from_slice(b"0"));
2002        let mut src = mixed_pages();
2003        src.move_to(&mut dst);
2004        let chunks: Vec<&[u8]> = dst.iter().collect();
2005        assert_eq!(
2006            chunks,
2007            [
2008                &b"0"[..],
2009                &b"first"[..],
2010                &b"ab"[..],
2011                &[b'b'; 64][..],
2012                &b"current"[..]
2013            ]
2014        );
2015        assert_eq!(dst.len(), 79);
2016        dst.extend_from_slice(b"last");
2017        assert_eq!(dst.num_pages(), 6);
2018
2019        // empty source
2020        let mut src = BytePages::default();
2021        src.move_to(&mut dst);
2022        assert_eq!(dst.num_pages(), 6);
2023    }
2024
2025    /// STATEMENT: Advancing a vector-backed page retains the correct suffix.
2026    ///
2027    /// HOW:
2028    /// - Prepend a vector containing `hello world`, advance six bytes, and verify length five and frozen `world`.
2029    #[test]
2030    fn advance_to_vec_page() {
2031        let mut pages = BytePages::default();
2032        pages.prepend(b"hello world".to_vec());
2033        pages.advance_to(6);
2034        assert_eq!(pages.len(), 5);
2035        assert_eq!(pages.freeze(), "world");
2036    }
2037
2038    /// STATEMENT: Advancing beyond total page-buffer length panics.
2039    ///
2040    /// HOW:
2041    /// - Build `mixed_pages`, advance by its length plus one, and expect the end-of-buffer bounds panic.
2042    #[test]
2043    #[should_panic(expected = "cannot advance past the end of the buffer")]
2044    fn advance_to_past_end() {
2045        let mut pages = mixed_pages();
2046        pages.advance_to(pages.len() + 1);
2047    }
2048}