use std::path::{Path, PathBuf};
use std::fs;
use std::io::{Error, Read, Write};
pub const BUFFER_SIZE: usize = 8 * 1024;
pub trait DiskBufferTrait {
fn clear(&mut self);
fn get_ref(&self) -> &[u8];
fn is_empty(&self) -> bool;
}
pub struct DiskBuffer {
path: PathBuf,
}
impl DiskBuffer {
pub fn new(path: &Path) -> DiskBuffer { DiskBuffer { path: path.to_owned() } }
pub fn write(self) -> Result<DiskBufferWriter, Error> {
fs::OpenOptions::new().create(true).write(true).open(&self.path)?;
Ok(DiskBufferWriter {
data: [b'\0'; BUFFER_SIZE],
capacity: 0,
path: self.path,
})
}
pub fn read(self) -> Result<DiskBufferReader, Error> {
let file = fs::OpenOptions::new().read(true).open(&self.path)?;
Ok(DiskBufferReader {
data: [b'\0'; BUFFER_SIZE],
capacity: 0,
file: file,
path: self.path,
})
}
}
pub struct DiskBufferReader {
pub data: [u8; BUFFER_SIZE],
pub capacity: usize,
pub file: fs::File,
pub path: PathBuf,
}
impl DiskBufferTrait for DiskBufferReader {
fn clear(&mut self) { self.capacity = 0; }
fn get_ref(&self) -> &[u8] { &self.data[0..self.capacity] }
fn is_empty(&self) -> bool { self.capacity == 0 }
}
impl DiskBufferReader {
pub fn buffer(&mut self, bytes_used: usize) -> Result<(), Error> {
if bytes_used == 0 {
self.capacity = self.file.read(&mut self.data)?;
} else {
let bytes_unused = self.capacity - bytes_used;
for (left, right) in (0..bytes_unused).zip(bytes_used + 1..bytes_used + bytes_unused) {
self.data[left] = self.data[right];
}
self.capacity = self.file.read(&mut self.data[bytes_unused-1..])? + bytes_unused - 1;
}
Ok(())
}
}
pub struct DiskBufferWriter {
pub data: [u8; BUFFER_SIZE],
pub capacity: usize,
pub path: PathBuf,
}
impl DiskBufferTrait for DiskBufferWriter {
fn clear(&mut self) { self.capacity = 0; }
fn get_ref(&self) -> &[u8] { &self.data[0..self.capacity] }
fn is_empty(&self) -> bool { self.capacity == 0 }
}
impl DiskBufferWriter {
pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
let cap = data.len();
if cap + self.capacity > BUFFER_SIZE {
fs::OpenOptions::new().write(true).append(true).open(&self.path)
.and_then(|mut file| file.write(self.get_ref()))?;
self.clear();
}
self.data[self.capacity..self.capacity + cap].clone_from_slice(data);
self.capacity += cap;
Ok(())
}
pub fn write_byte(&mut self, data: u8) -> Result<(), Error> {
if self.capacity + 1 > BUFFER_SIZE {
fs::OpenOptions::new().write(true).append(true).open(&self.path)
.and_then(|mut file| file.write(self.get_ref()))?;
self.clear();
}
self.data[self.capacity] = data;
self.capacity += 1;
Ok(())
}
pub fn flush(&mut self) -> Result<usize, Error> {
if !self.is_empty() {
return fs::OpenOptions::new().write(true).append(true).open(&self.path)
.and_then(|mut file| file.write(self.get_ref()));
}
self.capacity = 0;
Ok(0)
}
}
#[test]
fn test_disk_buffer_reader_simple() {
let file = include_bytes!("../../tests/buffer.dat");
let mut disk_buffer_reader = DiskBuffer::new(Path::new("tests/buffer.dat")).read().unwrap();
let _ = disk_buffer_reader.buffer(0);
assert_eq!(&file[0..BUFFER_SIZE], &disk_buffer_reader.data[..]);
let _ = disk_buffer_reader.buffer(0);
assert_eq!(&file[BUFFER_SIZE..BUFFER_SIZE*2], &disk_buffer_reader.data[..]);
let _ = disk_buffer_reader.buffer(0);
assert_eq!(&file[BUFFER_SIZE*2..], &disk_buffer_reader.data[..2989]);
}
#[test]
fn test_disk_buffer_reader_byte_shifting() {
let file = include_bytes!("../../tests/buffer.dat");
let mut disk_buffer_reader = DiskBuffer::new(Path::new("tests/buffer.dat")).read().unwrap();
let _ = disk_buffer_reader.buffer(0);
assert_eq!(&file[0..BUFFER_SIZE], &disk_buffer_reader.data[..]);
let _ = disk_buffer_reader.buffer(BUFFER_SIZE/2);
assert_eq!(&file[BUFFER_SIZE/2+1..BUFFER_SIZE/2+1+BUFFER_SIZE], &disk_buffer_reader.data[0..BUFFER_SIZE]);
}