use crate::engine::Engine;
use std::{
cmp, fmt, io,
io::{ErrorKind, Result},
};
pub(crate) const BUF_SIZE: usize = 1024;
const MAX_INPUT_LEN: usize = BUF_SIZE / 4 * 3;
const MIN_ENCODE_CHUNK_SIZE: usize = 3;
pub struct EncoderWriter<'e, E: Engine, W: io::Write> {
engine: &'e E,
delegate: Option<W>,
extra_input: [u8; MIN_ENCODE_CHUNK_SIZE],
extra_input_occupied_len: usize,
output: [u8; BUF_SIZE],
output_occupied_len: usize,
panicked: bool,
}
impl<'e, E: Engine, W: io::Write> fmt::Debug for EncoderWriter<'e, E, W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"extra_input: {:?} extra_input_occupied_len:{:?} output[..5]: {:?} output_occupied_len: {:?}",
self.extra_input,
self.extra_input_occupied_len,
&self.output[0..5],
self.output_occupied_len
)
}
}
impl<'e, E: Engine, W: io::Write> EncoderWriter<'e, E, W> {
pub fn new(delegate: W, engine: &'e E) -> EncoderWriter<'e, E, W> {
EncoderWriter {
engine,
delegate: Some(delegate),
extra_input: [0u8; MIN_ENCODE_CHUNK_SIZE],
extra_input_occupied_len: 0,
output: [0u8; BUF_SIZE],
output_occupied_len: 0,
panicked: false,
}
}
pub fn finish(&mut self) -> Result<W> {
if self.delegate.is_none() {
panic!("Encoder has already had finish() called");
};
self.write_final_leftovers()?;
let writer = self.delegate.take().expect("Writer must be present");
Ok(writer)
}
fn write_final_leftovers(&mut self) -> Result<()> {
if self.delegate.is_none() {
return Ok(());
}
self.write_all_encoded_output()?;
if self.extra_input_occupied_len > 0 {
let encoded_len = self
.engine
.encode_slice(
&self.extra_input[..self.extra_input_occupied_len],
&mut self.output[..],
)
.expect("buffer is large enough");
self.output_occupied_len = encoded_len;
self.write_all_encoded_output()?;
self.extra_input_occupied_len = 0;
}
Ok(())
}
fn write_to_delegate(&mut self, current_output_len: usize) -> Result<()> {
self.panicked = true;
let res = self
.delegate
.as_mut()
.expect("Writer must be present")
.write(&self.output[..current_output_len]);
self.panicked = false;
res.map(|consumed| {
debug_assert!(consumed <= current_output_len);
if consumed < current_output_len {
self.output_occupied_len = current_output_len.checked_sub(consumed).unwrap();
self.output.rotate_left(consumed);
} else {
self.output_occupied_len = 0;
}
})
}
fn write_all_encoded_output(&mut self) -> Result<()> {
while self.output_occupied_len > 0 {
let remaining_len = self.output_occupied_len;
match self.write_to_delegate(remaining_len) {
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e),
Ok(_) => {}
};
}
debug_assert_eq!(0, self.output_occupied_len);
Ok(())
}
pub fn into_inner(mut self) -> W {
self.delegate
.take()
.expect("Encoder has already had finish() called")
}
}
impl<'e, E: Engine, W: io::Write> io::Write for EncoderWriter<'e, E, W> {
fn write(&mut self, input: &[u8]) -> Result<usize> {
if self.delegate.is_none() {
panic!("Cannot write more after calling finish()");
}
if input.is_empty() {
return Ok(0);
}
if self.output_occupied_len > 0 {
let current_len = self.output_occupied_len;
return self
.write_to_delegate(current_len)
.map(|_| 0);
}
debug_assert_eq!(0, self.output_occupied_len);
let mut extra_input_read_len = 0;
let mut input = input;
let orig_extra_len = self.extra_input_occupied_len;
let mut encoded_size = 0;
let mut max_input_len = MAX_INPUT_LEN;
if self.extra_input_occupied_len > 0 {
debug_assert!(self.extra_input_occupied_len < 3);
if input.len() + self.extra_input_occupied_len >= MIN_ENCODE_CHUNK_SIZE {
extra_input_read_len = MIN_ENCODE_CHUNK_SIZE - self.extra_input_occupied_len;
debug_assert!(extra_input_read_len > 0);
self.extra_input[self.extra_input_occupied_len..MIN_ENCODE_CHUNK_SIZE]
.copy_from_slice(&input[0..extra_input_read_len]);
let len = self.engine.internal_encode(
&self.extra_input[0..MIN_ENCODE_CHUNK_SIZE],
&mut self.output[..],
);
debug_assert_eq!(4, len);
input = &input[extra_input_read_len..];
self.extra_input_occupied_len = 0;
encoded_size = 4;
max_input_len = MAX_INPUT_LEN - MIN_ENCODE_CHUNK_SIZE;
} else {
debug_assert_eq!(1, input.len());
debug_assert_eq!(1, self.extra_input_occupied_len);
self.extra_input[self.extra_input_occupied_len] = input[0];
self.extra_input_occupied_len += 1;
return Ok(1);
};
} else if input.len() < MIN_ENCODE_CHUNK_SIZE {
self.extra_input[0..input.len()].copy_from_slice(input);
self.extra_input_occupied_len = input.len();
return Ok(input.len());
};
debug_assert!(encoded_size == 0 || encoded_size == 4);
debug_assert!(
MAX_INPUT_LEN == max_input_len
|| MAX_INPUT_LEN == max_input_len + MIN_ENCODE_CHUNK_SIZE
);
let input_complete_chunks_len = input.len() - (input.len() % MIN_ENCODE_CHUNK_SIZE);
let input_chunks_to_encode_len = cmp::min(input_complete_chunks_len, max_input_len);
debug_assert_eq!(0, max_input_len % MIN_ENCODE_CHUNK_SIZE);
debug_assert_eq!(0, input_chunks_to_encode_len % MIN_ENCODE_CHUNK_SIZE);
encoded_size += self.engine.internal_encode(
&input[..(input_chunks_to_encode_len)],
&mut self.output[encoded_size..],
);
self.write_to_delegate(encoded_size)
.map(|_| extra_input_read_len + input_chunks_to_encode_len)
.map_err(|e| {
self.extra_input_occupied_len = orig_extra_len;
e
})
}
fn flush(&mut self) -> Result<()> {
self.write_all_encoded_output()?;
self.delegate
.as_mut()
.expect("Writer must be present")
.flush()
}
}
impl<'e, E: Engine, W: io::Write> Drop for EncoderWriter<'e, E, W> {
fn drop(&mut self) {
if !self.panicked {
let _ = self.write_final_leftovers();
}
}
}