persy 1.8.0

Transactional Persistence Engine
Documentation
use super::{Device, FileDevice, MemoryDevice, PageOps, UpdateList};
use crate::allocator::free_list::{FreeList, SKIPPED_EXP};
use crate::error::PERes;
use std::io::{Read, Write};
use tempfile::Builder;

fn temp_disk_ref(name: &str) -> FileDevice {
    FileDevice::new(
        Builder::new()
            .prefix(name)
            .suffix(".persy")
            .tempfile()
            .unwrap()
            .reopen()
            .unwrap(),
    )
    .unwrap()
}

fn create_load_flush_page_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let pg = device.load_page(page).unwrap();
    device.flush_page(&mut pg.clone_write()).unwrap();
}

fn set_get_next_free_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let pg = &mut device.load_free_page(page).unwrap();
    pg.set_next_free(30);
    device.flush_free_page(&pg).unwrap();
    let pg1 = &mut device.load_free_page(page).unwrap();
    let val = pg1.get_next_free();
    assert_eq!(val, 30);
}

fn free_page_load_flush_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let pg = &mut device.load_free_page(page).unwrap();
    pg.set_next_free(30);
    pg.set_prev_free(40);
    device.flush_free_page(pg).unwrap();
    let pg1 = &mut device.load_free_page(page).unwrap();
    let val = pg1.get_next_free();
    assert_eq!(val, 30);
    let val = pg1.get_prev_free();
    assert_eq!(val, 40);
}

fn get_size_page_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let pg = &mut device.load_page(page).unwrap();
    let sz = pg.get_size_exp();
    assert_eq!(sz, 5);
}

fn write_read_page_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    {
        let pg = &mut device.load_page(page).unwrap().clone_write();
        pg.write_all(&[10]).unwrap();
        device.flush_page(pg).unwrap();
    }
    {
        let pg = &mut device.load_page(page).unwrap();
        let mut data = [0; 1];
        pg.read_exact(&mut data).unwrap();
        assert_eq!(data[0], 10);
        let sz = pg.get_size_exp();
        assert_eq!(sz, 5);
    }
}

struct PanicCase {}
impl UpdateList for PanicCase {
    fn update(&mut self, _: u8, _: u64) -> PERes<u64> {
        panic!("should not put the free in the free list")
    }
    fn remove(&mut self, _: u8, _: u64, _: u64, _: bool) -> PERes<()> {
        panic!("should not put the free in the free list")
    }
    fn remove_last(&mut self, _: u8, _: u64, _: u64, _: bool) -> PERes<()> {
        panic!("should not put the free in the free list")
    }
}

fn create_load_trim_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let pg = &mut device.load_page(page).unwrap().clone_write();
    device.flush_page(pg).unwrap();
    device.trim_or_free_page(page, &mut PanicCase {}).unwrap();
    assert!(device.load_page(page).is_err());
}

fn create_load_two_trim_device(device: &mut dyn Device) {
    let _not_free = device.create_page(5).unwrap().get_index();
    let page = device.create_page(5).unwrap().get_index();
    let pg = &mut device.load_page(page).unwrap().clone_write();
    device.flush_page(pg).unwrap();
    let page_1 = device.create_page(5).unwrap().get_index();
    let pg = &mut device.load_page(page_1).unwrap().clone_write();
    device.flush_page(pg).unwrap();
    device.trim_or_free_page(page, &mut IgnoreCase {}).unwrap();
    device.trim_or_free_page(page_1, &mut IgnoreCase {}).unwrap();
    assert!(device.load_page(page).is_err());
}

struct IgnoreCase {}
impl UpdateList for IgnoreCase {
    fn update(&mut self, _: u8, _: u64) -> PERes<u64> {
        Ok(0)
    }
    fn remove(&mut self, _: u8, _: u64, _: u64, _: bool) -> PERes<()> {
        Ok(())
    }
    fn remove_last(&mut self, _: u8, _: u64, _: u64, _: bool) -> PERes<()> {
        Ok(())
    }
}

fn create_not_trim_not_last_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let _page_after = device.create_page(5).unwrap();
    let pg = &mut device.load_page(page).unwrap().clone_write();
    device.flush_page(pg).unwrap();
    device.trim_or_free_page(page, &mut IgnoreCase {}).unwrap();
    let load_page = device.load_page(page);
    assert!(load_page.is_ok());
    assert!(load_page.unwrap().is_free().unwrap());
}

fn create_reuse_page_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let _page_after = device.create_page(5).unwrap();
    let pg = &mut device.load_page(page).unwrap().clone_write();
    device.flush_page(pg).unwrap();
    device.trim_or_free_page(page, &mut IgnoreCase {}).unwrap();
    let load_page = device.load_page(page);
    assert!(load_page.unwrap().is_free().unwrap());
    device.mark_allocated(page).unwrap();
    let load_page = device.load_page(page);
    assert!(!load_page.unwrap().is_free().unwrap());
    device.trim_or_free_page(page, &mut IgnoreCase {}).unwrap();
    let load_page = device.load_page(page);
    assert!(load_page.is_ok());
    assert!(load_page.unwrap().is_free().unwrap());
}

