use crate::stvec;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum BytePageSize {
Size4 = 0,
Size8 = 1,
#[default]
Size16 = 2,
Size24 = 3,
Size32 = 4,
Size48 = 5,
Size64 = 6,
Size128 = 7,
Size256 = 8,
Unset = 9,
}
pub(crate) const PAGE_SIZES: [BytePageSize; 9] = [
BytePageSize::Size4,
BytePageSize::Size8,
BytePageSize::Size16,
BytePageSize::Size24,
BytePageSize::Size32,
BytePageSize::Size48,
BytePageSize::Size64,
BytePageSize::Size128,
BytePageSize::Size256,
];
const PAGE_UNIT_SHIFT: u32 = 12;
const CLASS_BY_UNITS: [BytePageSize; 65] = {
let mut table = [BytePageSize::Unset; 65];
let mut i = 0;
while i < PAGE_SIZES.len() {
table[PAGE_SIZES[i].alloc_size() >> PAGE_UNIT_SHIFT] = PAGE_SIZES[i];
i += 1;
}
table
};
impl BytePageSize {
pub const fn for_capacity(capacity: usize) -> BytePageSize {
let mut i = 0;
while i < PAGE_SIZES.len() {
if capacity <= PAGE_SIZES[i].capacity() {
return PAGE_SIZES[i];
}
i += 1;
}
BytePageSize::Unset
}
#[must_use]
pub const fn next(self) -> BytePageSize {
match self {
BytePageSize::Size4 => BytePageSize::Size8,
BytePageSize::Size8 => BytePageSize::Size16,
BytePageSize::Size16 => BytePageSize::Size24,
BytePageSize::Size24 => BytePageSize::Size32,
BytePageSize::Size32 => BytePageSize::Size48,
BytePageSize::Size48 => BytePageSize::Size64,
BytePageSize::Size64 => BytePageSize::Size128,
BytePageSize::Size128 => BytePageSize::Size256,
BytePageSize::Size256 | BytePageSize::Unset => BytePageSize::Unset,
}
}
#[must_use]
pub const fn prev(self) -> BytePageSize {
match self {
BytePageSize::Size4 | BytePageSize::Size8 => BytePageSize::Size4,
BytePageSize::Size16 => BytePageSize::Size8,
BytePageSize::Size24 => BytePageSize::Size16,
BytePageSize::Size32 => BytePageSize::Size24,
BytePageSize::Size48 => BytePageSize::Size32,
BytePageSize::Size64 => BytePageSize::Size48,
BytePageSize::Size128 => BytePageSize::Size64,
BytePageSize::Size256 => BytePageSize::Size128,
BytePageSize::Unset => BytePageSize::Size256,
}
}
pub const fn capacity(self) -> usize {
self.alloc_size() - stvec::METADATA_SIZE
}
const fn alloc_size(self) -> usize {
match self {
BytePageSize::Size4 => 4 * 1024,
BytePageSize::Size8 => 8 * 1024,
BytePageSize::Size16 => 16 * 1024,
BytePageSize::Size24 => 24 * 1024,
BytePageSize::Size32 => 32 * 1024,
BytePageSize::Size48 => 48 * 1024,
BytePageSize::Size64 | BytePageSize::Unset => 64 * 1024,
BytePageSize::Size128 => 128 * 1024,
BytePageSize::Size256 => 256 * 1024,
}
}
#[inline]
pub(crate) const fn from_alloc_size(size: usize) -> BytePageSize {
let units = size >> PAGE_UNIT_SHIFT;
if size & ((1 << PAGE_UNIT_SHIFT) - 1) != 0 || units >= CLASS_BY_UNITS.len() {
BytePageSize::Unset
} else {
CLASS_BY_UNITS[units]
}
}
pub const fn half_capacity(self) -> usize {
match self {
BytePageSize::Size4 => 2 * 1024,
BytePageSize::Size8 => 4 * 1024,
BytePageSize::Size16 => 8 * 1024,
BytePageSize::Size24 => 12 * 1024,
BytePageSize::Size32
| BytePageSize::Size48
| BytePageSize::Size64
| BytePageSize::Size128
| BytePageSize::Size256
| BytePageSize::Unset => 16 * 1024,
}
}
pub const fn low(self) -> usize {
self.alloc_size() >> 4
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn page_size() {
const META: usize = stvec::METADATA_SIZE;
assert_eq!(BytePageSize::Size4.capacity(), 4 * 1024 - META);
assert_eq!(BytePageSize::Size8.capacity(), 8 * 1024 - META);
assert_eq!(BytePageSize::Size16.capacity(), 16 * 1024 - META);
assert_eq!(BytePageSize::Size24.capacity(), 24 * 1024 - META);
assert_eq!(BytePageSize::Size32.capacity(), 32 * 1024 - META);
assert_eq!(BytePageSize::Size48.capacity(), 48 * 1024 - META);
assert_eq!(BytePageSize::Size64.capacity(), 64 * 1024 - META);
assert_eq!(BytePageSize::Size128.capacity(), 128 * 1024 - META);
assert_eq!(BytePageSize::Size256.capacity(), 256 * 1024 - META);
assert_eq!(BytePageSize::Unset.capacity(), 64 * 1024 - META);
assert_eq!(BytePageSize::Size4.half_capacity(), 2 * 1024);
assert_eq!(BytePageSize::Size8.half_capacity(), 4 * 1024);
assert_eq!(BytePageSize::Size16.half_capacity(), 8 * 1024);
assert_eq!(BytePageSize::Size24.half_capacity(), 12 * 1024);
assert_eq!(BytePageSize::Size32.half_capacity(), 16 * 1024);
assert_eq!(BytePageSize::Size48.half_capacity(), 16 * 1024);
assert_eq!(BytePageSize::Size64.half_capacity(), 16 * 1024);
assert_eq!(BytePageSize::Size128.half_capacity(), 16 * 1024);
assert_eq!(BytePageSize::Size256.half_capacity(), 16 * 1024);
assert_eq!(BytePageSize::Unset.half_capacity(), 16 * 1024);
}
#[test]
fn page_size_from_alloc_size() {
const META: usize = stvec::METADATA_SIZE;
assert_eq!(META, 16);
for size in PAGE_SIZES {
assert_eq!(BytePageSize::from_alloc_size(size.alloc_size()), size);
assert_eq!(BytePageSize::from_alloc_size(size.capacity() + META), size);
assert_eq!(
BytePageSize::from_alloc_size(size.alloc_size() - 1),
BytePageSize::Unset
);
assert_eq!(
BytePageSize::from_alloc_size(size.alloc_size() + 1),
BytePageSize::Unset
);
}
for units in [0, 3, 5, 10, 40, 63, 64 + 1, 128 + 1] {
assert_eq!(
BytePageSize::from_alloc_size(units * 4096),
BytePageSize::Unset
);
}
for size in [0, 1, 4095, 512 * 1024, usize::MAX, usize::MAX & !0xFFF] {
assert_eq!(BytePageSize::from_alloc_size(size), BytePageSize::Unset);
}
}
#[test]
fn page_size_for_capacity() {
assert_eq!(BytePageSize::for_capacity(0), BytePageSize::Size4);
for size in PAGE_SIZES {
let cap = size.capacity();
assert_eq!(BytePageSize::for_capacity(cap), size);
if size != BytePageSize::Size4 {
assert_eq!(BytePageSize::for_capacity(size.prev().capacity() + 1), size);
}
}
assert_eq!(
BytePageSize::for_capacity(BytePageSize::Size256.capacity() + 1),
BytePageSize::Unset
);
assert_eq!(BytePageSize::for_capacity(usize::MAX), BytePageSize::Unset);
}
#[test]
fn page_size_next_prev() {
let mut size = BytePageSize::Size4;
for expected in &PAGE_SIZES[1..] {
size = size.next();
assert_eq!(size, *expected);
}
assert_eq!(size.next(), BytePageSize::Unset);
assert_eq!(BytePageSize::Unset.next(), BytePageSize::Unset);
let mut size = BytePageSize::Unset;
for expected in PAGE_SIZES.iter().rev() {
size = size.prev();
assert_eq!(size, *expected);
}
assert_eq!(size.prev(), BytePageSize::Size4);
for pair in PAGE_SIZES.windows(2) {
assert!(pair[0].capacity() < pair[1].capacity());
}
}
}