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