use std::io;
use std::io::Write;
use crate::spanning::{RecoverableWrite, DataZone, DataZoneStream};
use crate::fs::ArchivalSink;
pub struct BlockingWriter<W, P = u64> where P: Clone + PartialEq {
blocking_factor: usize,
inner: W,
block: Vec<u8>,
datazone_stream: DataZoneStream<P>
}
impl<W: Write, P> BlockingWriter<W, P> where P: Clone + PartialEq {
pub fn new(inner: W) -> BlockingWriter<W, P> {
BlockingWriter {
inner: inner,
blocking_factor: 20 * 512,
block: Vec::with_capacity(20 * 512),
datazone_stream: DataZoneStream::new()
}
}
pub fn new_with_factor(inner: W, factor: usize) -> BlockingWriter<W, P> {
BlockingWriter {
inner: inner,
blocking_factor: factor * 512,
block: Vec::with_capacity(factor * 512),
datazone_stream: DataZoneStream::new()
}
}
pub fn as_inner_writer<'a>(&'a self) -> &'a W {
&self.inner
}
fn fill_block<'a>(&mut self, buf: &'a [u8]) -> Option<&'a [u8]> {
let block_space = self.blocking_factor - self.block.len();
if block_space >= buf.len() {
self.block.extend(buf);
self.datazone_stream.write_buffered(buf.len() as u64);
return None;
}
self.block.extend(&buf[0..block_space]);
self.datazone_stream.write_buffered(block_space as u64);
Some(&buf[block_space..])
}
fn empty_block<'a>(&mut self) -> io::Result<()> {
if self.block.len() >= self.blocking_factor {
self.inner.write_all(&self.block[..self.blocking_factor])?;
self.datazone_stream.write_committed(self.blocking_factor as u64);
unsafe { self.block.set_len(0); }
}
Ok(())
}
}
impl<W:Write, P> RecoverableWrite<P> for BlockingWriter<W, P> where P: Clone + PartialEq, W: RecoverableWrite<P> {
fn begin_data_zone(&mut self, ident: P) {
self.datazone_stream.begin_data_zone(ident.clone());
self.inner.begin_data_zone(ident);
}
fn resume_data_zone(&mut self, ident: P, committed: u64) {
self.datazone_stream.resume_data_zone(ident.clone(), committed);
self.inner.resume_data_zone(ident, committed);
}
fn end_data_zone(&mut self) {
self.datazone_stream.end_data_zone();
self.inner.end_data_zone();
}
fn uncommitted_writes(&self) -> Vec<DataZone<P>> {
let inner_ucw = self.inner.uncommitted_writes();
self.datazone_stream.uncommitted_writes(Some(inner_ucw))
}
}
impl<W:Write, P> ArchivalSink<P> for BlockingWriter<W, P> where W: Send + RecoverableWrite<P>, P: Send + Clone + PartialEq {
}
impl<W:Write, P> Write for BlockingWriter<W, P> where P: Clone + PartialEq, W: RecoverableWrite<P> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.empty_block()?;
if buf.len() == 0 {
return Ok(0);
}
let mut shortcircuit_writes = 0;
if self.block.len() == 0 && buf.len() >= self.blocking_factor {
while shortcircuit_writes <= (buf.len() - self.blocking_factor) {
match self.inner.write(&buf[shortcircuit_writes..(shortcircuit_writes + self.blocking_factor)]) {
Ok(blk_write) => {
shortcircuit_writes += blk_write;
self.datazone_stream.write_through(blk_write as u64);
}
Err(x) => return Err(x)
}
}
assert!(shortcircuit_writes > 0);
return Ok(shortcircuit_writes);
}
let remain = match self.fill_block(buf) {
Some(remain) => remain.len(),
None => 0
};
let write_size = buf.len() - remain;
assert!(write_size > 0);
Ok(write_size)
}
fn flush(&mut self) -> io::Result<()> {
self.end_data_zone();
if self.block.len() < self.blocking_factor {
self.block.resize(self.blocking_factor, 0);
}
self.empty_block()?;
self.inner.flush()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io::{Write, Cursor};
use crate::blocking::BlockingWriter;
use crate::spanning::{UnbufferedWriter, RecoverableWrite};
#[test]
fn blocking_factor_1_block_passthrough() {
let mut blk : BlockingWriter<_, u64> = BlockingWriter::new_with_factor(Cursor::new(vec![]), 1);
blk.write_all(&vec![0; 512]).unwrap();
blk.write_all(&vec![1; 512]).unwrap();
assert_eq!(blk.as_inner_writer().get_ref().len(), 1024);
assert_eq!(&blk.as_inner_writer().get_ref()[0..512], vec![0 as u8; 512].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[512..], vec![1 as u8; 512].as_slice());
}
#[test]
fn blocking_factor_1_record_splitting() {
let mut blk : BlockingWriter<_, u64> = BlockingWriter::new_with_factor(Cursor::new(vec![]), 1);
blk.write_all(&vec![0; 384]).unwrap();
blk.write_all(&vec![1; 384]).unwrap();
assert_eq!(blk.as_inner_writer().get_ref().len(), 512);
assert_eq!(&blk.as_inner_writer().get_ref()[0..384], vec![0; 384].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[384..], vec![1; 128].as_slice());
blk.write_all(&vec![2; 384]).unwrap();
blk.flush().unwrap();
assert_eq!(blk.as_inner_writer().get_ref().len(), 1536);
assert_eq!(&blk.as_inner_writer().get_ref()[0..384], vec![0; 384].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[384..768], vec![1; 384].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[768..1152], vec![2; 384].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[1152..], vec![0; 384].as_slice());
}
#[test]
fn blocking_factor_1_record_splitting_shortcircuit() {
let mut blk : BlockingWriter<_, u64> = BlockingWriter::new_with_factor(Cursor::new(vec![]), 1);
blk.write_all(&vec![0; 384]).unwrap();
blk.write_all(&vec![1; 1024]).unwrap();
assert_eq!(blk.as_inner_writer().get_ref().len(), 1024);
assert_eq!(&blk.as_inner_writer().get_ref()[0..384], vec![0; 384].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[384..], vec![1; 640].as_slice());
blk.write_all(&vec![2; 2048]).unwrap();
blk.flush().unwrap();
assert_eq!(blk.as_inner_writer().get_ref().len(), 3584);
assert_eq!(&blk.as_inner_writer().get_ref()[0..384], vec![0; 384].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[384..1408], vec![1; 1024].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[1408..3456], vec![2; 2048].as_slice());
assert_eq!(&blk.as_inner_writer().get_ref()[3456..], vec![0; 128].as_slice());
}
#[test]
fn blocking_factor_4_block_zone_tracking() {
let mut blk = BlockingWriter::new_with_factor(UnbufferedWriter::wrap(Cursor::new(vec![])), 4);
let ident1 = "ident1";
let ident2 = "ident2";
blk.begin_data_zone(ident1);
blk.write_all(&vec![0; 512]).unwrap();
blk.begin_data_zone(ident2);
blk.write_all(&vec![1; 512]).unwrap();
let zones = blk.uncommitted_writes();
assert_eq!(zones.len(), 2);
assert_eq!(zones[0].ident, Some(ident1));
assert_eq!(zones[0].length, 512);
assert_eq!(zones[0].uncommitted_length, 512);
assert_eq!(zones[0].committed_length, 0);
assert_eq!(zones[1].ident, Some(ident2));
assert_eq!(zones[1].length, 512);
assert_eq!(zones[1].uncommitted_length, 512);
assert_eq!(zones[1].committed_length, 0);
blk.flush().unwrap();
let zones_2 = blk.uncommitted_writes();
assert_eq!(zones_2.len(), 0);
assert_eq!(blk.as_inner_writer().as_inner_writer().get_ref().len(), 2048);
assert_eq!(&blk.as_inner_writer().as_inner_writer().get_ref()[0..512], vec![0 as u8; 512].as_slice());
assert_eq!(&blk.as_inner_writer().as_inner_writer().get_ref()[512..1024], vec![1 as u8; 512].as_slice());
assert_eq!(&blk.as_inner_writer().as_inner_writer().get_ref()[1024..], vec![0 as u8; 1024].as_slice());
}
}