1 //! Raw interface to in-memory compression/decompression streams
2 
3 use std::error;
4 use std::fmt;
5 use std::io;
6 use std::mem;
7 use std::ptr;
8 use std::slice;
9 
10 use brotli_sys;
11 use libc::c_int;
12 
13 use super::CompressParams;
14 
15 /// In-memory state for decompressing brotli-encoded data.
16 ///
17 /// This stream is at the heart of the I/O streams and is used to decompress an
18 /// incoming brotli stream.
19 pub struct Decompress {
20     state: *mut brotli_sys::BrotliDecoderState,
21 }
22 
23 unsafe impl Send for Decompress {}
24 unsafe impl Sync for Decompress {}
25 
26 /// In-memory state for compressing/encoding data with brotli
27 ///
28 /// This stream is at the heart of the I/O encoders and is used to compress
29 /// data.
30 pub struct Compress {
31     state: *mut brotli_sys::BrotliEncoderState,
32 }
33 
34 unsafe impl Send for Compress {}
35 unsafe impl Sync for Compress {}
36 
37 /// Possible choices for the operation performed by the compressor.
38 ///
39 /// When using any operation except `Process`, you must *not* alter the
40 /// input buffer or use a different operation until the current operation
41 /// has 'completed'. An operation may need to be repeated with more space to
42 /// write data until it can complete.
43 #[repr(isize)]
44 #[derive(Copy,Clone,Debug,PartialEq,Eq)]
45 pub enum CompressOp {
46     /// Compress input data
47     Process = brotli_sys::BROTLI_OPERATION_PROCESS as isize,
48     /// Compress input data, ensuring that all input so far has been
49     /// written out
50     Flush = brotli_sys::BROTLI_OPERATION_FLUSH as isize,
51     /// Compress input data, ensuring that all input so far has been
52     /// written out and then finalizing the stream so no more data can
53     /// be written
54     Finish = brotli_sys::BROTLI_OPERATION_FINISH as isize,
55     /// Emit a metadata block to the stream, an opaque piece of out-of-band
56     /// data that does not interfere with the main stream of data. Metadata
57     /// blocks *must* be no longer than 16MiB
58     EmitMetadata = brotli_sys::BROTLI_OPERATION_EMIT_METADATA as isize,
59 }
60 
61 /// Error that can happen from decompressing or compressing a brotli stream.
62 #[derive(Debug, Clone, PartialEq)]
63 pub struct Error(());
64 
65 /// Indication of whether a compression operation is 'complete'. This does
66 /// not indicate whether the whole stream is complete - see `Compress::compress`
67 /// for details.
68 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
69 pub enum CoStatus {
70     /// The operation completed successfully
71     Finished,
72     /// The operation has more work to do and needs to be called again with the
73     /// same buffer
74     Unfinished,
75 }
76 
77 /// Possible status results returned from decompressing.
78 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
79 pub enum DeStatus {
80     /// Decompression was successful and has finished
81     Finished,
82     /// More input is needed to continue
83     NeedInput,
84     /// More output is needed to continue
85     NeedOutput,
86 }
87 
88 impl Decompress {
89     /// Creates a new brotli decompression/decoding stream ready to receive
90     /// data.
new() -> Decompress91     pub fn new() -> Decompress {
92         unsafe {
93             let state = brotli_sys::BrotliDecoderCreateInstance(None, None, 0 as *mut _);
94             assert!(!state.is_null());
95             Decompress { state: state }
96         }
97     }
98 
99     /// Decompress some input data and write it to a buffer of output data.
100     ///
101     /// This function will decompress the data in `input` and place the output
102     /// in `output`, returning the result. Possible statuses that can be
103     /// returned are that the stream is finished, more input is needed, or more
104     /// output space is needed.
105     ///
106     /// The `input` slice is updated to point to the remaining data that was not
107     /// consumed, and the `output` slice is updated to point to the portion of
108     /// the output slice that still needs to be filled in.
109     ///
110     /// # Errors
111     ///
112     /// If the input stream is not a valid brotli stream, then an error is
113     /// returned.
decompress(&mut self, input: &mut &[u8], output: &mut &mut [u8]) -> Result<DeStatus, Error>114     pub fn decompress(&mut self,
115                       input: &mut &[u8],
116                       output: &mut &mut [u8]) -> Result<DeStatus, Error> {
117         let mut available_in = input.len();
118         let mut next_in = input.as_ptr();
119         let mut available_out = output.len();
120         let mut next_out = output.as_mut_ptr();
121         let r = unsafe {
122             brotli_sys::BrotliDecoderDecompressStream(self.state,
123                                                       &mut available_in,
124                                                       &mut next_in,
125                                                       &mut available_out,
126                                                       &mut next_out,
127                                                       ptr::null_mut())
128         };
129         *input = &input[input.len() - available_in..];
130         let out_len = output.len();
131         *output = &mut mem::replace(output, &mut [])[out_len - available_out..];
132         Decompress::rc(r)
133     }
134 
135     /// Retrieve a slice of the internal decompressor buffer up to `size_limit` in length
136     /// (unlimited length if `None`), consuming it. As the internal buffer may not be
137     /// contiguous, consecutive calls may return more output until this function returns
138     /// `None`.
take_output(&mut self, size_limit: Option<usize>) -> Option<&[u8]>139     pub fn take_output(&mut self, size_limit: Option<usize>) -> Option<&[u8]> {
140         if let Some(0) = size_limit { return None }
141         let mut size_limit = size_limit.unwrap_or(0); // 0 now means unlimited
142         unsafe {
143             let ptr = brotli_sys::BrotliDecoderTakeOutput(self.state, &mut size_limit);
144             if size_limit == 0 { // ptr may or may not be null
145                 None
146             } else {
147                 assert!(!ptr.is_null());
148                 Some(slice::from_raw_parts(ptr, size_limit))
149             }
150         }
151     }
152 
rc(rc: brotli_sys::BrotliDecoderResult) -> Result<DeStatus, Error>153     fn rc(rc: brotli_sys::BrotliDecoderResult) -> Result<DeStatus, Error> {
154         match rc {
155             // TODO: get info from BrotliDecoderGetErrorCode/BrotliDecoderErrorString
156             // for these decode errors
157             brotli_sys::BROTLI_DECODER_RESULT_ERROR => Err(Error(())),
158             brotli_sys::BROTLI_DECODER_RESULT_SUCCESS => Ok(DeStatus::Finished),
159             brotli_sys::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT => Ok(DeStatus::NeedInput),
160             brotli_sys::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT => Ok(DeStatus::NeedOutput),
161             n => panic!("unknown return code: {}", n)
162         }
163     }
164 }
165 
166 impl Drop for Decompress {
drop(&mut self)167     fn drop(&mut self) {
168         unsafe {
169             brotli_sys::BrotliDecoderDestroyInstance(self.state);
170         }
171     }
172 }
173 
174 /// Decompress data in one go in memory.
175 ///
176 /// Decompresses the data in `input` into the `output` buffer. The `output`
177 /// buffer is updated to point to the actual output slice if successful, or
178 /// an error is returned. The output buffer being too small is considered
179 /// to be an error.
decompress_buf(input: &[u8], output: &mut &mut [u8]) -> Result<usize, Error>180 pub fn decompress_buf(input: &[u8],
181                       output: &mut &mut [u8]) -> Result<usize, Error> {
182     let mut size = output.len();
183     let r = unsafe {
184         brotli_sys::BrotliDecoderDecompress(input.len(),
185                                             input.as_ptr(),
186                                             &mut size,
187                                             output.as_mut_ptr())
188     };
189     *output = &mut mem::replace(output, &mut [])[..size];
190     if r == 0 {
191         Err(Error(()))
192     } else {
193         Ok(size)
194     }
195 }
196 
197 impl Compress {
198     /// Creates a new compressor ready to encode data into brotli
new() -> Compress199     pub fn new() -> Compress {
200         unsafe {
201             let state = brotli_sys::BrotliEncoderCreateInstance(None, None, 0 as *mut _);
202             assert!(!state.is_null());
203 
204             Compress { state: state }
205         }
206     }
207 
208     // TODO: add the BrotliEncoderOperation variants of
209     // BrotliEncoderCompressStream here
210 
211     /// Pass some input data to the compressor and write it to a buffer of
212     /// output data, compressing or otherwise handling it as instructed by
213     /// the specified operation.
214     ///
215     /// This function will handle the data in `input` and place the output
216     /// in `output`, returning the Result. Possible statuses are that the
217     /// operation is complete or incomplete.
218     ///
219     /// The `input` slice is updated to point to the remaining data that was not
220     /// consumed, and the `output` slice is updated to point to the portion of
221     /// the output slice that still needs to be filled in.
222     ///
223     /// If the result of a compress operation is `Unfinished` (which it may be
224     /// for any operation except `Process`), you *must* call the operation again
225     /// with the same operation and input buffer and more space to output to.
226     /// `Process` will never return `Unfinished`, but it is a logic error to end
227     /// a buffer without calling either `Flush` or `Finish` as some output data
228     /// may not have been written.
229     ///
230     /// # Errors
231     ///
232     /// Returns an error if brotli encountered an error while processing the stream.
233     ///
234     /// # Examples
235     ///
236     /// ```
237     /// use brotli2::raw::{Error, Compress, CompressOp, CoStatus, decompress_buf};
238     ///
239     /// // An example of compressing `input` into the destination vector
240     /// // `output`, expanding as necessary
241     /// fn compress_vec(mut input: &[u8],
242     ///                 output: &mut Vec<u8>) -> Result<(), Error> {
243     ///     let mut compress = Compress::new();
244     ///     let nilbuf = &mut &mut [][..];
245     ///     loop {
246     ///         // Compressing to a buffer is easiest when the slice is already
247     ///         // available - since we need to grow, extend from compressor
248     ///         // internal buffer.
249     ///         let status = try!(compress.compress(CompressOp::Finish, &mut input, nilbuf));
250     ///         while let Some(buf) = compress.take_output(None) {
251     ///             output.extend_from_slice(buf)
252     ///         }
253     ///         match status {
254     ///             CoStatus::Finished => break,
255     ///             CoStatus::Unfinished => (),
256     ///         }
257     ///     }
258     ///     Ok(())
259     /// }
260     ///
261     /// fn assert_roundtrip(data: &[u8]) {
262     ///     let mut compressed = Vec::new();
263     ///     compress_vec(data, &mut compressed).unwrap();
264     ///
265     ///     let mut decompressed = [0; 2048];
266     ///     let mut decompressed = &mut decompressed[..];
267     ///     decompress_buf(&compressed, &mut decompressed).unwrap();
268     ///     assert_eq!(decompressed, data);
269     /// }
270     ///
271     /// assert_roundtrip(b"Hello, World!");
272     /// assert_roundtrip(b"");
273     /// assert_roundtrip(&[6; 1024]);
274     /// ```
compress(&mut self, op: CompressOp, input: &mut &[u8], output: &mut &mut [u8]) -> Result<CoStatus, Error>275     pub fn compress(&mut self,
276                     op: CompressOp,
277                     input: &mut &[u8],
278                     output: &mut &mut [u8]) -> Result<CoStatus, Error> {
279         let mut available_in = input.len();
280         let mut next_in = input.as_ptr();
281         let mut available_out = output.len();
282         let mut next_out = output.as_mut_ptr();
283         let r = unsafe {
284             brotli_sys::BrotliEncoderCompressStream(self.state,
285                                                     op as brotli_sys::BrotliEncoderOperation,
286                                                     &mut available_in,
287                                                     &mut next_in,
288                                                     &mut available_out,
289                                                     &mut next_out,
290                                                     ptr::null_mut())
291         };
292         *input = &input[input.len() - available_in..];
293         let out_len = output.len();
294         *output = &mut mem::replace(output, &mut [])[out_len - available_out..];
295         if r == 0 { return Err(Error(())) }
296         Ok(if op == CompressOp::Process {
297             CoStatus::Finished
298         } else if available_in != 0 {
299             CoStatus::Unfinished
300         } else if unsafe { brotli_sys::BrotliEncoderHasMoreOutput(self.state) } == 1 {
301             CoStatus::Unfinished
302         } else if op == CompressOp::Finish &&
303                 unsafe { brotli_sys::BrotliEncoderIsFinished(self.state) } == 0 {
304             CoStatus::Unfinished
305         } else {
306             CoStatus::Finished
307         })
308     }
309 
310     /// Retrieve a slice of the internal compressor buffer up to `size_limit` in length
311     /// (unlimited length if `None`), consuming it. As the internal buffer may not be
312     /// contiguous, consecutive calls may return more output until this function returns
313     /// `None`.
take_output(&mut self, size_limit: Option<usize>) -> Option<&[u8]>314     pub fn take_output(&mut self, size_limit: Option<usize>) -> Option<&[u8]> {
315         if let Some(0) = size_limit { return None }
316         let mut size_limit = size_limit.unwrap_or(0); // 0 now means unlimited
317         unsafe {
318             let ptr = brotli_sys::BrotliEncoderTakeOutput(self.state, &mut size_limit);
319             if size_limit == 0 { // ptr may or may not be null
320                 None
321             } else {
322                 assert!(!ptr.is_null());
323                 Some(slice::from_raw_parts(ptr, size_limit))
324             }
325         }
326     }
327 
328     /// Configure the parameters of this compression session.
329     ///
330     /// Note that this is likely to only successful if called before compression
331     /// starts.
set_params(&mut self, params: &CompressParams)332     pub fn set_params(&mut self, params: &CompressParams) {
333         unsafe {
334             brotli_sys::BrotliEncoderSetParameter(self.state,
335                                                   brotli_sys::BROTLI_PARAM_MODE,
336                                                   params.mode);
337             brotli_sys::BrotliEncoderSetParameter(self.state,
338                                                   brotli_sys::BROTLI_PARAM_QUALITY,
339                                                   params.quality);
340             brotli_sys::BrotliEncoderSetParameter(self.state,
341                                                   brotli_sys::BROTLI_PARAM_LGWIN,
342                                                   params.lgwin);
343             brotli_sys::BrotliEncoderSetParameter(self.state,
344                                                   brotli_sys::BROTLI_PARAM_LGBLOCK,
345                                                   params.lgblock);
346             // TODO: add these two
347             // brotli_sys::BrotliEncoderSetParameter(self.state,
348             //                                       brotli_sys::BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING,
349             //                                       params.lgblock);
350             // brotli_sys::BrotliEncoderSetParameter(self.state,
351             //                                       brotli_sys::BROTLI_PARAM_SIZE_HINT,
352             //                                       params.lgblock);
353         }
354     }
355 }
356 
357 impl Drop for Compress {
drop(&mut self)358     fn drop(&mut self) {
359         unsafe {
360             brotli_sys::BrotliEncoderDestroyInstance(self.state);
361         }
362     }
363 }
364 
365 /// Compresses the data in `input` into `output`.
366 ///
367 /// The `output` buffer is updated to point to the exact slice which contains
368 /// the output data.
369 ///
370 /// If successful, the amount of compressed bytes are returned (the size of the
371 /// `output` slice), or an error is returned. The output buffer being too small
372 /// is considered to be an error.
compress_buf(params: &CompressParams, input: &[u8], output: &mut &mut [u8]) -> Result<usize, Error>373 pub fn compress_buf(params: &CompressParams,
374                     input: &[u8],
375                     output: &mut &mut [u8]) -> Result<usize, Error> {
376     let mut size = output.len();
377     let r = unsafe {
378         brotli_sys::BrotliEncoderCompress(params.quality as c_int,
379                                           params.lgwin as c_int,
380                                           params.mode as brotli_sys::BrotliEncoderMode,
381                                           input.len(),
382                                           input.as_ptr(),
383                                           &mut size,
384                                           output.as_mut_ptr())
385     };
386     *output = &mut mem::replace(output, &mut [])[..size];
387     if r == 0 {
388         Err(Error(()))
389     } else {
390         Ok(size)
391     }
392 }
393 
394 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result395     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
396         error::Error::description(self).fmt(f)
397     }
398 }
399 
400 impl error::Error for Error {
description(&self) -> &str401     fn description(&self) -> &str {
402         "brotli error"
403     }
404 }
405 
406 impl From<Error> for io::Error {
from(_err: Error) -> io::Error407     fn from(_err: Error) -> io::Error {
408         io::Error::new(io::ErrorKind::Other, "brotli error")
409     }
410 }
411 
412 #[cfg(test)]
413 mod tests {
414     use super::*;
415 
416     #[test]
decompress_error()417     fn decompress_error() {
418         let mut d = Decompress::new();
419         d.decompress(&mut &[0; 1024][..], &mut &mut [0; 2048][..]).unwrap_err();
420     }
421 
422     #[test]
compress_buf_smoke()423     fn compress_buf_smoke() {
424         let mut data = [0; 128];
425         let mut data = &mut data[..];
426         compress_buf(&CompressParams::new(), b"hello!", &mut data).unwrap();
427 
428         let mut dst = [0; 128];
429         {
430             let mut dst = &mut dst[..];
431             let n = decompress_buf(data, &mut dst).unwrap();
432             assert_eq!(n, dst.len());
433             assert_eq!(dst.len(), 6);
434         }
435         assert_eq!(&dst[..6], b"hello!");
436     }
437 
438     #[test]
decompress_smoke()439     fn decompress_smoke() {
440         let mut data = [0; 128];
441         let mut data = &mut data[..];
442         compress_buf(&CompressParams::new(), b"hello!", &mut data).unwrap();
443 
444         let mut d = Decompress::new();
445         let mut dst = [0; 128];
446         {
447             let mut data = &data[..];
448             let mut dst = &mut dst[..];
449             assert_eq!(d.decompress(&mut data, &mut dst), Ok(DeStatus::Finished));
450         }
451         assert_eq!(&dst[..6], b"hello!");
452     }
453 
454     #[test]
compress_smoke()455     fn compress_smoke() {
456         let mut data = [0; 128];
457         let mut dst = [0; 128];
458 
459         {
460             let mut data = &mut data[..];
461             let mut c = Compress::new();
462             let mut input = &mut &b"hello!"[..];
463             assert_eq!(c.compress(CompressOp::Finish, input, &mut data), Ok(CoStatus::Finished));
464             assert!(input.is_empty());
465         }
466         decompress_buf(&data, &mut &mut dst[..]).unwrap();
467         assert_eq!(&dst[..6], b"hello!");
468 
469         {
470             let mut data = &mut data[..];
471             let mut c = Compress::new();
472             let mut input = &mut &b"hel"[..];
473             assert_eq!(c.compress(CompressOp::Flush, input, &mut data), Ok(CoStatus::Finished));
474             assert!(input.is_empty());
475             let mut input = &mut &b"lo!"[..];
476             assert_eq!(c.compress(CompressOp::Finish, input, &mut data), Ok(CoStatus::Finished));
477             assert!(input.is_empty());
478         }
479         decompress_buf(&data, &mut &mut dst[..]).unwrap();
480         assert_eq!(&dst[..6], b"hello!");
481     }
482 }
483