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