use crate::{
device::{FreePage, Page, ReadPage, SizeTool, UpdateList, is_free},
error::{GenericError, PERes},
util::io::ReadFormat,
};
use std::{
io::{Read, Seek, SeekFrom, Write},
sync::Arc,
};
fn check_reached_limit(limit: Option<u64>, new_size: u64) -> PERes<()> {
if let Some(l) = limit {
if l <= new_size {
return Err(GenericError::Io {
from: std::io::Error::other(format!("reached limit {}", l)),
});
}
}
Ok(())
}
pub(crate) fn load_free_page<T: Read + Seek>(fl: &mut T, page: u64) -> PERes<FreePage> {
fl.seek(SeekFrom::Start(page))?;
let mut buff = [0u8; 32];
fl.read_exact(&mut buff)?;
Ok(FreePage::new(page, buff))
}
pub(crate) fn flush_free_page<T: Write + Seek>(fl: &mut T, page: &FreePage) -> PERes<()> {
fl.seek(SeekFrom::Start(page.index))?;
fl.write_all(&page.buff)?;
Ok(())
}
pub(crate) fn load_page<T: Read + Seek>(fl: &mut T, page: u64) -> PERes<ReadPage> {
fl.seek(SeekFrom::Start(page))?;
let exp = fl.read_u8()?;
let size = 1 << exp;
let mut ve = vec![0u8; size];
ve[0] = exp;
fl.read_exact(&mut ve[1..size])?;
Ok(ReadPage::new(Arc::new(ve), 2, page, exp))
}
pub(crate) fn create_page_raw<T: Write + Seek>(fl: &mut T, exp: u8, limit: Option<u64>) -> PERes<u64> {
let size = 1 << exp;
let ve = vec![0u8; size];
let offset = fl.seek(SeekFrom::End(0))?;
check_reached_limit(limit, offset + size as u64)?;
fl.write_all(&ve)?;
Ok(offset)
}
pub(crate) fn load_page_raw<T: Read + Seek>(fl: &mut T, page: u64, size_exp: u8) -> PERes<Page> {
let mut ve;
let size;
{
fl.seek(SeekFrom::Start(page))?;
size = 1 << size_exp;
ve = vec![0u8; size];
fl.read_exact(&mut ve[0..size])?;
}
Ok(Page::new(ve, 0, page, size_exp))
}
pub(crate) fn flush_page<T: Write + Seek>(fl: &mut T, page: &Page) -> PERes<()> {
fl.seek(SeekFrom::Start(page.index))?;
fl.write_all(&page.buff)?;
Ok(())
}
pub(crate) fn create_page<T: Write + Seek>(fl: &mut T, exp: u8, limit: Option<u64>) -> PERes<Page> {
let size = 1 << exp;
let mut ve = vec![0u8; size];
ve[0] = exp;
ve[size - 1] = exp;
let offset = fl.seek(SeekFrom::End(0))?;
check_reached_limit(limit, offset + size as u64)?;
fl.write_all(&ve)?;
Ok(Page::new(ve, 2, offset, exp))
}
pub(crate) fn mark_allocated<T: Write + Read + Seek>(fl: &mut T, page: u64) -> PERes<u64> {
let mut fp = load_free_page(fl, page)?;
debug_assert!(fp.is_free()?, "allocating: {} already allocated ", page);
fp.set_free(false)?;
let prev = fp.get_prev_free();
fp.set_next_free(0);
fp.set_prev_free(0);
flush_free_page(fl, &fp)?;
if prev != 0 {
let mut pfp = load_free_page(fl, prev)?;
pfp.set_next_free(0);
flush_free_page(fl, &pfp)?;
}
Ok(prev)
}
pub(crate) fn trim_or_free_page<T: Write + Read + Seek + SizeTool>(
fl: &mut T,
page: u64,
update_list: &mut dyn UpdateList,
) -> PERes<()> {
let mut to_free = load_free_page(fl, page)?;
let exp = to_free.get_size_exp();
debug_assert!(!to_free.is_free()?, "freeing: {} already freed ", page);
let size = (1 << exp) as u64;
if page + size == fl.len()? {
fl.set_len(page)?;
while check_and_trim(fl, update_list, true)? {}
} else {
let next = update_list.update(exp, page)?;
to_free.set_free(true)?;
to_free.set_prev_free(0);
to_free.set_next_free(next);
flush_free_page(fl, &to_free)?;
if next != 0 {
let mut nf = load_free_page(fl, next)?;
nf.set_prev_free(page);
flush_free_page(fl, &nf)?;
}
}
Ok(())
}
pub(crate) fn check_and_trim<T: Write + Read + Seek + SizeTool>(
fl: &mut T,
update_list: &mut dyn UpdateList,
check_top_list: bool,
) -> PERes<bool> {
let len = fl.seek(SeekFrom::End(0))?;
if len == 0 {
return Ok(false);
}
fl.seek(SeekFrom::Start(len - 1))?;
let exp = fl.read_u8()?;
if exp == 0 {
return Ok(false);
}
let size = (1 << exp) as u64;
let mut header = [0u8; 18];
let page = len - size;
if page == 0 {
return Ok(false);
}
fl.seek(SeekFrom::Start(page))?;
fl.read_exact(&mut header)?;
if is_free(header[1]) {
let fp = load_free_page(fl, page)?;
if fp.get_prev_free() == 0 {
update_list.remove(exp, page, fp.get_next_free(), check_top_list)?;
} else {
let mut pfp = load_free_page(fl, fp.get_prev_free())?;
pfp.set_next_free(fp.get_next_free());
flush_free_page(fl, &pfp)?;
}
if fp.get_next_free() == 0 {
update_list.remove_last(exp, page, fp.get_prev_free(), check_top_list)?;
} else {
let mut nfp = load_free_page(fl, fp.get_next_free())?;
nfp.set_prev_free(fp.get_prev_free());
flush_free_page(fl, &nfp)?;
}
fl.set_len(page)?;
Ok(true)
} else {
Ok(false)
}
}