libdeflater/
lib.rs

1//! Rust bindings to [`libdeflate`], a DEFLATE-based buffer
2//! compression/decompression library that works with raw DEFLATE,
3//! zlib, and gzip data.
4//!
5//! **Warning**: Libdeflate is targeted at *specialized*
6//! performance-sensitive use-cases where developers have a good
7//! understanding of their input/output data. Developers looking for a
8//! general-purpose DEFLATE library should use something like
9//! [`flate2`], which can handle a much wider range of inputs (network
10//! streams, large files, etc.).
11//!
12//! [`libdeflate`]: https://github.com/ebiggers/libdeflate
13//! [`flate2`]: https://github.com/alexcrichton/flate2-rs
14//!
15//! # Decompression
16//!
17//! [`Decompressor::new`] can be used to construct a [`Decompressor`],
18//! which can decompress:
19//!
20//! - DEFLATE data ([`deflate_decompress`])
21//! - zlib data ([`zlib_decompress`])
22//! - gzip data ([`gzip_decompress`])
23//!
24//! **Note**: `libdeflate` requires that the input *and* output
25//! buffers are pre-allocated before decompressing. Because of this,
26//! you will at least need to know the upper bound on how large the
27//! compressed data will decompress to; otherwise, a `decompress_*`
28//! function call will return `DecompressionError::InsufficientSpace`
29//!
30//! [`Decompressor::new`]: struct.Decompressor.html#method.new
31//! [`Decompressor`]: struct.Decompressor.html
32//! [`deflate_decompress`]: struct.Decompressor.html#method.deflate_decompress
33//! [`zlib_decompress`]: struct.Decompressor.html#method.zlib_decompress
34//! [`gzip_decompress`]: struct.Decompressor.html#method.gzip_decompress
35//! [`DecompressionError::InsufficientSpace`]: enum.DecompressionError.html
36//!
37//! # Compression
38//!
39//! `Compressor::new` can be used to construct a [`Compressor`], which
40//! can compress data into the following formats:
41//!
42//! - DEFLATE ([`deflate_compress`])
43//! - zlib ([`zlib_compress`])
44//! - gzip ([`gzip_compress`])
45//!
46//! Because buffers must be allocated up-front, developers need to
47//! supply these functions with output buffers that are big enough to
48//! fit the compressed data. The maximum size of the compressed data
49//! can be found with the associated `*_bound` methods:
50//!
51//! - [`deflate_compress_bound`]
52//! - [`zlib_compress_bound`]
53//! - [`gzip_compress_bound`]
54//!
55//! [`Compressor::new`]: struct.Compressor.html#method.new
56//! [`Compressor`]: struct.Compressor.html
57//! [`deflate_compress`]: struct.Compressor.html#method.deflate_compress
58//! [`zlib_compress`]: struct.Compressor.html#method.zlib_compress
59//! [`gzip_compress`]: struct.Compressor.html#method.gzip_compress
60//! [`deflate_compress_bound`]: struct.Compressor.html#method.deflate_compress_bound
61//! [`zlib_compress_bound`]: struct.Compressor.html#method.zlib_compress_bound
62//! [`gzip_compress_bound`]: struct.Compressor.html#method.gzip_compress_bound
63
64use std::error::Error;
65use std::fmt;
66use std::ptr::NonNull;
67use libdeflate_sys::{libdeflate_decompressor,
68                            libdeflate_free_decompressor,
69                            libdeflate_gzip_decompress,
70                            libdeflate_zlib_decompress,
71                            libdeflate_deflate_decompress,
72                            libdeflate_result,
73                            libdeflate_result_LIBDEFLATE_SUCCESS,
74                            libdeflate_result_LIBDEFLATE_BAD_DATA,
75                            libdeflate_result_LIBDEFLATE_INSUFFICIENT_SPACE,
76                            libdeflate_compressor,
77                            libdeflate_deflate_compress_bound,
78                            libdeflate_deflate_compress,
79                            libdeflate_zlib_compress_bound,
80                            libdeflate_zlib_compress,
81                            libdeflate_gzip_compress_bound,
82                            libdeflate_gzip_compress,
83                            libdeflate_free_compressor,
84                            libdeflate_crc32,
85                            libdeflate_adler32};
86
87#[cfg(feature = "use_rust_alloc")]
88mod malloc_wrapper;
89
90unsafe fn alloc_compressor(compression_level: std::os::raw::c_int) -> *mut libdeflate_compressor {
91    #[cfg(feature = "use_rust_alloc")]
92    { libdeflate_sys::libdeflate_alloc_compressor_ex(compression_level, &malloc_wrapper::OPTIONS) }
93    #[cfg(not(feature = "use_rust_alloc"))]
94    { libdeflate_sys::libdeflate_alloc_compressor(compression_level) }
95}
96
97unsafe fn alloc_decompressor() -> *mut libdeflate_decompressor {
98    #[cfg(feature = "use_rust_alloc")]
99    { libdeflate_sys::libdeflate_alloc_decompressor_ex(&malloc_wrapper::OPTIONS) }
100    #[cfg(not(feature = "use_rust_alloc"))]
101    { libdeflate_sys::libdeflate_alloc_decompressor() }
102}
103
104/// A `libdeflate` decompressor that can inflate DEFLATE, zlib, or
105/// gzip data.
106pub struct Decompressor {
107    p: NonNull<libdeflate_decompressor>,
108}
109unsafe impl Send for Decompressor {}
110
111/// An error that may be returned by one of the
112/// [`Decompressor`](struct.Decompressor.html)'s `decompress_*`
113/// methods when a decompression cannot be performed.
114#[derive(Debug, PartialEq)]
115pub enum DecompressionError {
116    /// The provided data is invalid in some way. For example, the
117    /// checksum in the data revealed possible corruption, magic
118    /// numbers in the data do not match expectations, etc.
119    BadData,
120
121    /// The provided output buffer is not large enough to accomodate
122    /// the decompressed data.
123    InsufficientSpace,
124}
125
126impl fmt::Display for DecompressionError {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        match &self {
129            DecompressionError::BadData => write!(f, "the data provided to a libdeflater *_decompress function call was invalid in some way (e.g. bad magic numbers, bad checksum)"),
130            DecompressionError::InsufficientSpace => write!(f, "a buffer provided to a libdeflater *_decompress function call was too small to accommodate the decompressed data")
131        }
132    }
133}
134
135impl Error for DecompressionError {}
136
137/// A result returned by decompression methods
138type DecompressionResult<T> = std::result::Result<T, DecompressionError>;
139
140impl Default for Decompressor {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146#[allow(non_upper_case_globals)]
147impl Decompressor {
148
149    /// Returns a newly constructed instance of a `Decompressor`.
150    pub fn new() -> Decompressor {
151        unsafe {
152            let ptr = alloc_decompressor();
153            if let Some(ptr) = NonNull::new(ptr) {
154                Decompressor{ p: ptr }
155            } else {
156                panic!("libdeflate_alloc_decompressor returned NULL: out of memory");
157            }
158        }
159    }
160
161    /// Decompresses `gz_data` (a buffer containing
162    /// [`gzip`](https://tools.ietf.org/html/rfc1952) data) and writes
163    /// the decompressed data into `out`. Returns the number of
164    /// decompressed bytes written into `out`, or an error (see
165    /// [`DecompressionError`](enum.DecompressionError.html) for error
166    /// cases).
167    pub fn gzip_decompress(&mut self,
168                           gz_data: &[u8],
169                           out: &mut [u8]) -> DecompressionResult<usize> {
170        unsafe {
171            let mut out_nbytes = 0;
172            let in_ptr = gz_data.as_ptr() as *const std::ffi::c_void;
173            let out_ptr = out.as_mut_ptr() as *mut std::ffi::c_void;
174            let ret: libdeflate_result =
175                libdeflate_gzip_decompress(self.p.as_ptr(),
176                                           in_ptr,
177                                           gz_data.len(),
178                                           out_ptr,
179                                           out.len(),
180                                           &mut out_nbytes);
181            match ret {
182                libdeflate_result_LIBDEFLATE_SUCCESS => {
183                    Ok(out_nbytes)
184                },
185                libdeflate_result_LIBDEFLATE_BAD_DATA => {
186                    Err(DecompressionError::BadData)
187                },
188                libdeflate_result_LIBDEFLATE_INSUFFICIENT_SPACE => {
189                    Err(DecompressionError::InsufficientSpace)
190                },
191                _ => {
192                    panic!("libdeflate_gzip_decompress returned an unknown error type: this is an internal bug that **must** be fixed");
193                }
194            }
195        }
196    }
197
198    /// Decompresses `zlib_data` (a buffer containing
199    /// [`zlib`](https://www.ietf.org/rfc/rfc1950.txt) data) and
200    /// writes the decompressed data to `out`. Returns the number of
201    /// decompressed bytes written into `out`, or an error (see
202    /// [`DecompressionError`](enum.DecompressionError.html) for error
203    /// cases).
204    pub fn zlib_decompress(&mut self,
205                           zlib_data: &[u8],
206                           out: &mut [u8]) -> DecompressionResult<usize> {
207        unsafe {
208            let mut out_nbytes = 0;
209            let in_ptr = zlib_data.as_ptr() as *const std::ffi::c_void;
210            let out_ptr = out.as_mut_ptr() as *mut std::ffi::c_void;
211            let ret: libdeflate_result =
212                libdeflate_zlib_decompress(self.p.as_ptr(),
213                                           in_ptr,
214                                           zlib_data.len(),
215                                           out_ptr,
216                                           out.len(),
217                                           &mut out_nbytes);
218
219            match ret {
220                libdeflate_result_LIBDEFLATE_SUCCESS => {
221                    Ok(out_nbytes)
222                },
223                libdeflate_result_LIBDEFLATE_BAD_DATA => {
224                    Err(DecompressionError::BadData)
225                },
226                libdeflate_result_LIBDEFLATE_INSUFFICIENT_SPACE => {
227                    Err(DecompressionError::InsufficientSpace)
228                },
229                _ => {
230                    panic!("libdeflate_zlib_decompress returned an unknown error type: this is an internal bug that **must** be fixed");
231                }
232            }
233        }
234    }
235
236    /// Decompresses `deflate_data` (a buffer containing
237    /// [`deflate`](https://tools.ietf.org/html/rfc1951) data) and
238    /// writes the decompressed data to `out`. Returns the number of
239    /// decompressed bytes written into `out`, or an error (see
240    /// [`DecompressionError`](enum.DecompressionError.html) for error
241    /// cases).
242    pub fn deflate_decompress(&mut self,
243                              deflate_data: &[u8],
244                              out: &mut [u8]) -> DecompressionResult<usize> {
245        unsafe {
246            let mut out_nbytes = 0;
247            let in_ptr = deflate_data.as_ptr() as *const std::ffi::c_void;
248            let out_ptr = out.as_mut_ptr() as *mut std::ffi::c_void;
249            let ret: libdeflate_result =
250                libdeflate_deflate_decompress(self.p.as_ptr(),
251                                              in_ptr,
252                                              deflate_data.len(),
253                                              out_ptr,
254                                              out.len(),
255                                              &mut out_nbytes);
256
257            match ret {
258                libdeflate_result_LIBDEFLATE_SUCCESS => {
259                    Ok(out_nbytes)
260                },
261                libdeflate_result_LIBDEFLATE_BAD_DATA => {
262                    Err(DecompressionError::BadData)
263                },
264                libdeflate_result_LIBDEFLATE_INSUFFICIENT_SPACE => {
265                    Err(DecompressionError::InsufficientSpace)
266                },
267                _ => {
268                    panic!("libdeflate_deflate_decompress returned an unknown error type: this is an internal bug that **must** be fixed");
269                }
270            }
271        }
272    }
273}
274
275impl Drop for Decompressor {
276    fn drop(&mut self) {
277        unsafe {
278            libdeflate_free_decompressor(self.p.as_ptr());
279        }
280    }
281}
282
283/// Raw numeric values of compression levels that are accepted by libdeflate
284const MIN_COMPRESSION_LVL: i32 = 0;
285const DEFAULT_COMPRESSION_LVL : i32 = 6;
286const MAX_COMPRESSION_LVL: i32 = 12;
287
288/// Compression level used by a [`Compressor`](struct.Compressor.html)
289/// instance.
290#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
291pub struct CompressionLvl(i32);
292
293/// Errors that can be returned when attempting to create a
294/// [`CompressionLvl`](enum.CompressionLvl.html) from a numeric value.
295#[derive(Copy, Clone, PartialEq, Eq, Debug)]
296pub enum CompressionLvlError {
297    InvalidValue,
298}
299
300/// A result that is returned when trying to create a
301/// [`CompressionLvl`](enum.CompressionLvl.html) from a numeric value.
302type CompressionLevelResult = Result<CompressionLvl, CompressionLvlError>;
303
304impl CompressionLvl {
305    /// Try to create a valid
306    /// [`CompressionLvl`](enum.CompressionLvl.html) from a numeric
307    /// value.
308    ///
309    /// If `level` is a valid custom compression level for libdeflate,
310    /// returns a `Result::Ok(CompressionLvl)`. Otherwise, returns
311    /// `Result::Error(error)`.
312    ///
313    /// Valid compression levels for libdeflate, at time of writing,
314    /// are 1-12.
315    pub const fn new(level: i32) -> CompressionLevelResult {
316        if MIN_COMPRESSION_LVL <= level && level <= MAX_COMPRESSION_LVL {
317            Ok(CompressionLvl(level))
318        } else {
319            Err(CompressionLvlError::InvalidValue)
320        }
321    }
322
323    /// Returns the fastest compression level. This compression level
324    /// offers the highest performance but lowest compression ratio.
325    pub const fn fastest() -> CompressionLvl {
326        CompressionLvl(MIN_COMPRESSION_LVL)
327    }
328
329    /// Returns the best compression level, in terms of compression
330    /// ratio. This compression level offers the best compression
331    /// ratio but lowest performance.
332    pub const fn best() -> CompressionLvl {
333        CompressionLvl(MAX_COMPRESSION_LVL)
334    }
335
336    /// Returns an iterator that emits all compression levels
337    /// supported by `libdeflate` in ascending order.
338    pub const fn iter() -> CompressionLvlIter {
339        CompressionLvlIter(MIN_COMPRESSION_LVL)
340    }
341}
342
343impl Default for CompressionLvl {
344    /// Returns the default compression level reccomended by
345    /// libdeflate.
346    fn default() -> CompressionLvl {
347        CompressionLvl(DEFAULT_COMPRESSION_LVL)
348    }
349}
350
351/// An iterator over the
352/// [`CompressionLvl`](struct.CompressionLvl.html)s supported by the
353/// [`Compressor`](struct.Compressor.html).
354pub struct CompressionLvlIter(i32);
355
356impl Iterator for CompressionLvlIter {
357    type Item = CompressionLvl;
358
359    fn next(&mut self) -> Option<Self::Item> {
360        if self.0 <= MAX_COMPRESSION_LVL {
361            let ret = Some(CompressionLvl(self.0));
362            self.0 += 1;
363            ret
364        } else {
365            None
366        }
367    }
368}
369
370impl From<CompressionLvl> for i32 {
371    fn from(level: CompressionLvl) -> Self {
372        level.0
373    }
374}
375
376impl From<&CompressionLvl> for i32 {
377    fn from(level: &CompressionLvl) -> Self {
378        level.0
379    }
380}
381
382/// An error that may be returned when calling one of the
383/// [`Compressor`](struct.Compressor.html)'s `compress_*` methods.
384#[derive(Debug, PartialEq)]
385pub enum CompressionError {
386    InsufficientSpace,
387}
388
389impl fmt::Display for CompressionError {
390    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391        match &self {
392            CompressionError::InsufficientSpace => write!(f, "the output buffer provided to a libdeflater *_compress function call was too small for the input data"),
393        }
394    }
395}
396
397impl Error for CompressionError {}
398
399type CompressionResult<T> = std::result::Result<T, CompressionError>;
400
401/// A `libdeflate` compressor that can compress arbitrary data into
402/// DEFLATE, zlib, or gzip formats.
403pub struct Compressor {
404    p: NonNull<libdeflate_compressor>,
405}
406unsafe impl Send for Compressor {}
407
408impl Default for Compressor {
409    fn default() -> Self {
410        Self::new(CompressionLvl::default())
411    }
412}
413
414impl Compressor {
415
416    /// Returns a newly constructed `Compressor` that compresses data
417    /// with the supplied
418    /// [`CompressionLvl`](struct.CompressionLvl.html)
419    pub fn new(lvl: CompressionLvl) -> Compressor {
420        unsafe {
421            let ptr = alloc_compressor(lvl.0 as _);
422            if let Some(ptr) = NonNull::new(ptr) {
423                Compressor{ p: ptr }
424            } else {
425                panic!("libdeflate_alloc_compressor returned NULL: out of memory");
426            }
427        }
428    }
429
430    /// Returns the maximum number of bytes required to encode
431    /// `n_bytes` as [`deflate`](https://tools.ietf.org/html/rfc1951)
432    /// data. This is a hard upper-bound that assumes the worst
433    /// possible compression ratio (i.e. assumes the data cannot be
434    /// compressed), format overhead, etc.
435    pub fn deflate_compress_bound(&mut self, n_bytes: usize) -> usize {
436        unsafe {
437            libdeflate_deflate_compress_bound(self.p.as_ptr(), n_bytes)
438        }
439    }
440
441    /// Compresses `in_raw_data` as
442    /// [`deflate`](https://tools.ietf.org/html/rfc1951) data, writing
443    /// the data into `out_deflate_data`. Returns the number of bytes
444    /// written into `out_deflate_data`.
445    pub fn deflate_compress(&mut self,
446                            in_raw_data: &[u8],
447                            out_deflate_data: &mut [u8]) -> CompressionResult<usize> {
448        unsafe {
449            let in_ptr = in_raw_data.as_ptr() as *const std::ffi::c_void;
450            let out_ptr = out_deflate_data.as_mut_ptr() as *mut std::ffi::c_void;
451
452            let sz = libdeflate_deflate_compress(self.p.as_ptr(),
453                                                 in_ptr,
454                                                 in_raw_data.len(),
455                                                 out_ptr,
456                                                 out_deflate_data.len());
457
458            if sz != 0 {
459                Ok(sz)
460            } else {
461                Err(CompressionError::InsufficientSpace)
462            }
463        }
464    }
465
466    /// Returns the maximum number of bytes required to encode
467    /// `n_bytes` as [`zlib`](https://www.ietf.org/rfc/rfc1950.txt)
468    /// data. This is a hard upper-bound that assumes the worst
469    /// possible compression ratio (i.e. assumes the data cannot be
470    /// compressed), format overhead, etc.
471    pub fn zlib_compress_bound(&mut self, n_bytes: usize) -> usize {
472        unsafe {
473            libdeflate_zlib_compress_bound(self.p.as_ptr(), n_bytes)
474        }
475    }
476
477    /// Compresses `in_raw_data` as
478    /// [`zlib`](https://www.ietf.org/rfc/rfc1950.txt) data, writing
479    /// the data into `out_zlib_data`. Returns the number of bytes
480    /// written into `out_zlib_data`.
481    pub fn zlib_compress(&mut self,
482                         in_raw_data: &[u8],
483                         out_zlib_data: &mut [u8]) -> CompressionResult<usize> {
484        unsafe {
485            let in_ptr = in_raw_data.as_ptr() as *const std::ffi::c_void;
486            let out_ptr = out_zlib_data.as_mut_ptr() as *mut std::ffi::c_void;
487
488            let sz = libdeflate_zlib_compress(self.p.as_ptr(),
489                                              in_ptr,
490                                              in_raw_data.len(),
491                                              out_ptr,
492                                              out_zlib_data.len());
493
494            if sz != 0 {
495                Ok(sz)
496            } else {
497                Err(CompressionError::InsufficientSpace)
498            }
499        }
500    }
501
502    /// Returns the maximum number of bytes required to encode
503    /// `n_bytes` as [`gzip`](https://tools.ietf.org/html/rfc1952)
504    /// data. This is a hard upper-bound that assumes the worst
505    /// possible compression ratio (i.e. assumes the data cannot be
506    /// compressed), format overhead, etc.
507    pub fn gzip_compress_bound(&mut self, n_bytes: usize) -> usize {
508        unsafe {
509            libdeflate_gzip_compress_bound(self.p.as_ptr(), n_bytes)
510        }
511    }
512
513    /// Compresses `in_raw_data` as
514    /// [`gzip`](https://tools.ietf.org/html/rfc1952) data, writing
515    /// the data into `out_gzip_data`. Returns the number of bytes
516    /// written into `out_gzip_data`.
517    pub fn gzip_compress(&mut self,
518                         in_raw_data: &[u8],
519                         out_gzip_data: &mut [u8]) -> CompressionResult<usize> {
520        unsafe {
521            let in_ptr = in_raw_data.as_ptr() as *const std::ffi::c_void;
522            let out_ptr = out_gzip_data.as_mut_ptr() as *mut std::ffi::c_void;
523
524            let sz = libdeflate_gzip_compress(self.p.as_ptr(),
525                                              in_ptr,
526                                              in_raw_data.len(),
527                                              out_ptr,
528                                              out_gzip_data.len());
529
530            if sz != 0 {
531                Ok(sz)
532            } else {
533                Err(CompressionError::InsufficientSpace)
534            }
535        }
536    }
537}
538
539impl Drop for Compressor {
540    fn drop(&mut self) {
541        unsafe {
542            libdeflate_free_compressor(self.p.as_ptr());
543        }
544    }
545}
546
547/// Struct holding the state required to compute a rolling crc32
548/// value.
549pub struct Crc {
550    val: u32,
551}
552
553impl Default for Crc {
554    fn default() -> Self {
555        Self::new()
556    }
557}
558
559impl Crc {
560    /// Returns a new `Crc` instance
561    pub const fn new() -> Crc {
562        Crc { val: 0 }
563    }
564
565    /// Update the CRC with the bytes in `data`
566    pub fn update(&mut self, data: &[u8]) {
567        unsafe {
568            self.val = libdeflate_crc32(self.val,
569                                        data.as_ptr() as *const core::ffi::c_void,
570                                        data.len());
571        }
572    }
573
574    /// Returns the current CRC32 checksum
575    pub const fn sum(&self) -> u32 {
576        self.val
577    }
578}
579
580/// Returns the CRC32 checksum of the bytes in `data`.
581///
582/// Note: this is a one-shot method that requires all data
583/// up-front. Developers wanting to compute a rolling crc32 from
584/// (e.g.) a stream should use [`Crc`](struct.Crc.html)
585pub fn crc32(data: &[u8]) -> u32 {
586    let mut crc = Crc::new();
587    crc.update(&data);
588    crc.sum()
589}
590
591/// Struct holding the state required to compute a rolling adler32
592/// value.
593pub struct Adler32 {
594    val: u32,
595}
596
597impl Default for Adler32 {
598    fn default() -> Self {
599        Self::new()
600    }
601}
602
603impl Adler32 {
604    /// Returns a new `Adler32` instance (with initial adler32 value 1, which is default for adler32)
605    pub const fn new() -> Adler32 {
606        Adler32 { val: 1 }
607    }
608    /// Update the Adler32 with the bytes in `data`
609    pub fn update(&mut self, data: &[u8]) {
610        unsafe
611            {
612                self.val = libdeflate_adler32(self.val,
613                                              data.as_ptr() as *const core::ffi::c_void,
614                                              data.len());
615            }
616    }
617    /// Returns the current Adler32 checksum
618    pub const fn sum(&self) -> u32 {
619        self.val
620    }
621}
622/// Returns the Adler32 checksum of the bytes in `data`.
623///
624/// Note: this is a one-shot method that requires all data
625/// up-front. Developers wanting to compute a rolling adler32 from
626/// (e.g.) a stream should use [`Adler32`](struct.Adler32.html)
627pub fn adler32(data:&[u8]) -> u32 {
628    let mut adler32 = Adler32::new();
629    adler32.update(&data);
630    adler32.sum()
631}