use std::fmt;
use std::io::{self, Write};
use crate::compress::Encoder;
use crate::crc32::CheckSummer;
pub use crate::error::IntoInnerError;
use crate::frame::{
compress_frame, CHUNK_HEADER_AND_CRC_SIZE, MAX_COMPRESS_BLOCK_SIZE,
STREAM_IDENTIFIER,
};
use crate::MAX_BLOCK_SIZE;
pub struct FrameEncoder<W: io::Write> {
inner: Option<Inner<W>>,
src: Vec<u8>,
}
struct Inner<W> {
w: W,
enc: Encoder,
checksummer: CheckSummer,
dst: Vec<u8>,
wrote_stream_ident: bool,
chunk_header: [u8; 8],
}
impl<W: io::Write> FrameEncoder<W> {
pub fn new(wtr: W) -> FrameEncoder<W> {
FrameEncoder {
inner: Some(Inner {
w: wtr,
enc: Encoder::new(),
checksummer: CheckSummer::new(),
dst: vec![0; MAX_COMPRESS_BLOCK_SIZE],
wrote_stream_ident: false,
chunk_header: [0; CHUNK_HEADER_AND_CRC_SIZE],
}),
src: Vec::with_capacity(MAX_BLOCK_SIZE),
}
}
pub fn into_inner(mut self) -> Result<W, IntoInnerError<FrameEncoder<W>>> {
match self.flush() {
Ok(()) => Ok(self.inner.take().unwrap().w),
Err(err) => Err(IntoInnerError::new(self, err)),
}
}
pub fn get_ref(&self) -> &W {
&self.inner.as_ref().unwrap().w
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.inner.as_mut().unwrap().w
}
}
impl<W: io::Write> Drop for FrameEncoder<W> {
fn drop(&mut self) {
if self.inner.is_some() {
let _ = self.flush();
}
}
}
impl<W: io::Write> io::Write for FrameEncoder<W> {
fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> {
let mut total = 0;
loop {
let free = self.src.capacity() - self.src.len();
let n = if buf.len() <= free {
break;
} else if self.src.is_empty() {
self.inner.as_mut().unwrap().write(buf)?
} else {
self.src.extend_from_slice(&buf[0..free]);
self.flush()?;
free
};
buf = &buf[n..];
total += n;
}
debug_assert!(buf.len() <= (self.src.capacity() - self.src.len()));
self.src.extend_from_slice(buf);
total += buf.len();
debug_assert!(self.src.capacity() == MAX_BLOCK_SIZE);
Ok(total)
}
fn flush(&mut self) -> io::Result<()> {
if self.src.is_empty() {
return Ok(());
}
self.inner.as_mut().unwrap().write(&self.src)?;
self.src.truncate(0);
Ok(())
}
}
impl<W: io::Write> Inner<W> {
fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> {
let mut total = 0;
if !self.wrote_stream_ident {
self.wrote_stream_ident = true;
self.w.write_all(STREAM_IDENTIFIER)?;
}
while !buf.is_empty() {
let mut src = buf;
if src.len() > MAX_BLOCK_SIZE {
src = &src[0..MAX_BLOCK_SIZE];
}
buf = &buf[src.len()..];
let frame_data = compress_frame(
&mut self.enc,
self.checksummer,
src,
&mut self.chunk_header,
&mut self.dst,
false,
)?;
self.w.write_all(&self.chunk_header)?;
self.w.write_all(frame_data)?;
total += src.len();
}
Ok(total)
}
}
impl<W: fmt::Debug + io::Write> fmt::Debug for FrameEncoder<W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("FrameEncoder")
.field("inner", &self.inner)
.field("src", &"[...]")
.finish()
}
}
impl<W: fmt::Debug + io::Write> fmt::Debug for Inner<W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Inner")
.field("w", &self.w)
.field("enc", &self.enc)
.field("checksummer", &self.checksummer)
.field("dst", &"[...]")
.field("wrote_stream_ident", &self.wrote_stream_ident)
.field("chunk_header", &self.chunk_header)
.finish()
}
}