persy 1.8.0

Transactional Persistence Engine
Documentation
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 {
            //TODO: use specific error
            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> {
    // add 2 to skip the metadata
    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)
    }
}