use super::*;
use crate::dictionary::PreparedDictionary;
use crate::{Operation, StreamConfig};
use alloc::boxed::Box;
use std::io::{self, Write};
#[derive(Debug)]
pub struct FramedWriter<'c, W> {
session: FramedEncoderSession<'c>,
transport: Transport<W>,
}
#[derive(Debug)]
struct Transport<W> {
sink: W,
bytes: [u8; 8192],
cursor: usize,
length: usize,
deferred: Option<FramedEncodeError>,
}
impl<W: Write> Transport<W> {
fn drain(&mut self) -> Result<(), FramedEncodeError> {
while self.cursor < self.length {
match self.sink.write(&self.bytes[self.cursor..self.length]) {
Ok(0) => return Err(io::Error::from(io::ErrorKind::WriteZero).into()),
Ok(n) if n <= self.length - self.cursor => self.cursor += n,
Ok(_) => return Err(io::Error::other("sink reported an oversized write").into()),
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e.into()),
}
}
self.cursor = 0;
self.length = 0;
if let Some(e) = self.deferred.take() {
return Err(e);
}
Ok(())
}
}
impl FramedCompressor {
pub fn framed_writer<W: Write>(
&mut self,
writer: W,
stream: FramedEncodeStreamConfig,
) -> Result<FramedWriter<'_, W>, FramedEncodeError> {
Ok(FramedWriter {
session: self.start(stream)?,
transport: Transport {
sink: writer,
bytes: [0; 8192],
cursor: 0,
length: 0,
deferred: None,
},
})
}
}
impl<W: Write> FramedWriter<'_, W> {
fn drain(&mut self, operation: FramedEncodeOperation) -> Result<(), FramedEncodeError> {
loop {
self.transport.drain()?;
let p = match self.session.process(&mut self.transport.bytes, operation) {
Ok(p) => p,
Err(e) => {
self.transport.length = e.produced;
self.transport.deferred = Some(e.error);
self.transport.drain()?;
return Err(FramedEncodeError::InvalidState);
}
};
self.transport.length = p.produced;
self.transport.drain()?;
if p.status != FramedEncoderStatus::NeedsOutput {
return Ok(());
}
}
}
pub fn resource(
&mut self,
options: ResourceOptions,
stream: StreamConfig,
) -> Result<ResourceWriter<'_, 'static, W>, FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
Ok(ResourceWriter {
session: self.session.resource(options, stream)?,
transport: &mut self.transport,
})
}
pub fn resource_with_dictionary<'s, 'dict>(
&'s mut self,
options: ResourceOptions,
stream: StreamConfig,
dictionary: &'dict PreparedDictionary,
references: &[DictionaryReference],
) -> Result<ResourceWriter<'s, 'dict, W>, FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
Ok(ResourceWriter {
session: self
.session
.resource_with_dictionary(options, stream, dictionary, references)?,
transport: &mut self.transport,
})
}
pub fn uncompressed_resource(
&mut self,
options: ResourceOptions,
) -> Result<ResourceWriter<'_, 'static, W>, FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
Ok(ResourceWriter {
session: self.session.uncompressed_resource(options)?,
transport: &mut self.transport,
})
}
pub fn metadata(
&mut self,
kind: MetadataKind,
fields: &[MetadataField<'_>],
) -> Result<(), FramedEncodeError> {
self.metadata_with_options(kind, fields, MetadataOptions::default())
}
pub fn metadata_with_options(
&mut self,
kind: MetadataKind,
fields: &[MetadataField<'_>],
options: MetadataOptions<'_>,
) -> Result<(), FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
self.session.metadata_with_options(kind, fields, options)
}
pub fn repeat_metadata_fields(&mut self, codes: &[[u8; 2]]) -> Result<(), FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
self.session.repeat_metadata_fields(codes)
}
pub fn padding(&mut self, bytes: usize) -> Result<(), FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
self.session.padding(bytes)
}
pub fn flush(&mut self) -> Result<(), FramedEncodeError> {
self.drain(FramedEncodeOperation::Process)?;
self.transport.sink.flush()?;
Ok(())
}
pub fn try_finish(&mut self) -> Result<(), FramedEncodeError> {
self.drain(FramedEncodeOperation::Finish)?;
self.transport.sink.flush()?;
Ok(())
}
pub fn finish(mut self) -> Result<W, Box<FramingFinishError<Self>>> {
match self.try_finish() {
Ok(()) => Ok(self.transport.sink),
Err(error) => Err(Box::new(FramingFinishError {
writer: self,
error,
})),
}
}
#[must_use]
pub const fn get_ref(&self) -> &W {
&self.transport.sink
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.transport.sink
}
#[must_use]
pub const fn next_chunk_offset(&self) -> u64 {
self.session.next_chunk_offset()
}
#[must_use]
pub fn into_inner(self) -> W {
self.transport.sink
}
}
#[derive(Debug)]
pub struct ResourceWriter<'s, 'dict, W> {
session: FramedResourceSession<'s, 'dict>,
transport: &'s mut Transport<W>,
}
impl<W: Write> ResourceWriter<'_, '_, W> {
fn complete(&mut self, operation: Operation) -> Result<(), FramedEncodeError> {
loop {
self.transport.drain()?;
let p = match self
.session
.process(&[], &mut self.transport.bytes, operation)
{
Ok(p) => p,
Err(e) => {
self.transport.length = e.produced;
self.transport.deferred = Some(e.error);
self.transport.drain()?;
return Err(FramedEncodeError::InvalidState);
}
};
self.transport.length = p.produced;
self.transport.drain()?;
if p.status != FramedEncoderStatus::NeedsOutput {
return Ok(());
}
}
}
pub fn try_finish(&mut self) -> Result<(), FramedEncodeError> {
self.complete(Operation::End)
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.transport.sink
}
}
impl<W: Write> Write for ResourceWriter<'_, '_, W> {
fn write(&mut self, input: &[u8]) -> io::Result<usize> {
if input.is_empty() {
return Ok(0);
}
self.transport.drain().map_err(io::Error::from)?;
if self.session.is_finished() {
return Err(FramedEncodeError::InvalidState.into());
}
loop {
match self
.session
.process(input, &mut self.transport.bytes, Operation::Continue)
{
Ok(p) => {
self.transport.length = p.produced;
if p.consumed != 0 {
return Ok(p.consumed);
}
self.transport.drain().map_err(io::Error::from)?;
}
Err(e) => {
self.transport.length = e.produced;
if e.consumed != 0 {
self.transport.deferred = Some(e.error);
return Ok(e.consumed);
}
return Err(e.error.into());
}
}
}
}
fn flush(&mut self) -> io::Result<()> {
self.complete(Operation::Flush).map_err(io::Error::from)?;
self.transport.sink.flush()
}
}