use page::{Page, PAGE_SIZE};
use error::Error;
use pref::PRef;
use std::cmp::min;
use std::io::{self, ErrorKind};
pub trait PagedFile : Send + Sync {
fn read_page(&self, pref: PRef) -> Result<Option<Page>, Error>;
fn len(&self) -> Result<u64, Error>;
fn truncate(&mut self, new_len: u64) -> Result<(), Error>;
fn sync(&self) -> Result<(), Error>;
fn shutdown(&mut self);
fn append_page(&mut self, page: Page) -> Result<(), Error>;
fn update_page(&mut self, page: Page) -> Result<u64, Error>;
fn flush(&mut self) -> Result<(), Error>;
}
pub trait PagedFileRead {
fn read(&self, pos: PRef, buf: &mut [u8]) -> Result<PRef, Error>;
}
pub trait PagedFileWrite {
fn append(&mut self, buf: &[u8]) -> Result<PRef, Error>;
}
pub struct PagedFileAppender {
file: Box<dyn PagedFile>,
pos: PRef,
page: Option<Page>
}
impl PagedFileAppender {
pub fn new(file: Box<dyn PagedFile>, pos: PRef) -> PagedFileAppender {
PagedFileAppender {file, pos, page: None}
}
pub fn position(&self) -> PRef {
self.pos
}
pub fn append(&mut self, buf: &[u8]) -> Result<PRef, Error> {
let mut wrote = 0;
while wrote < buf.len() {
if self.page.is_none() {
self.page = Some(Page::new());
}
if let Some(ref mut page) = self.page {
let space = min(PAGE_SIZE - self.pos.in_page_pos(), buf.len() - wrote);
page.write(self.pos.in_page_pos(), &buf[wrote..wrote + space]);
wrote += space;
if self.pos.in_page_pos() + space == PAGE_SIZE {
self.file.append_page(page.clone())?;
}
self.pos += space as u64;
}
if self.pos.in_page_pos() == 0 {
self.page = None;
}
}
Ok(self.pos)
}
pub fn read(&self, mut pos: PRef, buf: &mut [u8], len: usize) -> Result<PRef, Error> {
let mut read = 0;
while read < len {
if let Some(ref page) = self.read_page(pos.this_page())? {
let have = min(PAGE_SIZE - pos.in_page_pos(), len - read);
page.read(pos.in_page_pos(), &mut buf[read .. read + have]);
read += have;
pos += have as u64;
}
else {
return Err(Error::IO(io::Error::from(ErrorKind::UnexpectedEof)));
}
}
Ok(pos)
}
}
impl PagedFile for PagedFileAppender {
fn read_page(&self, pref: PRef) -> Result<Option<Page>, Error> {
if let Some(ref page) = self.page {
if pref.this_page() == self.pos.this_page() {
return Ok(Some(page.clone()));
}
}
self.file.read_page(pref)
}
fn len(&self) -> Result<u64, Error> {
self.file.len()
}
fn truncate(&mut self, new_len: u64) -> Result<(), Error> {
self.pos = PRef::from(new_len);
self.file.truncate(new_len)
}
fn sync(&self) -> Result<(), Error> {
self.file.sync()
}
fn shutdown(&mut self) {
self.file.shutdown()
}
fn append_page(&mut self, page: Page) -> Result<(), Error> {
self.file.append_page(page)
}
fn update_page(&mut self, _: Page) -> Result<u64, Error> {
unimplemented!()
}
fn flush(&mut self) -> Result<(), Error> {
if let Some(ref mut page) = self.page {
if self.pos.in_page_pos() > 0 {
self.file.append_page(page.clone())?;
self.pos += PAGE_SIZE as u64 - self.pos.in_page_pos() as u64;
}
}
self.page = None;
Ok(self.file.flush()?)
}
}
pub struct PagedFileIterator<'file> {
pagenumber: u64,
file: &'file dyn PagedFile
}
impl<'file> PagedFileIterator<'file> {
pub fn new(file: &'file dyn PagedFile, pref: PRef) -> PagedFileIterator {
PagedFileIterator {pagenumber: pref.page_number(), file}
}
}
impl<'file> Iterator for PagedFileIterator<'file> {
type Item = Page;
fn next(&mut self) -> Option<Self::Item> {
if self.pagenumber <= (1 << 35) / PAGE_SIZE as u64 {
let pref = PRef::from((self.pagenumber)* PAGE_SIZE as u64);
if let Ok(Some(page)) = self.file.read_page(pref) {
self.pagenumber += 1;
return Some(page);
}
}
None
}
}