Skip to main content

brotli/enc/
writer.rs

1use alloc::{Allocator, SliceWrapperMut};
2#[cfg(feature = "std")]
3use std::io;
4#[cfg(feature = "std")]
5use std::io::{Error, ErrorKind, Write};
6
7#[cfg(feature = "std")]
8pub use alloc_stdlib::StandardAlloc;
9use brotli_decompressor::CustomWrite;
10#[cfg(feature = "std")]
11pub use brotli_decompressor::{IntoIoWriter, IoWriterWrapper};
12
13use super::backward_references::BrotliEncoderParams;
14use super::combined_alloc::BrotliAlloc;
15use super::encode::{
16    BrotliEncoderDestroyInstance, BrotliEncoderOperation, BrotliEncoderParameter,
17    BrotliEncoderStateStruct,
18};
19use super::interface;
20use crate::enc::combined_alloc::allocate;
21
22#[cfg(feature = "std")]
23pub struct CompressorWriterCustomAlloc<
24    W: Write,
25    BufferType: SliceWrapperMut<u8>,
26    Alloc: BrotliAlloc,
27>(CompressorWriterCustomIo<io::Error, IntoIoWriter<W>, BufferType, Alloc>);
28
29#[cfg(feature = "std")]
30impl<W: Write, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
31    CompressorWriterCustomAlloc<W, BufferType, Alloc>
32{
33    pub fn new(w: W, buffer: BufferType, alloc: Alloc, q: u32, lgwin: u32) -> Self {
34        CompressorWriterCustomAlloc::<W, BufferType, Alloc>(CompressorWriterCustomIo::<
35            Error,
36            IntoIoWriter<W>,
37            BufferType,
38            Alloc,
39        >::new(
40            IntoIoWriter::<W>(w),
41            buffer,
42            alloc,
43            Error::new(ErrorKind::InvalidData, "Invalid Data"),
44            Error::new(ErrorKind::WriteZero, "No room in output."),
45            q,
46            lgwin,
47        ))
48    }
49
50    pub fn get_ref(&self) -> &W {
51        &self.0.get_ref().0
52    }
53    pub fn get_mut(&mut self) -> &mut W {
54        &mut self.0.get_mut().0
55    }
56    pub fn into_inner(self) -> W {
57        self.0.into_inner().0
58    }
59}
60
61#[cfg(feature = "std")]
62impl<W: Write, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc> Write
63    for CompressorWriterCustomAlloc<W, BufferType, Alloc>
64{
65    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
66        self.0.write(buf)
67    }
68    fn flush(&mut self) -> Result<(), Error> {
69        self.0.flush()
70    }
71}
72
73#[cfg(feature = "std")]
74pub struct CompressorWriter<W: Write>(
75    CompressorWriterCustomAlloc<
76        W,
77        <StandardAlloc as Allocator<u8>>::AllocatedMemory,
78        StandardAlloc,
79    >,
80);
81
82#[cfg(feature = "std")]
83impl<W: Write> CompressorWriter<W> {
84    pub fn new(w: W, buffer_size: usize, q: u32, lgwin: u32) -> Self {
85        let mut alloc = StandardAlloc::default();
86        let buffer = allocate::<u8, _>(
87            &mut alloc,
88            if buffer_size == 0 { 4096 } else { buffer_size },
89        );
90        CompressorWriter::<W>(CompressorWriterCustomAlloc::new(w, buffer, alloc, q, lgwin))
91    }
92
93    pub fn with_params(w: W, buffer_size: usize, params: &BrotliEncoderParams) -> Self {
94        let mut writer = Self::new(w, buffer_size, params.quality as u32, params.lgwin as u32);
95        (writer.0).0.state.params = params.clone();
96        writer
97    }
98
99    pub fn get_ref(&self) -> &W {
100        self.0.get_ref()
101    }
102    pub fn get_mut(&mut self) -> &mut W {
103        self.0.get_mut()
104    }
105    pub fn into_inner(self) -> W {
106        self.0.into_inner()
107    }
108}
109
110#[cfg(feature = "std")]
111impl<W: Write> Write for CompressorWriter<W> {
112    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
113        self.0.write(buf)
114    }
115    fn flush(&mut self) -> Result<(), Error> {
116        self.0.flush()
117    }
118}
119
120pub struct CompressorWriterCustomIo<
121    ErrType,
122    W: CustomWrite<ErrType>,
123    BufferType: SliceWrapperMut<u8>,
124    Alloc: BrotliAlloc,
125> {
126    output_buffer: BufferType,
127    total_out: Option<usize>,
128    output: Option<W>,
129    error_if_invalid_data: Option<ErrType>,
130    state: BrotliEncoderStateStruct<Alloc>,
131    error_if_zero_bytes_written: Option<ErrType>,
132}
133pub fn write_all<ErrType, W: CustomWrite<ErrType>, ErrMaker: FnMut() -> Option<ErrType>>(
134    writer: &mut W,
135    mut buf: &[u8],
136    mut error_to_return_if_zero_bytes_written: ErrMaker,
137) -> Result<(), ErrType> {
138    while !buf.is_empty() {
139        match writer.write(buf) {
140            Ok(bytes_written) => {
141                if bytes_written != 0 {
142                    buf = &buf[bytes_written..]
143                } else {
144                    if let Some(err) = error_to_return_if_zero_bytes_written() {
145                        return Err(err);
146                    } else {
147                        return Ok(());
148                    }
149                }
150            }
151            Err(e) => return Err(e),
152        }
153    }
154    Ok(())
155}
156impl<ErrType, W: CustomWrite<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
157    CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
158{
159    pub fn new(
160        w: W,
161        buffer: BufferType,
162        alloc: Alloc,
163        invalid_data_error_type: ErrType,
164        error_if_zero_bytes_written: ErrType,
165        q: u32,
166        lgwin: u32,
167    ) -> Self {
168        let mut ret = CompressorWriterCustomIo {
169            output_buffer: buffer,
170            total_out: Some(0),
171            output: Some(w),
172            state: BrotliEncoderStateStruct::new(alloc),
173            error_if_invalid_data: Some(invalid_data_error_type),
174            error_if_zero_bytes_written: Some(error_if_zero_bytes_written),
175        };
176        ret.state
177            .set_parameter(BrotliEncoderParameter::BROTLI_PARAM_QUALITY, q);
178        ret.state
179            .set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LGWIN, lgwin);
180
181        ret
182    }
183    fn flush_or_close(&mut self, op: BrotliEncoderOperation) -> Result<(), ErrType> {
184        let mut nop_callback =
185            |_data: &mut interface::PredictionModeContextMap<interface::InputReferenceMut>,
186             _cmds: &mut [interface::StaticCommand],
187             _mb: interface::InputPair,
188             _mfv: &mut Alloc| ();
189
190        loop {
191            let mut avail_in: usize = 0;
192            let mut input_offset: usize = 0;
193            let mut avail_out: usize = self.output_buffer.slice_mut().len();
194            let mut output_offset: usize = 0;
195            let ret = self.state.compress_stream(
196                op,
197                &mut avail_in,
198                &[],
199                &mut input_offset,
200                &mut avail_out,
201                self.output_buffer.slice_mut(),
202                &mut output_offset,
203                &mut self.total_out,
204                &mut nop_callback,
205            );
206            if output_offset > 0 {
207                let zero_err = &mut self.error_if_zero_bytes_written;
208                let fallback = &mut self.error_if_invalid_data;
209                match write_all(
210                    self.output.as_mut().unwrap(),
211                    &self.output_buffer.slice_mut()[..output_offset],
212                    || {
213                        if let Some(err) = zero_err.take() {
214                            return Some(err);
215                        }
216                        fallback.take()
217                    },
218                ) {
219                    Ok(_) => {}
220                    Err(e) => return Err(e),
221                }
222            }
223            if !ret {
224                return Err(self.error_if_invalid_data.take().unwrap());
225            }
226            if let BrotliEncoderOperation::BROTLI_OPERATION_FLUSH = op {
227                if self.state.has_more_output() {
228                    continue;
229                }
230                return Ok(());
231            }
232            if self.state.is_finished() {
233                return Ok(());
234            }
235        }
236    }
237
238    pub fn get_ref(&self) -> &W {
239        self.output.as_ref().unwrap()
240    }
241    pub fn get_mut(&mut self) -> &mut W {
242        self.output.as_mut().unwrap()
243    }
244    pub fn into_inner(mut self) -> W {
245        match self.flush_or_close(BrotliEncoderOperation::BROTLI_OPERATION_FINISH) {
246            Ok(_) => {}
247            Err(_) => {}
248        }
249        self.output.take().unwrap()
250    }
251}
252
253impl<ErrType, W: CustomWrite<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc> Drop
254    for CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
255{
256    fn drop(&mut self) {
257        if self.output.is_some() {
258            match self.flush_or_close(BrotliEncoderOperation::BROTLI_OPERATION_FINISH) {
259                Ok(_) => {}
260                Err(_) => {}
261            }
262        }
263        BrotliEncoderDestroyInstance(&mut self.state);
264    }
265}
266impl<ErrType, W: CustomWrite<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
267    CustomWrite<ErrType> for CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
268{
269    fn write(&mut self, buf: &[u8]) -> Result<usize, ErrType> {
270        let mut nop_callback =
271            |_data: &mut interface::PredictionModeContextMap<interface::InputReferenceMut>,
272             _cmds: &mut [interface::StaticCommand],
273             _mb: interface::InputPair,
274             _mfv: &mut Alloc| ();
275        let mut avail_in = buf.len();
276        let mut input_offset: usize = 0;
277        while avail_in != 0 {
278            let mut output_offset = 0;
279            let mut avail_out = self.output_buffer.slice_mut().len();
280            let ret = self.state.compress_stream(
281                BrotliEncoderOperation::BROTLI_OPERATION_PROCESS,
282                &mut avail_in,
283                buf,
284                &mut input_offset,
285                &mut avail_out,
286                self.output_buffer.slice_mut(),
287                &mut output_offset,
288                &mut self.total_out,
289                &mut nop_callback,
290            );
291            if output_offset > 0 {
292                let zero_err = &mut self.error_if_zero_bytes_written;
293                let fallback = &mut self.error_if_invalid_data;
294                match write_all(
295                    self.output.as_mut().unwrap(),
296                    &self.output_buffer.slice_mut()[..output_offset],
297                    || {
298                        if let Some(err) = zero_err.take() {
299                            return Some(err);
300                        }
301                        fallback.take()
302                    },
303                ) {
304                    Ok(_) => {}
305                    Err(e) => return Err(e),
306                }
307            }
308            if !ret {
309                return Err(self.error_if_invalid_data.take().unwrap());
310            }
311        }
312        Ok(buf.len())
313    }
314    fn flush(&mut self) -> Result<(), ErrType> {
315        match self.flush_or_close(BrotliEncoderOperation::BROTLI_OPERATION_FLUSH) {
316            Ok(_) => {}
317            Err(e) => return Err(e),
318        }
319        self.output.as_mut().unwrap().flush()
320    }
321}