use core::fmt;
use crate::{order_to_bytes, order_to_pages, PhysAddr, MAX_ORDER, PAGE_SIZE};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)]
pub struct PageOrder(u8);
impl PageOrder {
pub const MIN: Self = Self(0);
pub const MAX: Self = Self((MAX_ORDER - 1) as u8);
#[inline]
#[must_use]
pub const fn new(order: usize) -> Option<Self> {
if order < MAX_ORDER {
Some(Self(order as u8))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn new_unchecked(order: usize) -> Self {
Self(order as u8)
}
#[inline]
#[must_use]
pub const fn as_usize(self) -> usize {
self.0 as usize
}
#[inline]
#[must_use]
pub const fn pages(self) -> usize {
order_to_pages(self.0 as usize)
}
#[inline]
#[must_use]
pub const fn bytes(self) -> usize {
order_to_bytes(self.0 as usize)
}
#[inline]
#[must_use]
pub const fn next(self) -> Option<Self> {
if (self.0 as usize) < MAX_ORDER - 1 {
Some(Self(self.0 + 1))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn prev(self) -> Option<Self> {
if self.0 > 0 {
Some(Self(self.0 - 1))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn from_pages(pages: usize) -> Option<Self> {
if pages == 0 {
return None;
}
let order = crate::pages_to_order(pages);
Self::new(order)
}
#[inline]
#[must_use]
pub const fn from_bytes(bytes: usize) -> Option<Self> {
if bytes == 0 {
return None;
}
let pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
Self::from_pages(pages)
}
}
impl fmt::Debug for PageOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PageOrder({})", self.0)
}
}
impl fmt::Display for PageOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "order {} ({} pages, {} bytes)", self.0, self.pages(), self.bytes())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct PageFrame {
addr: PhysAddr,
order: PageOrder,
}
impl PageFrame {
#[inline]
#[must_use]
pub const fn new(addr: PhysAddr, order: PageOrder) -> Self {
Self { addr, order }
}
#[inline]
#[must_use]
pub const fn single_page(addr: PhysAddr) -> Self {
Self {
addr,
order: PageOrder::MIN,
}
}
#[inline]
#[must_use]
pub const fn addr(&self) -> PhysAddr {
self.addr
}
#[inline]
#[must_use]
pub const fn order(&self) -> PageOrder {
self.order
}
#[inline]
#[must_use]
pub const fn pages(&self) -> usize {
self.order.pages()
}
#[inline]
#[must_use]
pub const fn bytes(&self) -> usize {
self.order.bytes()
}
#[inline]
#[must_use]
pub const fn end_addr(&self) -> PhysAddr {
self.addr.add_pages(self.pages())
}
#[inline]
#[must_use]
pub const fn pfn(&self) -> u64 {
self.addr.pfn()
}
#[inline]
#[must_use]
pub const fn contains(&self, addr: PhysAddr) -> bool {
addr.is_in_range(self.addr, self.end_addr())
}
#[inline]
#[must_use]
pub const fn split(&self) -> Option<(Self, Self)> {
match self.order.prev() {
Some(new_order) => {
let left = Self {
addr: self.addr,
order: new_order,
};
let right = Self {
addr: self.addr.add_pages(new_order.pages()),
order: new_order,
};
Some((left, right))
}
None => None,
}
}
#[inline]
#[must_use]
pub const fn buddy_addr(&self) -> PhysAddr {
let block_size = self.bytes() as u64;
PhysAddr::new(self.addr.as_u64() ^ block_size)
}
#[inline]
#[must_use]
pub const fn merge(&self, buddy: &Self) -> Option<Self> {
if self.order.0 != buddy.order.0 {
return None;
}
if self.buddy_addr().as_u64() != buddy.addr.as_u64() {
return None;
}
match self.order.next() {
Some(new_order) => {
let addr = if self.addr.as_u64() < buddy.addr.as_u64() {
self.addr
} else {
buddy.addr
};
Some(Self {
addr,
order: new_order,
})
}
None => None,
}
}
}
impl fmt::Debug for PageFrame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PageFrame")
.field("addr", &self.addr)
.field("order", &self.order.0)
.field("pages", &self.pages())
.finish()
}
}
impl fmt::Display for PageFrame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"PageFrame[{} - {}, {} pages]",
self.addr,
self.end_addr(),
self.pages()
)
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::format;
use super::*;
#[test]
fn test_page_order_new() {
assert!(PageOrder::new(0).is_some());
assert!(PageOrder::new(9).is_some());
assert!(PageOrder::new(10).is_none());
assert!(PageOrder::new(100).is_none());
}
#[test]
fn test_page_order_pages() {
assert_eq!(PageOrder::new(0).unwrap().pages(), 1);
assert_eq!(PageOrder::new(1).unwrap().pages(), 2);
assert_eq!(PageOrder::new(2).unwrap().pages(), 4);
assert_eq!(PageOrder::new(9).unwrap().pages(), 512);
}
#[test]
fn test_page_order_bytes() {
assert_eq!(PageOrder::new(0).unwrap().bytes(), 4096);
assert_eq!(PageOrder::new(1).unwrap().bytes(), 8192);
assert_eq!(PageOrder::new(9).unwrap().bytes(), 2 * 1024 * 1024);
}
#[test]
fn test_page_order_next_prev() {
let order = PageOrder::new(5).unwrap();
assert_eq!(order.next().map(|o| o.as_usize()), Some(6));
assert_eq!(order.prev().map(|o| o.as_usize()), Some(4));
assert!(PageOrder::MIN.prev().is_none());
assert!(PageOrder::MAX.next().is_none());
}
#[test]
fn test_page_order_from_pages() {
assert_eq!(PageOrder::from_pages(0), None);
assert_eq!(PageOrder::from_pages(1).map(|o| o.as_usize()), Some(0));
assert_eq!(PageOrder::from_pages(2).map(|o| o.as_usize()), Some(1));
assert_eq!(PageOrder::from_pages(3).map(|o| o.as_usize()), Some(2));
assert_eq!(PageOrder::from_pages(4).map(|o| o.as_usize()), Some(2));
assert_eq!(PageOrder::from_pages(512).map(|o| o.as_usize()), Some(9));
assert!(PageOrder::from_pages(513).is_none());
}
#[test]
fn test_page_order_from_bytes() {
assert_eq!(PageOrder::from_bytes(0), None);
assert_eq!(PageOrder::from_bytes(1).map(|o| o.as_usize()), Some(0));
assert_eq!(PageOrder::from_bytes(4096).map(|o| o.as_usize()), Some(0));
assert_eq!(PageOrder::from_bytes(4097).map(|o| o.as_usize()), Some(1));
assert_eq!(PageOrder::from_bytes(8192).map(|o| o.as_usize()), Some(1));
}
#[test]
fn test_page_frame_new() {
let frame = PageFrame::new(
PhysAddr::new(0x1000_0000),
PageOrder::new(3).unwrap(),
);
assert_eq!(frame.addr().as_u64(), 0x1000_0000);
assert_eq!(frame.order().as_usize(), 3);
assert_eq!(frame.pages(), 8);
assert_eq!(frame.bytes(), 8 * 4096);
}
#[test]
fn test_page_frame_single_page() {
let frame = PageFrame::single_page(PhysAddr::new(0x5000));
assert_eq!(frame.pages(), 1);
assert_eq!(frame.order().as_usize(), 0);
}
#[test]
fn test_page_frame_end_addr() {
let frame = PageFrame::new(
PhysAddr::new(0x1000),
PageOrder::new(2).unwrap(),
);
assert_eq!(frame.end_addr().as_u64(), 0x5000);
}
#[test]
fn test_page_frame_contains() {
let frame = PageFrame::new(
PhysAddr::new(0x1000),
PageOrder::new(2).unwrap(),
);
assert!(frame.contains(PhysAddr::new(0x1000)));
assert!(frame.contains(PhysAddr::new(0x2000)));
assert!(frame.contains(PhysAddr::new(0x4FFF)));
assert!(!frame.contains(PhysAddr::new(0x5000)));
assert!(!frame.contains(PhysAddr::new(0x0FFF)));
}
#[test]
fn test_page_frame_split() {
let frame = PageFrame::new(
PhysAddr::new(0x4000),
PageOrder::new(2).unwrap(),
);
let (left, right) = frame.split().unwrap();
assert_eq!(left.order().as_usize(), 1);
assert_eq!(right.order().as_usize(), 1);
assert_eq!(left.addr().as_u64(), 0x4000);
assert_eq!(right.addr().as_u64(), 0x6000);
assert_eq!(left.pages(), 2);
assert_eq!(right.pages(), 2);
let single = PageFrame::single_page(PhysAddr::new(0x1000));
assert!(single.split().is_none());
}
#[test]
fn test_page_frame_buddy_addr() {
let frame = PageFrame::new(
PhysAddr::new(0x2000),
PageOrder::new(1).unwrap(),
);
assert_eq!(frame.buddy_addr().as_u64(), 0x0);
let frame = PageFrame::new(
PhysAddr::new(0x0),
PageOrder::new(1).unwrap(),
);
assert_eq!(frame.buddy_addr().as_u64(), 0x2000);
let frame = PageFrame::new(
PhysAddr::new(0x4000),
PageOrder::new(2).unwrap(),
);
assert_eq!(frame.buddy_addr().as_u64(), 0x0);
}
#[test]
fn test_page_frame_merge() {
let left = PageFrame::new(PhysAddr::new(0x0), PageOrder::new(1).unwrap());
let right = PageFrame::new(PhysAddr::new(0x2000), PageOrder::new(1).unwrap());
let merged = left.merge(&right).unwrap();
assert_eq!(merged.order().as_usize(), 2);
assert_eq!(merged.addr().as_u64(), 0x0);
assert_eq!(merged.pages(), 4);
let merged2 = right.merge(&left).unwrap();
assert_eq!(merged2.addr().as_u64(), 0x0);
let non_buddy = PageFrame::new(PhysAddr::new(0x4000), PageOrder::new(1).unwrap());
assert!(left.merge(&non_buddy).is_none());
let diff_order = PageFrame::new(PhysAddr::new(0x2000), PageOrder::new(2).unwrap());
assert!(left.merge(&diff_order).is_none());
}
#[test]
fn test_page_frame_display() {
let frame = PageFrame::new(
PhysAddr::new(0x1000),
PageOrder::new(2).unwrap(),
);
let s = format!("{frame}");
assert!(s.contains("0x1000"));
assert!(s.contains("0x5000"));
assert!(s.contains("4 pages"));
}
}