use crate::{
error::PERes,
io::{
read_u64, write_u64, ArcSliceRead, InfallibleRead, InfallibleReadFormat, InfallibleWrite,
InfallibleWriteFormat, ReadFormat, WriteFormat,
},
};
use std::{
cmp,
fs::File,
io::{self, Cursor, Read, Seek, SeekFrom, Write},
sync::{Arc, Mutex},
};
pub const PAGE_METADATA_SIZE: u32 = 3;
const SIZE_OFFSET: usize = 0;
const FLAGS_OFFSET: usize = SIZE_OFFSET + 1;
const NEXT_OFFSET: usize = FLAGS_OFFSET + 1;
const PREV_OFFSET: usize = NEXT_OFFSET + 8;
pub trait UpdateList {
fn update(&mut self, exp: u8, page: u64) -> PERes<u64>;
fn remove(&mut self, exp: u8, page: u64, next: u64) -> PERes<()>;
}
pub trait Device: Sync + Send {
fn load_page(&self, page: u64) -> PERes<ReadPage>;
fn load_page_if_exists(&self, page: u64) -> PERes<Option<ReadPage>>;
fn load_page_raw(&self, page: u64, size_exp: u8) -> PERes<Page>;
fn flush_page(&self, page: &Page) -> PERes<()>;
fn create_page_raw(&self, exp: u8) -> PERes<u64>;
fn create_page(&self, exp: u8) -> PERes<Page>;
fn mark_allocated(&self, page: u64) -> PERes<u64>;
fn sync(&self) -> PERes<()>;
fn trim_or_free_page(&self, page: u64, update_list: &mut dyn UpdateList) -> PERes<()>;
fn trim_end_pages(&self, update_list: &mut dyn UpdateList) -> PERes<()>;
}
trait SizeTool {
fn len(&self) -> PERes<u64>;
fn set_len(&mut self, len: u64) -> PERes<()>;
}
impl SizeTool for File {
fn len(&self) -> PERes<u64> {
Ok(self.metadata()?.len())
}
fn set_len(&mut self, len: u64) -> PERes<()> {
Ok(File::set_len(self, len)?)
}
}
impl SizeTool for Cursor<Vec<u8>> {
fn len(&self) -> PERes<u64> {
Ok(self.get_ref().len() as u64)
}
fn set_len(&mut self, len: u64) -> PERes<()> {
self.get_mut().resize(len as usize, 0);
Ok(())
}
}
pub struct ReadPage {
buff: Arc<Vec<u8>>,
index: u64,
exp: u8,
pos: usize,
}
impl ReadPage {
pub fn new(buff: Arc<Vec<u8>>, pos: usize, index: u64, exp: u8) -> ReadPage {
ReadPage { buff, index, exp, pos }
}
}
impl ReadPage {
pub fn get_size_exp(&mut self) -> u8 {
self.exp
}
pub fn slice(&self, len: usize) -> &[u8] {
let final_len = self.pos + len;
debug_assert!(final_len < self.buff.len() - 1);
&self.buff[self.pos..final_len]
}
pub fn arc_slice(&self, len: usize) -> ArcSliceRead {
ArcSliceRead::new(self.buff.clone(), self.pos, self.pos + len)
}
}
impl Read for ReadPage {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let len = self.buff.len() - 1;
let amt = cmp::min(self.pos, len);
let read = Read::read(&mut &self.buff[(amt as usize)..len], buf)?;
self.pos += read;
Ok(read)
}
}
impl InfallibleRead for ReadPage {
fn read_exact(&mut self, buf: &mut [u8]) {
Read::read_exact(self, buf).expect("in memory buff never fail");
}
fn read_slice(&mut self, size: usize) -> ArcSliceRead {
debug_assert!(self.buff.len() - 1 > self.pos + size);
let read = ArcSliceRead::new(self.buff.clone(), self.pos, size);
self.pos += size;
read
}
}
impl PageOps for ReadPage {
fn seek(&mut self, pos: u32) {
debug_assert!(pos + 2 < (1 << self.exp));
self.pos = (pos + 2) as usize;
}
fn get_index(&self) -> u64 {
self.index
}
fn get_size_exp(&self) -> u8 {
self.exp
}
fn clone_read(&self) -> ReadPage {
ReadPage::new(self.buff.clone(), 2, self.index, self.exp)
}
fn clone_write(&self) -> Page {
Page::new(self.buff.as_ref().clone(), 2, self.index, self.exp)
}
fn is_free(&self) -> PERes<bool> {
Ok((self.buff[1] & 0b1000_0000) != 0)
}
fn cursor_pos(&self) -> usize {
if self.pos >= 2 {
self.pos - 2
} else {
0
}
}
}
#[derive(Clone)]
pub struct Page {
buff: Vec<u8>,
index: u64,
exp: u8,
pos: usize,
}
pub trait PageOps {
fn seek(&mut self, pos: u32);
fn get_index(&self) -> u64;
fn get_size_exp(&self) -> u8;
fn clone_read(&self) -> ReadPage;
fn clone_write(&self) -> Page;
fn is_free(&self) -> PERes<bool>;
fn cursor_pos(&self) -> usize;
}
fn free_flag_set(mut cur: u8, free: bool) -> u8 {
if free {
cur |= 0b1000_0000;
} else {
cur &= !0b1000_0000;
}
cur
}
fn is_free(cur: u8) -> bool {
cur & 0b1000_0000 != 0
}
impl Page {
pub fn new(buff: Vec<u8>, pos: usize, index: u64, exp: u8) -> Page {
Page { buff, index, exp, pos }
}
pub fn new_alloc(index: u64, exp: u8) -> Page {
let size = 1 << exp;
let mut buff = vec![0; size];
buff[0] = exp;
buff[1] = free_flag_set(0, false);
buff[size - 1] = exp;
Self::new(buff, 2, index, exp)
}
pub fn clone_resetted(&self) -> Page {
let mut page = self.clone();
page.pos = 2;
page
}
}
impl Read for Page {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let len = self.buff.len() - 1;
let amt = cmp::min(self.pos, len);
let read = Read::read(&mut &self.buff[(amt as usize)..len], buf)?;
self.pos += read;
Ok(read)
}
}
impl InfallibleRead for Page {
fn read_exact(&mut self, buf: &mut [u8]) {
Read::read_exact(self, buf).expect("in memory buff never fail");
}
fn read_slice(&mut self, size: usize) -> ArcSliceRead {
let mut slice: Vec<u8> = vec![0; size];
InfallibleRead::read_exact(self, &mut slice);
ArcSliceRead::new_vec(slice)
}
}
impl Write for Page {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let pre = self.pos;
let len = self.buff.len() - 1;
if pre + (*buf).len() > len {
panic!(
"Over page allowed content size:{}, data size: {}",
len,
pre + (*buf).len()
);
}
let pos = cmp::min(self.pos, len);
let amt = (&mut self.buff[(pos as usize)..len]).write(buf)?;
self.pos += amt;
Ok(amt)
}
fn flush(&mut self) -> io::Result<()> {
self.buff.flush()
}
}
impl InfallibleWrite for Page {
fn write_all(&mut self, buf: &[u8]) {
Write::write_all(self, buf).expect("in memory write should never fail")
}
}
impl PageOps for Page {
fn seek(&mut self, pos: u32) {
debug_assert!(pos + 2 < (1 << self.exp));
self.pos = (pos + 2) as usize;
}
fn get_index(&self) -> u64 {
self.index
}
fn get_size_exp(&self) -> u8 {
self.exp
}
fn clone_read(&self) -> ReadPage {
ReadPage::new(Arc::new(self.buff.clone()), 2, self.index, self.exp)
}
fn clone_write(&self) -> Page {
self.clone_resetted()
}
fn is_free(&self) -> PERes<bool> {
Ok(is_free(self.buff[1]))
}
fn cursor_pos(&self) -> usize {
if self.pos >= 2 {
self.pos - 2
} else {
0
}
}
}
impl Page {
pub fn set_free(&mut self, free: bool) -> PERes<()> {
self.buff[1] = free_flag_set(self.buff[1], free);
Ok(())
}
pub fn set_next_free(&mut self, next: u64) {
let pre = self.pos;
self.pos = NEXT_OFFSET;
InfallibleWriteFormat::write_u64(self, next);
self.pos = pre;
}
pub fn get_next_free(&mut self) -> u64 {
let pre = self.pos;
self.pos = NEXT_OFFSET;
let val = InfallibleReadFormat::read_u64(self);
self.pos = pre;
val
}
pub fn set_prev_free(&mut self, next: u64) {
let pre = self.pos;
self.pos = PREV_OFFSET;
InfallibleWriteFormat::write_u64(self, next);
self.pos = pre;
}
pub fn get_prev_free(&mut self) -> u64 {
let pre = self.pos;
self.pos = PREV_OFFSET;
let val = InfallibleReadFormat::read_u64(self);
self.pos = pre;
val
}
pub fn reset(&mut self) -> PERes<()> {
self.buff = vec![0; self.buff.len()];
self.buff[0] = self.exp;
let end = self.buff.len() - 1;
self.buff[end] = self.exp;
Ok(())
}
pub fn make_read(self) -> ReadPage {
ReadPage::new(Arc::new(self.buff), 2, self.index, self.exp)
}
}
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))
}
fn create_page_raw<T: Write + Seek>(fl: &mut T, exp: u8) -> PERes<u64> {
let size = 1 << exp; let ve = vec![0u8; size];
let offset = fl.seek(SeekFrom::End(0))?;
fl.write_all(&ve)?;
Ok(offset)
}
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))
}
fn flush_page<T: Write + Seek>(fl: &mut T, page: &Page) -> PERes<()> {
fl.seek(SeekFrom::Start(page.index))?;
fl.write_all(&page.buff)?;
Ok(())
}
fn create_page<T: Write + Seek>(fl: &mut T, exp: u8) -> 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))?;
fl.write_all(&ve)?;
Ok(Page::new(ve, 2, offset, exp))
}
fn mark_allocated<T: Write + Read + Seek>(fl: &mut T, page: u64) -> PERes<u64> {
fl.seek(SeekFrom::Start(page + FLAGS_OFFSET as u64))?;
let mut moderator = fl.read_u8()?;
fl.seek(SeekFrom::Start(page + NEXT_OFFSET as u64))?;
let next = fl.read_u64()?;
moderator = free_flag_set(moderator, false);
fl.seek(SeekFrom::Start(page + 1))?;
fl.write_u8(moderator)?;
fl.write_u64(0)?; fl.write_u64(0)?; if next != 0 {
fl.seek(SeekFrom::Start(next + PREV_OFFSET as u64))?;
fl.write_u64(0)?;
}
Ok(next)
}
fn trim_or_free_page<T: Write + Read + Seek + SizeTool>(
fl: &mut T,
page: u64,
update_list: &mut dyn UpdateList,
) -> PERes<()> {
fl.seek(SeekFrom::Start(page))?;
let exp = fl.read_u8()?;
let mut mitigator = fl.read_u8()?;
debug_assert!(!is_free(mitigator), "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)? {}
} else {
let next = update_list.update(exp, page)?;
fl.seek(SeekFrom::Start(page + 1))?;
mitigator = free_flag_set(mitigator, true);
let mut data: [u8; 9] = [0; 9];
data[0] = mitigator;
write_u64(&mut data[1..9], next);
fl.seek(SeekFrom::Start(page + 1))?;
fl.write_all(&data)?;
fl.write_all(&[0u8; 8])?;
if next != 0 {
let mut data = [0; 8];
write_u64(&mut data, page);
fl.seek(SeekFrom::Start(next + PREV_OFFSET as u64))?;
fl.write_all(&data)?;
}
}
Ok(())
}
fn check_and_trim<T: Write + Read + Seek + SizeTool>(fl: &mut T, update_list: &mut dyn UpdateList) -> 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 next = read_u64(&header[NEXT_OFFSET..(NEXT_OFFSET + 8)]);
let prev = read_u64(&header[PREV_OFFSET..(PREV_OFFSET + 8)]);
if prev == 0 {
update_list.remove(exp, page, next)?;
} else {
let mut data = [0u8; 8];
write_u64(&mut data, next);
fl.seek(SeekFrom::Start(prev + NEXT_OFFSET as u64))?;
fl.write_all(&data)?;
}
if next != 0 {
let mut data = [0u8; 8];
write_u64(&mut data, prev);
fl.seek(SeekFrom::Start(next + PREV_OFFSET as u64))?;
fl.write_all(&data)?;
}
fl.set_len(page)?;
Ok(true)
} else {
Ok(false)
}
}
#[cfg(unix)]
pub struct DiscRef {
file: File,
size: Mutex<u64>,
}
#[cfg(unix)]
impl DiscRef {
pub fn new(file: File) -> PERes<DiscRef> {
let len = file.len()?;
Ok(DiscRef {
file,
size: Mutex::new(len),
})
}
fn create_page_offset(&self, size: u64) -> PERes<u64> {
let mut current = self.size.lock()?;
let page = *current;
*current += size;
Ok(page)
}
fn trim_if_possible(&self, page: u64, size: u64) -> PERes<bool> {
let mut current = self.size.lock()?;
if page + size == *current {
*current = page;
self.file.set_len(page)?;
Ok(true)
} else {
Ok(false)
}
}
fn check_and_trim(&self, update_list: &mut dyn UpdateList) -> PERes<bool> {
use std::os::unix::prelude::FileExt;
let mut lock = self.size.lock()?;
let value = *lock;
if value == 0 {
return Ok(false);
}
let mut exp = [0u8];
self.file.read_exact_at(&mut exp, value - 1)?;
if exp[0] == 0 {
return Ok(false);
}
let size = (1 << exp[0]) as u64; let mut header = [0u8; 18];
let page = value - size;
if page == 0 {
return Ok(false);
}
self.file.read_exact_at(&mut header, page)?;
if is_free(header[1]) {
let next = read_u64(&header[NEXT_OFFSET..(NEXT_OFFSET + 8)]);
let prev = read_u64(&header[PREV_OFFSET..(PREV_OFFSET + 8)]);
if prev == 0 {
update_list.remove(exp[0], page, next)?;
} else {
let mut data = [0u8; 8];
write_u64(&mut data, next);
self.file.write_all_at(&data, prev + NEXT_OFFSET as u64)?;
}
if next != 0 {
let mut data = [0u8; 8];
write_u64(&mut data, prev);
self.file.write_all_at(&data, next + PREV_OFFSET as u64)?;
}
*lock = page;
self.file.set_len(page)?;
Ok(true)
} else {
Ok(false)
}
}
}
#[cfg(unix)]
impl Device for DiscRef {
fn load_page(&self, page: u64) -> PERes<ReadPage> {
use std::os::unix::prelude::FileExt;
let mut exp = [0u8];
self.file.read_exact_at(&mut exp, page)?;
let size = 1 << exp[0]; let mut ve = vec![0u8; size];
ve[0] = exp[0];
self.file.read_exact_at(&mut ve[1..size], page + 1)?;
Ok(ReadPage::new(Arc::new(ve), 2, page, exp[0]))
}
fn load_page_if_exists(&self, page: u64) -> PERes<Option<ReadPage>> {
{
if *self.size.lock()? <= page {
return Ok(None);
}
}
Ok(Some(self.load_page(page)?))
}
fn load_page_raw(&self, page: u64, size_exp: u8) -> PERes<Page> {
use std::os::unix::prelude::FileExt;
let size = 1 << size_exp; let mut ve = vec![0u8; size];
self.file.read_exact_at(&mut ve, page)?;
Ok(Page::new(ve, 0, page, size_exp))
}
fn flush_page(&self, page: &Page) -> PERes<()> {
use std::os::unix::prelude::FileExt;
self.file.write_all_at(&page.buff, page.get_index())?;
Ok(())
}
fn create_page_raw(&self, exp: u8) -> PERes<u64> {
use std::os::unix::prelude::FileExt;
let size = (1 << exp) as usize; let offset = self.create_page_offset(size as u64)?;
let ve = vec![0u8; size];
self.file.write_all_at(&ve, offset)?; Ok(offset)
}
fn create_page(&self, exp: u8) -> PERes<Page> {
use std::os::unix::prelude::FileExt;
let size = (1 << exp) as usize; let offset = self.create_page_offset(size as u64)?;
let mut ve = vec![0u8; size];
ve[0] = exp;
ve[size - 1] = exp;
self.file.write_all_at(&ve, offset)?; Ok(Page::new(ve, 2, offset, exp))
}
fn mark_allocated(&self, page: u64) -> PERes<u64> {
use std::os::unix::prelude::FileExt;
let mut mitigator_and_next = [0u8; 9]; self.file.read_exact_at(&mut mitigator_and_next, page + 1)?;
debug_assert!(
is_free(mitigator_and_next[0]),
"allocating: {} already allocated ",
page
);
let mut mitigator = [0u8; 17]; mitigator[0] = free_flag_set(mitigator_and_next[0], false);
self.file.write_all_at(&mitigator, page + 1)?;
let next_page = read_u64(&mitigator_and_next[1..]);
if next_page != 0 {
let prev_data = [0u8; 8];
self.file.write_all_at(&prev_data, next_page + PREV_OFFSET as u64)?;
}
Ok(next_page)
}
fn sync(&self) -> PERes<()> {
self.file.sync_data()?;
Ok(())
}
fn trim_or_free_page(&self, page: u64, update_list: &mut dyn UpdateList) -> PERes<()> {
use std::os::unix::prelude::FileExt;
let mut exp = [0u8];
self.file.read_exact_at(&mut exp, page)?;
let size = (1 << exp[0]) as u64; let mut mitigator = [0u8];
self.file.read_exact_at(&mut mitigator, page + 1)?;
debug_assert!(!is_free(mitigator[0]), "freeing: {} already freed ", page);
if !self.trim_if_possible(page, size)? {
let old = update_list.update(exp[0], page)?;
mitigator[0] = free_flag_set(mitigator[0], true);
let mut data = [0u8; 17];
data[0] = mitigator[0];
write_u64(&mut data[1..9], old);
self.file.write_all_at(&data, page + 1)?;
if old != 0 {
let mut data = [0; 8];
write_u64(&mut data, page);
self.file.write_all_at(&data, old + PREV_OFFSET as u64)?;
}
} else {
self.trim_end_pages(update_list)?;
}
Ok(())
}
fn trim_end_pages(&self, update_list: &mut dyn UpdateList) -> PERes<()> {
while self.check_and_trim(update_list)? {}
Ok(())
}
}
#[cfg(not(unix))]
pub struct FileHandler {
file: File,
metadata_changed: bool,
}
#[cfg(not(unix))]
pub struct DiscRef {
file: Mutex<FileHandler>,
}
#[cfg(not(unix))]
impl DiscRef {
pub fn new(file: File) -> PERes<DiscRef> {
Ok(DiscRef {
file: Mutex::new(FileHandler {
file,
metadata_changed: false,
}),
})
}
}
#[cfg(not(unix))]
impl Device for DiscRef {
fn load_page(&self, page: u64) -> PERes<ReadPage> {
load_page(&mut self.file.lock()?.file, page)
}
fn load_page_if_exists(&self, page: u64) -> PERes<Option<ReadPage>> {
let mut lock = self.file.lock()?;
if lock.file.len()? <= page {
return Ok(None);
}
Ok(Some(load_page(&mut lock.file, page)?))
}
fn load_page_raw(&self, page: u64, size_exp: u8) -> PERes<Page> {
load_page_raw(&mut self.file.lock()?.file, page, size_exp)
}
fn flush_page(&self, page: &Page) -> PERes<()> {
flush_page(&mut self.file.lock()?.file, page)
}
fn create_page_raw(&self, exp: u8) -> PERes<u64> {
let lock = &mut self.file.lock()?;
lock.metadata_changed = true;
create_page_raw(&mut lock.file, exp)
}
fn create_page(&self, exp: u8) -> PERes<Page> {
let lock = &mut self.file.lock()?;
lock.metadata_changed = true;
create_page(&mut lock.file, exp)
}
fn mark_allocated(&self, page: u64) -> PERes<u64> {
mark_allocated(&mut self.file.lock()?.file, page)
}
fn sync(&self) -> PERes<()> {
let to_sync;
let metadata_changed;
{
let mut lock = self.file.lock()?;
to_sync = lock.file.try_clone()?;
metadata_changed = lock.metadata_changed;
lock.metadata_changed = false;
}
if metadata_changed {
to_sync.sync_all()?;
} else {
to_sync.sync_data()?;
}
Ok(())
}
fn trim_or_free_page(&self, page: u64, update_list: &mut dyn UpdateList) -> PERes<()> {
trim_or_free_page(&mut self.file.lock()?.file, page, update_list)
}
fn trim_end_pages(&self, update_list: &mut dyn UpdateList) -> PERes<()> {
while check_and_trim(&mut self.file.lock()?.file, update_list)? {}
Ok(())
}
}
pub struct MemRef {
data: Mutex<Cursor<Vec<u8>>>,
}
impl MemRef {
pub fn new() -> PERes<MemRef> {
Ok(MemRef {
data: Mutex::new(Cursor::new(Vec::new())),
})
}
}
impl Device for MemRef {
fn load_page(&self, page: u64) -> PERes<ReadPage> {
load_page(&mut *self.data.lock()?, page)
}
fn load_page_if_exists(&self, page: u64) -> PERes<Option<ReadPage>> {
let mut data = self.data.lock()?;
if data.len()? <= page {
return Ok(None);
}
Ok(Some(load_page(&mut *data, page)?))
}
fn load_page_raw(&self, page: u64, size_exp: u8) -> PERes<Page> {
load_page_raw(&mut *self.data.lock()?, page, size_exp)
}
fn flush_page(&self, page: &Page) -> PERes<()> {
flush_page(&mut *self.data.lock()?, page)
}
fn create_page_raw(&self, exp: u8) -> PERes<u64> {
create_page_raw(&mut *self.data.lock()?, exp)
}
fn create_page(&self, exp: u8) -> PERes<Page> {
create_page(&mut *self.data.lock()?, exp)
}
fn mark_allocated(&self, page: u64) -> PERes<u64> {
mark_allocated(&mut *self.data.lock()?, page)
}
fn sync(&self) -> PERes<()> {
Ok(())
}
fn trim_or_free_page(&self, page: u64, update_list: &mut dyn UpdateList) -> PERes<()> {
trim_or_free_page(&mut *self.data.lock()?, page, update_list)
}
fn trim_end_pages(&self, update_list: &mut dyn UpdateList) -> PERes<()> {
while check_and_trim(&mut *self.data.lock()?, update_list)? {}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Device, DiscRef, MemRef, PageOps, UpdateList};
use crate::error::PERes;
use std::io::{Read, Write};
use tempfile::Builder;
fn temp_disc_ref(name: &str) -> DiscRef {
DiscRef::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_page(page).unwrap().clone_write();
pg.set_next_free(30);
device.flush_page(pg).unwrap();
let pg1 = &mut device.load_page(page).unwrap().clone_write();
let val = pg1.get_next_free();
assert_eq!(val, 30);
}
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) -> 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) -> 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());
}
#[derive(Default)]
struct PrevCase {
pages: [u64; 32],
}
impl UpdateList for PrevCase {
fn update(&mut self, pos: u8, val: u64) -> PERes<u64> {
let prev = self.pages[pos as usize];
self.pages[pos as usize] = val;
Ok(prev)
}
fn remove(&mut self, pos: u8, _page: u64, next: u64) -> PERes<()> {
self.pages[pos as usize] = next;
Ok(())
}
}
fn free_tracking_device(device: &mut dyn Device) {
let mut update_list = PrevCase::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.pages[5], page);
device.trim_or_free_page(second_page, &mut update_list).unwrap();
assert_eq!(update_list.pages[5], second_page);
let prev = device.mark_allocated(second_page).unwrap();
assert_eq!(prev, page);
let prev = device.mark_allocated(page).unwrap();
assert_eq!(prev, 0);
update_list.pages[5] = 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.pages[5], page);
device.trim_or_free_page(second_page, &mut update_list).unwrap();
assert_eq!(update_list.pages[5], 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 = PrevCase::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.pages[5], first_page);
device.trim_or_free_page(third_page, &mut update_list).unwrap();
assert_eq!(update_list.pages[5], third_page);
device.trim_or_free_page(forth_page, &mut update_list).unwrap();
assert_eq!(update_list.pages[5], first_page);
let mut load_page = device.load_page(first_page).unwrap().clone_write();
assert_eq!(load_page.get_next_free(), 0);
assert_eq!(load_page.get_prev_free(), 0);
}
#[test]
fn create_load_flush_page_disc() {
create_load_flush_page_device(&mut temp_disc_ref("disc_ref.raw"));
}
#[test]
fn create_load_flush_page_memory() {
create_load_flush_page_device(&mut MemRef::new().unwrap());
}
#[test]
fn set_get_next_free_disc() {
set_get_next_free_device(&mut temp_disc_ref("set_free.raw"));
}
#[test]
fn set_get_next_free_memory() {
set_get_next_free_device(&mut MemRef::new().unwrap());
}
#[test]
fn get_size_page_disc() {
get_size_page_device(&mut temp_disc_ref("get_size.raw"));
}
#[test]
fn get_size_page_memory() {
get_size_page_device(&mut MemRef::new().unwrap());
}
#[test]
fn write_read_page_disc() {
write_read_page_device(&mut temp_disc_ref("write_read.raw"));
}
#[test]
fn write_read_page_memory() {
write_read_page_device(&mut MemRef::new().unwrap());
}
#[test]
fn create_load_trim_disc() {
create_load_trim_device(&mut temp_disc_ref("disc_ref_trim.raw"));
}
#[test]
fn create_load_trim_memory() {
create_load_trim_device(&mut MemRef::new().unwrap());
}
#[test]
fn create_two_load_trim_disc() {
create_load_two_trim_device(&mut temp_disc_ref("disc_ref_two_trim.raw"));
}
#[test]
fn create_two_load_trim_memory() {
create_load_two_trim_device(&mut MemRef::new().unwrap());
}
#[test]
fn create_not_trim_not_last_disc() {
create_not_trim_not_last_device(&mut temp_disc_ref("disc_ref_no_trim.raw"));
}
#[test]
fn create_not_trim_not_last_memory() {
create_not_trim_not_last_device(&mut MemRef::new().unwrap());
}
#[test]
fn create_reuse_page_disc() {
create_reuse_page_device(&mut temp_disc_ref("disc_ref_no_trim.raw"));
}
#[test]
fn create_reuse_page_memory() {
create_reuse_page_device(&mut MemRef::new().unwrap());
}
#[test]
fn create_reuse_trim_page_disc() {
create_reuse_trim_page_device(&mut temp_disc_ref("disc_ref_no_trim.raw"));
}
#[test]
fn create_reuse_trim_page_memory() {
create_reuse_trim_page_device(&mut MemRef::new().unwrap());
}
#[test]
fn free_tracking_disc() {
free_tracking_device(&mut temp_disc_ref("disc_ref_no_trim.raw"));
}
#[test]
fn free_tracking_memory() {
free_tracking_device(&mut MemRef::new().unwrap());
}
#[test]
fn free_tracking_on_trim_disc() {
free_tracking_on_trim_device(&mut temp_disc_ref("disc_ref_trim.raw"));
}
#[test]
fn free_tracking_on_trim_memory() {
free_tracking_on_trim_device(&mut MemRef::new().unwrap());
}
}