use anyhow::Result;
use thiserror::Error;
use super::RVA;
const PAGE_SIZE: usize = 0x1000;
const PAGE_SHIFT: usize = 12;
const PAGE_MASK: usize = 0xFFF;
#[derive(Debug, Error)]
pub enum PageMapError {
#[error("address not mapped")]
NotMapped,
}
fn page(rva: RVA) -> usize {
(rva as usize) >> PAGE_SHIFT
}
fn page_offset(rva: RVA) -> usize {
(rva as usize) & PAGE_MASK
}
#[derive(Clone)]
struct Page<T: Default + Copy> {
elements: [T; PAGE_SIZE],
}
impl<T: Default + Copy> Page<T> {
fn new(items: &[T]) -> Page<T> {
let mut page: Page<T> = Default::default();
page.elements.copy_from_slice(items);
page
}
}
impl<T: Default + Copy> Default for Page<T> {
fn default() -> Self {
Page {
elements: [Default::default(); PAGE_SIZE],
}
}
}
#[derive(Clone)]
pub struct PageMap<T: Default + Copy> {
pages: Vec<Option<Page<T>>>,
}
impl<T: Default + Copy> PageMap<T> {
pub fn with_capacity(capacity: RVA) -> PageMap<T> {
let page_count = page(capacity) + 1;
let mut pages = Vec::with_capacity(page_count);
pages.resize_with(page_count, || None);
PageMap { pages }
}
pub fn from_items(items: &[T]) -> PageMap<T> {
let capacity = crate::util::align(items.len() as u64, PAGE_SIZE as u64);
let mut map = PageMap::with_capacity(capacity);
map.writezx(0x0, items).unwrap();
map
}
fn write_page(&mut self, rva: RVA, items: &[T]) -> Result<()> {
if page_offset(rva) != 0 {
panic!("invalid map address");
}
if items.len() != PAGE_SIZE {
panic!("invalid map buffer size");
}
if page(rva) > self.pages.len() - 1 {
return Err(PageMapError::NotMapped.into());
}
self.pages[page(rva)] = Some(Page::new(items));
Ok(())
}
pub fn write(&mut self, rva: RVA, items: &[T]) -> Result<()> {
if items.len() % PAGE_SIZE != 0 {
panic!("items must be page aligned");
}
for (i, chunk) in items.chunks_exact(PAGE_SIZE).enumerate() {
self.write_page(rva + (i * PAGE_SIZE) as u64, chunk)?;
}
Ok(())
}
pub fn map_empty(&mut self, rva: RVA, size: usize) -> Result<()> {
self.write(rva, &vec![Default::default(); size])
}
pub fn writezx(&mut self, rva: RVA, items: &[T]) -> Result<()> {
let empty_count = PAGE_SIZE - page_offset(items.len() as u64);
let mut padded_items = Vec::with_capacity(items.len() + empty_count);
padded_items.extend(items);
padded_items.extend(&vec![Default::default(); empty_count]);
self.write(rva, &padded_items)
}
pub fn probe(&self, rva: RVA) -> bool {
if page(rva) > self.pages.len() - 1 {
return false;
}
self.pages[page(rva)].is_some()
}
pub fn get(&self, rva: RVA) -> Option<T> {
if page(rva) > self.pages.len() - 1 {
return None;
}
let page = match &self.pages[page(rva)] {
None => return None,
Some(page) => page,
};
Some(page.elements[page_offset(rva)])
}
pub fn get_mut(&mut self, rva: RVA) -> Option<&mut T> {
if page(rva) > self.pages.len() - 1 {
return None;
}
let page = match &mut self.pages[page(rva)] {
None => return None,
Some(page) => page,
};
Some(&mut page.elements[page_offset(rva)])
}
fn slice_into_simple<'a>(&self, start: RVA, buf: &'a mut [T]) -> Result<&'a [T]> {
if page(start) > self.pages.len() - 1 {
return Err(PageMapError::NotMapped.into());
}
let page = match &self.pages[page(start)] {
None => return Err(PageMapError::NotMapped.into()),
Some(page) => page,
};
let end = start + buf.len() as u64;
let elements = &page.elements[page_offset(start)..page_offset(end)];
buf.copy_from_slice(elements);
Ok(buf)
}
fn slice_into_split<'a>(&self, start: RVA, buf: &'a mut [T]) -> Result<&'a [T]> {
let end = start + buf.len() as u64;
let start_page = page(start);
let end_page = if page_offset(end) == 0 {
page(end) - 1
} else {
page(end)
};
if end_page > self.pages.len() - 1 {
return Err(PageMapError::NotMapped.into());
}
for page in start_page..=end_page {
if !self.probe((page * PAGE_SIZE) as RVA) {
return Err(PageMapError::NotMapped.into());
}
}
let mut offset: usize = 0;
{
let page = self.pages[page(start)].as_ref().expect("slice_into_split: one");
let elements = &page.elements[page_offset(start)..];
{
let dst = &mut buf[offset..offset + elements.len()];
dst.copy_from_slice(elements);
offset += elements.len();
}
}
if page(start) != page(end) - 1 {
let start_index = page(start) + 1;
let end_index = page(end);
for page_index in start_index..end_index {
let page = self.pages[page_index].as_ref().expect("slice_into_split: two");
let elements = &page.elements[..];
{
let dst = &mut buf[offset..offset + elements.len()];
dst.copy_from_slice(elements);
offset += elements.len();
}
}
}
if page_offset(end) != 0x0 {
let page = self.pages[page(end)].as_ref().expect("slice_into_split: three");
let elements = &page.elements[..page_offset(end)];
{
let dst = &mut buf[offset..offset + elements.len()];
dst.copy_from_slice(elements);
}
}
Ok(buf)
}
pub fn slice_into<'a>(&self, start: RVA, buf: &'a mut [T]) -> Result<&'a [T]> {
let end = start + buf.len() as u64;
if page(start) == page(end) {
self.slice_into_simple(start, buf)
} else {
self.slice_into_split(start, buf)
}
}
pub fn slice(&self, start: RVA, end: RVA) -> Result<Vec<T>> {
if start > end {
panic!("start > end");
}
if end - start > std::usize::MAX as u64 {
panic!("slice too large")
}
let mut ret = vec![Default::default(); (end - start) as usize];
self.slice_into(start, &mut ret)?;
Ok(ret)
}
}
impl<T: Default + Copy> std::fmt::Debug for PageMap<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut was_allocated = false;
writeln!(f, "regions:")?;
for (i, page) in self.pages.iter().enumerate() {
match page {
Some(_) => {
if !was_allocated {
write!(f, " - {:#x}", i * PAGE_SIZE)?;
}
was_allocated = true;
}
None => {
if was_allocated {
writeln!(f, "-{:#x} mapped", i * PAGE_SIZE)?;
}
was_allocated = false;
}
};
}
if was_allocated {
writeln!(f, " - {:#x} mapped", self.pages.len() * PAGE_SIZE)?;
}
writeln!(f, "capacity: {:#x}", self.pages.len() * PAGE_SIZE)?;
Ok(())
}
}