use super::Pos;
use crate::{PageTable, Pte, VmMeta};
use core::ptr::NonNull;
pub trait Visitor<Meta: VmMeta> {
fn arrive(&mut self, pte: Pte<Meta>, target: Pos<Meta>) -> Pos<Meta>;
fn meet(
&mut self,
level: usize,
pte: Pte<Meta>,
target: Pos<Meta>,
) -> Option<NonNull<Pte<Meta>>>;
fn block(&mut self, level: usize, pte: Pte<Meta>, target: Pos<Meta>) -> Pos<Meta>;
}
pub trait Decorator<Meta: VmMeta> {
fn arrive(&mut self, pte: &mut Pte<Meta>, target_hint: Pos<Meta>) -> Pos<Meta>;
fn meet(
&mut self,
level: usize,
pte: Pte<Meta>,
target: Pos<Meta>,
) -> Option<NonNull<Pte<Meta>>>;
fn block(&mut self, level: usize, pte: Pte<Meta>, target_hint: Pos<Meta>) -> Update<Meta>;
}
pub enum Update<Meta: VmMeta> {
Target(Pos<Meta>),
Pte(Pte<Meta>, NonNull<Pte<Meta>>),
}
pub(super) fn walk_inner<Meta: VmMeta>(
table: &PageTable<Meta>,
visitor: &mut impl Visitor<Meta>,
target: &mut Pos<Meta>,
) {
let range = table.range();
let level = table.level;
while level >= target.level && range.contains(&target.vpn) {
let index = target.vpn.index_in(level);
let pte = table.mem[index];
if level > target.level {
if pte.is_valid() && !pte.is_leaf() {
match visitor.meet(level, pte, *target) {
Some(ptr) => {
let table = unsafe {
PageTable::from_raw_parts(
ptr,
range.start + index * Meta::pages_in_table(level - 1),
level - 1,
)
};
walk_inner(&table, visitor, target);
}
None => *target = Pos::stop(),
}
}
else {
*target = visitor.block(level, pte, *target);
}
}
else {
*target = visitor.arrive(pte, *target);
}
}
}
pub(super) fn walk_inner_mut<Meta: VmMeta>(
table: &mut PageTable<Meta>,
visitor: &mut impl Decorator<Meta>,
target: &mut Pos<Meta>,
) {
let range = table.range();
let level = table.level;
while level >= target.level && range.contains(&target.vpn) {
let index = target.vpn.index_in(level);
let pte = &mut table.mem[index];
if level > target.level {
if pte.is_valid() && !pte.is_leaf() {
match visitor.meet(level, *pte, *target) {
Some(ptr) => {
let mut table = unsafe {
PageTable::from_raw_parts(
ptr,
range.start + index * Meta::pages_in_table(level - 1),
level - 1,
)
};
walk_inner_mut(&mut table, visitor, target);
}
None => *target = Pos::stop(),
}
}
else {
match visitor.block(level, *pte, *target) {
Update::Target(new) => *target = new,
Update::Pte(new, ptr) => {
*pte = new;
let mut table = unsafe {
PageTable::from_raw_parts(
ptr,
range.start + index * Meta::pages_in_table(level - 1),
level - 1,
)
};
walk_inner_mut(&mut table, visitor, target);
}
}
}
}
else {
*target = visitor.arrive(pte, *target);
}
}
}