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