fn create_reuse_trim_page_device(device: &mut dyn Device) {
    let page = device.create_page(5).unwrap().get_index();
    let free_after = device.create_page(5).unwrap();
    let pg = &mut device.load_page(page).unwrap().clone_write();
    device.flush_page(pg).unwrap();
    device.trim_or_free_page(page, &mut IgnoreCase {}).unwrap();
    let load_page = device.load_page(page);
    assert!(load_page.unwrap().is_free().unwrap());
    device.mark_allocated(page).unwrap();
    let load_page = device.load_page(page);
    assert!(!load_page.unwrap().is_free().unwrap());
    device
        .trim_or_free_page(free_after.get_index(), &mut IgnoreCase {})
        .unwrap();
    device.trim_or_free_page(page, &mut IgnoreCase {}).unwrap();
    let load_page = device.load_page(page);
    assert!(load_page.is_err());
}

fn free_tracking_device(device: &mut dyn Device) {
    let mut update_list = FreeList::default();
    let _first_0 = device.create_page(5).unwrap().get_index();
    let page = device.create_page(5).unwrap().get_index();
    let second_page = device.create_page(5).unwrap().get_index();
    let free_to_clean = device.create_page(5).unwrap().get_index();
    device.trim_or_free_page(page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], page);
    device.trim_or_free_page(second_page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], second_page);
    let next = device.mark_allocated(second_page).unwrap();
    assert_eq!(next, 0);
    let next = device.mark_allocated(page).unwrap();
    assert_eq!(next, second_page);
    update_list.heads[5 - SKIPPED_EXP] = 0;
    update_list.tails[5 - SKIPPED_EXP] = 0;
    device.trim_or_free_page(free_to_clean, &mut update_list).unwrap();
    device.trim_or_free_page(page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], page);
    device.trim_or_free_page(second_page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], 0);
    let load_page = device.load_page(page);
    assert!(load_page.is_err());
    let load_page = device.load_page_if_exists(page).unwrap();
    assert!(load_page.is_none());
    let load_page = device.load_page(second_page);
    assert!(load_page.is_err());
    let load_page = device.load_page_if_exists(second_page).unwrap();
    assert!(load_page.is_none());
}

fn free_tracking_on_trim_device(device: &mut dyn Device) {
    let mut update_list = FreeList::default();
    let _place_older_0 = device.create_page(5).unwrap().get_index();
    let first_page = device.create_page(5).unwrap().get_index();
    let _second_page = device.create_page(5).unwrap().get_index();
    let third_page = device.create_page(5).unwrap().get_index();
    let forth_page = device.create_page(5).unwrap().get_index();
    device.trim_or_free_page(first_page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], first_page);
    assert_eq!(update_list.tails[5 - SKIPPED_EXP], first_page);
    device.trim_or_free_page(third_page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], third_page);
    assert_eq!(update_list.tails[5 - SKIPPED_EXP], first_page);
    device.trim_or_free_page(forth_page, &mut update_list).unwrap();
    assert_eq!(update_list.heads[5 - SKIPPED_EXP], first_page);
    assert_eq!(update_list.tails[5 - SKIPPED_EXP], first_page);
    let load_page = device.load_free_page(first_page).unwrap();
    assert_eq!(load_page.get_next_free(), 0);
    assert_eq!(load_page.get_prev_free(), 0);
}

#[test]
fn create_load_flush_page_disk() {
    create_load_flush_page_device(&mut temp_disk_ref("disk_ref.raw"));
}

#[test]
fn create_load_flush_page_memory() {
    create_load_flush_page_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn set_get_next_free_disk() {
    set_get_next_free_device(&mut temp_disk_ref("set_free.raw"));
}

#[test]
fn set_get_next_free_memory() {
    set_get_next_free_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn get_size_page_disk() {
    get_size_page_device(&mut temp_disk_ref("get_size.raw"));
}
#[test]
fn get_size_page_memory() {
    get_size_page_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn write_read_page_disk() {
    write_read_page_device(&mut temp_disk_ref("write_read.raw"));
}
#[test]
fn write_read_page_memory() {
    write_read_page_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn create_load_trim_disk() {
    create_load_trim_device(&mut temp_disk_ref("disk_ref_trim.raw"));
}
#[test]
fn create_load_trim_memory() {
    create_load_trim_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn create_two_load_trim_disk() {
    create_load_two_trim_device(&mut temp_disk_ref("disk_ref_two_trim.raw"));
}
#[test]
fn create_two_load_trim_memory() {
    create_load_two_trim_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn create_not_trim_not_last_disk() {
    create_not_trim_not_last_device(&mut temp_disk_ref("disk_ref_no_trim.raw"));
}

#[test]
fn create_not_trim_not_last_memory() {
    create_not_trim_not_last_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn create_reuse_page_disk() {
    create_reuse_page_device(&mut temp_disk_ref("disk_ref_no_trim.raw"));
}

#[test]
fn create_reuse_page_memory() {
    create_reuse_page_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn create_reuse_trim_page_disk() {
    create_reuse_trim_page_device(&mut temp_disk_ref("disk_ref_no_trim.raw"));
}

#[test]
fn create_reuse_trim_page_memory() {
    create_reuse_trim_page_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn free_tracking_disk() {
    free_tracking_device(&mut temp_disk_ref("disk_ref_no_trim.raw"));
}

#[test]
fn free_tracking_memory() {
    free_tracking_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn free_tracking_on_trim_disk() {
    free_tracking_on_trim_device(&mut temp_disk_ref("disk_ref_trim.raw"));
}

#[test]
fn free_tracking_on_trim_memory() {
    free_tracking_on_trim_device(&mut MemoryDevice::new(None).unwrap());
}

#[test]
fn free_page_load_flush_disk() {
    free_page_load_flush_device(&mut temp_disk_ref("disk_load_flush_free.raw"));
}

#[test]
fn free_page_load_flush_memory() {
    free_page_load_flush_device(&mut MemoryDevice::new(None).unwrap());
}