use std::io::{ ErrorKind, Error, Seek, SeekFrom, Write, Read };
use std::collections::HashMap;
const SLAB_SIZE: usize = 1024*1024;
const SLAB_MASK: u64 = SLAB_SIZE as u64 - 1;
const DEFAULT_NUM_SLABS: usize = 16;
struct Slab {
pub dat: Vec<u8>,
start: u64,
uses: u64,
dirty: bool
}
impl Slab {
pub fn new<F: Seek + Read + Write>(loc: u64, end: u64, file: &mut F) -> Result<Slab, Error> {
file.seek(SeekFrom::Start(loc))?;
let mut dat = vec![0u8; SLAB_SIZE];
if loc != end {
file.read(&mut dat[0..])?;
}
Ok(Slab {
dat: dat,
start: loc,
uses: 0,
dirty: false
})
}
pub fn write<F: Seek + Write>(&mut self, file: &mut F) -> Result<(), Error> {
if !self.dirty { return Ok(()) }
file.seek(SeekFrom::Start(self.start))?;
file.write_all(&self.dat[0..])?;
self.dirty = false;
Ok(())
}
}
pub struct BufFile<F: Write + Read + Seek> {
slabs: usize,
map: HashMap<u64, usize>,
dat: Vec<Slab>,
file: Option<F>,
cursor: u64,
end: u64
}
impl<F: Write + Read + Seek> BufFile<F> {
pub fn new(file: F) -> Result<BufFile<F>, Error> {
Self::with_capacity(DEFAULT_NUM_SLABS, file)
}
pub fn with_capacity(slabs: usize, mut file: F) -> Result<BufFile<F>, Error> {
let end = file.seek(SeekFrom::End(0))?;
file.seek(SeekFrom::Start(0))?;
Ok(BufFile {
slabs: slabs, dat: Vec::with_capacity(slabs),
map: HashMap::new(),
file: Some(file),
cursor: 0, end
})
}
pub fn into_inner(mut self) -> Result<F, Error> {
self.flush()?;
Ok(self.file.take().unwrap())
}
pub fn set_slabs(&mut self, num_slabs: usize) -> Result<(), Error> {
if num_slabs == 0 { return Ok(()) }
if num_slabs >= self.dat.len() {
self.slabs = num_slabs;
return Ok(())
}
while self.dat.len() > num_slabs {
let mut min = 0;
for i in 0..self.slabs {
if self.dat[min].uses == 1 {
min = i;
break;
}
if self.dat[min].uses > self.dat[i].uses {
min = i;
}
}
self.dat[min].write(self.file.as_mut().unwrap())?;
let _ = self.dat.swap_remove(min);
}
self.slabs = num_slabs;
Ok(())
}
pub fn cursor_loc(&self) -> u64 {
self.cursor
}
fn fetch_slab(&mut self, mut loc: u64) -> Result<&mut Slab, Error> {
loc = loc & !SLAB_MASK;
if let Some(x) = self.find_slab(loc) {
Ok(&mut self.dat[x])
} else {
let ind = self.add_slab(loc)?;
Ok(&mut self.dat[ind])
}
}
fn find_slab(&mut self, loc: u64) -> Option<usize> {
if self.map.contains_key(&loc) {
let x = self.map[&loc].clone();
Some(x)
} else {
None
}
}
fn add_slab(&mut self, start: u64) -> Result<usize, Error> {
if self.map.contains_key(&start) {
return Ok(self.map[&start].clone());
}
if self.dat.len() < self.slabs {
let ind = self.dat.len();
match Slab::new(start, self.end, self.file.as_mut().unwrap()) {
Ok(x) => {
self.map.insert(start, self.dat.len());
self.dat.push(x);
Ok(ind)
},
Err(e) => Err(e)
}
}
else {
let mut min = 0;
for i in 0..self.slabs {
if self.dat[min].uses == 1 {
min = i;
break;
}
if self.dat[min].uses > self.dat[i].uses {
min = i;
}
}
match Slab::new(start, self.end, self.file.as_mut().unwrap()) {
Ok(x) => {
self.dat[min].write(self.file.as_mut().unwrap())?;
self.file.as_mut().unwrap().seek(SeekFrom::Start(self.cursor))?;
self.map.remove(&self.dat[min].start);
self.map.insert(start, min);
self.dat[min] = x;
Ok(min)
},
Err(x) => Err(x)
}
}
}
}
impl<F: Write + Read + Seek> Read for BufFile<F> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
let cursor = self.cursor;
let len = {
let slab = self.fetch_slab(cursor)?;
slab.uses += 1;
let start = slab.start;
let mut dat = &slab.dat[(cursor - start) as usize ..];
let x = dat.read(buf);
x?
};
self.cursor += len as u64;
Ok(len)
}
}
impl<F: Write + Read + Seek> Write for BufFile<F> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
let cursor = self.cursor;
let len = {
let slab = self.fetch_slab(cursor)?;
slab.uses += 1;
slab.dirty = true;
let mut dat = &mut slab.dat[(cursor - slab.start) as usize..];
dat.write(buf)?
};
self.cursor += len as u64;
if self.end < self.cursor { self.end = self.cursor; }
Ok(len)
}
fn flush(&mut self) -> Result<(), Error> {
for slab in self.dat.iter_mut() {
slab.write(self.file.as_mut().unwrap())?;
}
Ok(())
}
}
impl<F: Write + Read + Seek> Seek for BufFile<F> {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
let pos = pos.clone();
let new_pos = match pos {
SeekFrom::Start(x) => {
let _ = self.fetch_slab(x)?;
self.cursor = x;
self.cursor
},
SeekFrom::End(x) => {
self.cursor =
if x < 0 { self.end - (-x) as u64 } else { self.end - x as u64 }; let cursor = self.cursor;
let _ = self.fetch_slab(x as u64)?;
cursor
},
SeekFrom::Current(x) => {
let cur = self.cursor;
let cursor =
if x < 0 { cur - (-x) as u64 }
else { cur - x as u64 };
self.cursor = cursor;
let _ = self.fetch_slab(x as u64)?;
self.cursor
}
};
if new_pos <= self.end {
Ok(new_pos)
} else {
Err(Error::new(ErrorKind::UnexpectedEof, "Attempted to seek beyond the end of the file"))
}
}
}
impl<F: Read + Write + Seek> Drop for BufFile<F> {
fn drop(&mut self) {
if self.file.is_none() { return }
let _ = self.flush();
}
}