Skip to main content

ntex_bytes/
size.rs

1use crate::stvec;
2
3/// Capacity category used when allocating [`crate::BytePage`] storage.
4///
5/// Buffers with a page size are returned to a per-thread cache of their
6/// category when the last reference is dropped, see
7/// [`crate::set_page_cache_size`].
8#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum BytePageSize {
11    /// A 4 KiB page.
12    Size4 = 0,
13    /// An 8 KiB page.
14    Size8 = 1,
15    /// A 16 KiB page.
16    #[default]
17    Size16 = 2,
18    /// A 24 KiB page.
19    Size24 = 3,
20    /// A 32 KiB page.
21    Size32 = 4,
22    /// A 48 KiB page.
23    Size48 = 5,
24    /// A 64 KiB page.
25    Size64 = 6,
26    /// A 128 KiB page.
27    Size128 = 7,
28    /// A 256 KiB page.
29    Size256 = 8,
30    /// No fixed page category.
31    ///
32    /// Buffers of this category are sized on demand and never returned to
33    /// the page cache. A buffer whose allocation is exactly a page size
34    /// belongs to that page category, whichever way it was created. It
35    /// cannot be used as the page size of [`crate::BytePages`].
36    Unset = 9,
37}
38
39/// Page categories in increasing order of size, `Unset` excluded.
40pub(crate) const PAGE_SIZES: [BytePageSize; 9] = [
41    BytePageSize::Size4,
42    BytePageSize::Size8,
43    BytePageSize::Size16,
44    BytePageSize::Size24,
45    BytePageSize::Size32,
46    BytePageSize::Size48,
47    BytePageSize::Size64,
48    BytePageSize::Size128,
49    BytePageSize::Size256,
50];
51
52/// Size of the units of [`CLASS_BY_UNITS`], every page size is a multiple of it.
53const PAGE_UNIT_SHIFT: u32 = 12;
54
55/// Page category by allocation size in 4 KiB units, `Unset` for sizes that
56/// are not a page size.
57const CLASS_BY_UNITS: [BytePageSize; 65] = {
58    let mut table = [BytePageSize::Unset; 65];
59    let mut i = 0;
60    while i < PAGE_SIZES.len() {
61        table[PAGE_SIZES[i].alloc_size() >> PAGE_UNIT_SHIFT] = PAGE_SIZES[i];
62        i += 1;
63    }
64    table
65};
66
67impl BytePageSize {
68    /// Returns the smallest page category with a [`capacity`](Self::capacity)
69    /// of at least `capacity` bytes.
70    ///
71    /// Returns [`BytePageSize::Unset`] if `capacity` is larger than the
72    /// capacity of the largest category.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// use ntex_bytes::BytePageSize;
78    ///
79    /// assert_eq!(BytePageSize::for_capacity(100), BytePageSize::Size4);
80    /// assert_eq!(BytePageSize::for_capacity(5000), BytePageSize::Size8);
81    /// assert_eq!(BytePageSize::for_capacity(1024 * 1024), BytePageSize::Unset);
82    /// ```
83    pub const fn for_capacity(capacity: usize) -> BytePageSize {
84        let mut i = 0;
85        while i < PAGE_SIZES.len() {
86            if capacity <= PAGE_SIZES[i].capacity() {
87                return PAGE_SIZES[i];
88            }
89            i += 1;
90        }
91        BytePageSize::Unset
92    }
93
94    /// Returns the next larger page category.
95    ///
96    /// The largest category returns [`BytePageSize::Unset`], `Unset` returns
97    /// itself.
98    #[must_use]
99    pub const fn next(self) -> BytePageSize {
100        match self {
101            BytePageSize::Size4 => BytePageSize::Size8,
102            BytePageSize::Size8 => BytePageSize::Size16,
103            BytePageSize::Size16 => BytePageSize::Size24,
104            BytePageSize::Size24 => BytePageSize::Size32,
105            BytePageSize::Size32 => BytePageSize::Size48,
106            BytePageSize::Size48 => BytePageSize::Size64,
107            BytePageSize::Size64 => BytePageSize::Size128,
108            BytePageSize::Size128 => BytePageSize::Size256,
109            BytePageSize::Size256 | BytePageSize::Unset => BytePageSize::Unset,
110        }
111    }
112
113    /// Returns the next smaller page category.
114    ///
115    /// The smallest category returns itself, [`BytePageSize::Unset`] returns
116    /// the largest category.
117    #[must_use]
118    pub const fn prev(self) -> BytePageSize {
119        match self {
120            BytePageSize::Size4 | BytePageSize::Size8 => BytePageSize::Size4,
121            BytePageSize::Size16 => BytePageSize::Size8,
122            BytePageSize::Size24 => BytePageSize::Size16,
123            BytePageSize::Size32 => BytePageSize::Size24,
124            BytePageSize::Size48 => BytePageSize::Size32,
125            BytePageSize::Size64 => BytePageSize::Size48,
126            BytePageSize::Size128 => BytePageSize::Size64,
127            BytePageSize::Size256 => BytePageSize::Size128,
128            BytePageSize::Unset => BytePageSize::Size256,
129        }
130    }
131
132    /// Returns the page capacity in bytes.
133    ///
134    /// A page is allocated together with its header, the capacity is the
135    /// category size minus the header, so the allocation is exactly the
136    /// category size and fits the allocator's size classes.
137    pub const fn capacity(self) -> usize {
138        self.alloc_size() - stvec::METADATA_SIZE
139    }
140
141    const fn alloc_size(self) -> usize {
142        match self {
143            BytePageSize::Size4 => 4 * 1024,
144            BytePageSize::Size8 => 8 * 1024,
145            BytePageSize::Size16 => 16 * 1024,
146            BytePageSize::Size24 => 24 * 1024,
147            BytePageSize::Size32 => 32 * 1024,
148            BytePageSize::Size48 => 48 * 1024,
149            BytePageSize::Size64 | BytePageSize::Unset => 64 * 1024,
150            BytePageSize::Size128 => 128 * 1024,
151            BytePageSize::Size256 => 256 * 1024,
152        }
153    }
154
155    /// Returns the page category of an allocation of `size` bytes, header
156    /// included, `Unset` if `size` is not a page size.
157    #[inline]
158    pub(crate) const fn from_alloc_size(size: usize) -> BytePageSize {
159        let units = size >> PAGE_UNIT_SHIFT;
160        if size & ((1 << PAGE_UNIT_SHIFT) - 1) != 0 || units >= CLASS_BY_UNITS.len() {
161            BytePageSize::Unset
162        } else {
163            CLASS_BY_UNITS[units]
164        }
165    }
166
167    /// Returns the recommended write-buffer threshold for this page size.
168    ///
169    /// This is half of the category size, but at most 16 KiB.
170    pub const fn half_capacity(self) -> usize {
171        match self {
172            BytePageSize::Size4 => 2 * 1024,
173            BytePageSize::Size8 => 4 * 1024,
174            BytePageSize::Size16 => 8 * 1024,
175            BytePageSize::Size24 => 12 * 1024,
176            BytePageSize::Size32
177            | BytePageSize::Size48
178            | BytePageSize::Size64
179            | BytePageSize::Size128
180            | BytePageSize::Size256
181            | BytePageSize::Unset => 16 * 1024,
182        }
183    }
184
185    /// Returns the low free-capacity threshold for this page size.
186    ///
187    /// This is 2/32 of the category size: 256 bytes for `Size4`, 1 KiB for
188    /// `Size16`, 16 KiB for `Size256` and 4 KiB for [`BytePageSize::Unset`].
189    /// [`crate::BytesMut::reserve_more`] grows a buffer once its remaining
190    /// capacity falls below it.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use ntex_bytes::BytePageSize;
196    ///
197    /// assert_eq!(BytePageSize::Size4.low(), 256);
198    /// assert_eq!(BytePageSize::Size8.low(), 512);
199    /// assert_eq!(BytePageSize::Size16.low(), 1024);
200    /// assert_eq!(BytePageSize::Unset.low(), 4096);
201    /// ```
202    pub const fn low(self) -> usize {
203        self.alloc_size() >> 4
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn page_size() {
213        const META: usize = stvec::METADATA_SIZE;
214        assert_eq!(BytePageSize::Size4.capacity(), 4 * 1024 - META);
215        assert_eq!(BytePageSize::Size8.capacity(), 8 * 1024 - META);
216        assert_eq!(BytePageSize::Size16.capacity(), 16 * 1024 - META);
217        assert_eq!(BytePageSize::Size24.capacity(), 24 * 1024 - META);
218        assert_eq!(BytePageSize::Size32.capacity(), 32 * 1024 - META);
219        assert_eq!(BytePageSize::Size48.capacity(), 48 * 1024 - META);
220        assert_eq!(BytePageSize::Size64.capacity(), 64 * 1024 - META);
221        assert_eq!(BytePageSize::Size128.capacity(), 128 * 1024 - META);
222        assert_eq!(BytePageSize::Size256.capacity(), 256 * 1024 - META);
223        assert_eq!(BytePageSize::Unset.capacity(), 64 * 1024 - META);
224        assert_eq!(BytePageSize::Size4.half_capacity(), 2 * 1024);
225        assert_eq!(BytePageSize::Size8.half_capacity(), 4 * 1024);
226        assert_eq!(BytePageSize::Size16.half_capacity(), 8 * 1024);
227        assert_eq!(BytePageSize::Size24.half_capacity(), 12 * 1024);
228        assert_eq!(BytePageSize::Size32.half_capacity(), 16 * 1024);
229        assert_eq!(BytePageSize::Size48.half_capacity(), 16 * 1024);
230        assert_eq!(BytePageSize::Size64.half_capacity(), 16 * 1024);
231        assert_eq!(BytePageSize::Size128.half_capacity(), 16 * 1024);
232        assert_eq!(BytePageSize::Size256.half_capacity(), 16 * 1024);
233        assert_eq!(BytePageSize::Unset.half_capacity(), 16 * 1024);
234    }
235
236    #[test]
237    fn page_size_from_alloc_size() {
238        const META: usize = stvec::METADATA_SIZE;
239        assert_eq!(META, 16);
240        for size in PAGE_SIZES {
241            assert_eq!(BytePageSize::from_alloc_size(size.alloc_size()), size);
242            assert_eq!(BytePageSize::from_alloc_size(size.capacity() + META), size);
243            assert_eq!(
244                BytePageSize::from_alloc_size(size.alloc_size() - 1),
245                BytePageSize::Unset
246            );
247            assert_eq!(
248                BytePageSize::from_alloc_size(size.alloc_size() + 1),
249                BytePageSize::Unset
250            );
251        }
252        for units in [0, 3, 5, 10, 40, 63, 64 + 1, 128 + 1] {
253            assert_eq!(
254                BytePageSize::from_alloc_size(units * 4096),
255                BytePageSize::Unset
256            );
257        }
258        for size in [0, 1, 4095, 512 * 1024, usize::MAX, usize::MAX & !0xFFF] {
259            assert_eq!(BytePageSize::from_alloc_size(size), BytePageSize::Unset);
260        }
261    }
262
263    #[test]
264    fn page_size_for_capacity() {
265        assert_eq!(BytePageSize::for_capacity(0), BytePageSize::Size4);
266        for size in PAGE_SIZES {
267            let cap = size.capacity();
268            assert_eq!(BytePageSize::for_capacity(cap), size);
269            if size != BytePageSize::Size4 {
270                assert_eq!(BytePageSize::for_capacity(size.prev().capacity() + 1), size);
271            }
272        }
273        assert_eq!(
274            BytePageSize::for_capacity(BytePageSize::Size256.capacity() + 1),
275            BytePageSize::Unset
276        );
277        assert_eq!(BytePageSize::for_capacity(usize::MAX), BytePageSize::Unset);
278    }
279
280    #[test]
281    fn page_size_next_prev() {
282        let mut size = BytePageSize::Size4;
283        for expected in &PAGE_SIZES[1..] {
284            size = size.next();
285            assert_eq!(size, *expected);
286        }
287        assert_eq!(size.next(), BytePageSize::Unset);
288        assert_eq!(BytePageSize::Unset.next(), BytePageSize::Unset);
289
290        let mut size = BytePageSize::Unset;
291        for expected in PAGE_SIZES.iter().rev() {
292            size = size.prev();
293            assert_eq!(size, *expected);
294        }
295        assert_eq!(size.prev(), BytePageSize::Size4);
296
297        for pair in PAGE_SIZES.windows(2) {
298            assert!(pair[0].capacity() < pair[1].capacity());
299        }
300    }
301}