use page::{Page, PAGE_SIZE, PAGE_PAYLOAD_SIZE};
use error::HammersbaldError;
use pref::PRef;
use std::cmp::min;
pub trait PagedFile : Send + Sync {
fn read_page (&self, pref: PRef) -> Result<Option<Page>, HammersbaldError>;
fn len (&self) -> Result<u64, HammersbaldError>;
fn truncate(&mut self, new_len: u64) -> Result<(), HammersbaldError>;
fn sync (&self) -> Result<(), HammersbaldError>;
fn shutdown (&mut self);
fn append_page (&mut self, page: Page) -> Result<(), HammersbaldError>;
fn update_page (&mut self, page: Page) -> Result<u64, HammersbaldError>;
fn flush(&mut self) -> Result<(), HammersbaldError>;
}
pub trait PagedFileRead {
fn read(&self, pos: PRef, buf: &mut [u8]) -> Result<PRef, HammersbaldError>;
}
pub trait PagedFileWrite {
fn append(&mut self, buf: &[u8]) -> Result<PRef, HammersbaldError>;
}
pub struct PagedFileAppender {
file: Box<PagedFile>,
pos: PRef,
page: Option<Page>,
lep: PRef
}
impl PagedFileAppender {
pub fn new (file: Box<PagedFile>, pos: PRef, lep: PRef) -> PagedFileAppender {
PagedFileAppender {file, pos, page: None, lep}
}
pub fn position (&self) -> PRef {
self.pos
}
pub fn lep (&self) -> PRef {
self.lep
}
pub fn advance (&mut self) {
self.lep = self.pos;
}
pub fn append(&mut self, buf: &[u8]) -> Result<PRef, HammersbaldError> {
let mut wrote = 0;
while wrote < buf.len() {
if self.page.is_none () {
self.page = Some(Page::new(self.lep));
}
if let Some(ref mut page) = self.page {
let space = min(PAGE_PAYLOAD_SIZE - self.pos.in_page_pos(), buf.len() - wrote);
page.write(self.pos.in_page_pos(), &buf[wrote..wrote + space]);
wrote += space;
self.pos += space as u64;
if self.pos.in_page_pos() == PAGE_PAYLOAD_SIZE {
page.write_pref(PAGE_PAYLOAD_SIZE, self.lep);
self.file.append_page(page.clone())?;
self.pos += (PAGE_SIZE - PAGE_PAYLOAD_SIZE) 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]) -> Result<PRef, HammersbaldError> {
let mut read = 0;
while read < buf.len() {
if let Some(ref page) = self.read_page(pos.this_page())? {
let have = min(PAGE_PAYLOAD_SIZE - pos.in_page_pos(), buf.len() - read);
page.read(pos.in_page_pos(), &mut buf[read .. read + have]);
read += have;
pos += have as u64;
if pos.in_page_pos() == PAGE_PAYLOAD_SIZE {
pos += (PAGE_SIZE - PAGE_PAYLOAD_SIZE) as u64;
}
}
else {
break;
}
}
Ok(pos)
}
}
impl PagedFile for PagedFileAppender {
fn read_page(&self, pref: PRef) -> Result<Option<Page>, HammersbaldError> {
if let Some(ref page) = self.page {
if self.pos.this_page() == pref {
return Ok(Some(page.clone()))
}
}
return self.file.read_page(pref)
}
fn len(&self) -> Result<u64, HammersbaldError> {
self.file.len()
}
fn truncate(&mut self, new_len: u64) -> Result<(), HammersbaldError> {
if new_len >= PAGE_SIZE as u64 {
if let Some(last_page) = self.file.read_page(PRef::from(new_len - PAGE_SIZE as u64))? {
self.lep = last_page.read_pref(PAGE_PAYLOAD_SIZE);
}
else {
return Err(HammersbaldError::Corrupted("where is the last page?".to_string()));
}
}
else {
self.lep = PRef::invalid();
}
self.pos = PRef::from(new_len);
self.file.truncate(new_len)
}
fn sync(&self) -> Result<(), HammersbaldError> {
self.file.sync()
}
fn shutdown(&mut self) {
self.file.shutdown()
}
fn append_page(&mut self, page: Page) -> Result<(), HammersbaldError> {
self.file.append_page(page)
}
fn update_page(&mut self, _: Page) -> Result<u64, HammersbaldError> {
unimplemented!()
}
fn flush(&mut self) -> Result<(), HammersbaldError> {
if let Some(ref mut page) = self.page {
if self.pos.in_page_pos() > 0 {
page.write_pref(PAGE_PAYLOAD_SIZE, self.lep);
self.file.append_page(page.clone())?;
self.pos += PAGE_SIZE as u64 - self.pos.in_page_pos() as u64;
}
}
Ok(self.file.flush()?)
}
}
pub struct PagedFileIterator<'file> {
pagenumber: u64,
file: &'file PagedFile
}
impl<'file> PagedFileIterator<'file> {
pub fn new (file: &'file 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
}
}