bzip2/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Bzip compression for Rust
//!
//! This library contains bindings to [`libbz2`] to support bzip compression and
//! decompression for Rust. The streams offered in this library are primarily
//! found in the [`mod@read`] and [`mod@write`] modules. Both compressors and
//! decompressors are available in each module depending on what operation you
//! need.
//!
//! A more low-level interface, much closer to the interface of [`libbz2`], is
//! available via the [`Compress`] and [`Decompress`] structs.
//!
//! [`libbz2`]: https://sourceware.org/bzip2/manual/manual.html
//!
//! # Example
//!
//! ```
//! use std::io::{BufRead, Read, Write};
//! use bzip2::Compression;
//! use bzip2::read::{BzEncoder, BzDecoder};
//!
//! // Round trip some bytes from a byte source, into a compressor, into a
//! // decompressor, and finally into a vector.
//! let data = "Hello, World!".as_bytes();
//! let compressor = BzEncoder::new(data, Compression::best());
//! let mut decompressor = BzDecoder::new(compressor);
//!
//! let mut contents = String::new();
//! decompressor.read_to_string(&mut contents).unwrap();
//! assert_eq!(contents, "Hello, World!");
//! ```
//!
//! # Multistreams (e.g. Wikipedia or pbzip2)
//!
//! Some tools such as pbzip2 or data from sources such as Wikipedia
//! are encoded as so called bzip2 "multistreams," meaning they
//! contain back to back chunks of bzip'd data. `BzDecoder` does not
//! attempt to convert anything after the the first bzip chunk in the
//! source stream. Thus, if you wish to decode all bzip chunks from
//! the input until end of file, use `MultiBzDecoder`.
//!
//! *Protip*: If you use `BzDecoder` to decode data and the output is
//! incomplete and exactly 900K bytes, you probably need a
//! `MultiBzDecoder`.
//!
//! All methods are internally capable of working with streams that may return
//! [`ErrorKind::WouldBlock`](std::io::ErrorKind::WouldBlock) when they're not
//! ready to perform the particular operation.
//!
//! Note that care needs to be taken when using these objects, however. The
//! Tokio runtime, in particular, requires that data is fully flushed before
//! dropping streams. For compatibility with blocking streams all streams are
//! flushed/written when they are dropped, and this is not always a suitable
//! time to perform I/O. If I/O streams are flushed before drop, however, then
//! these operations will be a noop.

#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/bzip2/")]

#[cfg(not(feature = "libbz2-rs-sys"))]
extern crate bzip2_sys as ffi;
#[cfg(feature = "libbz2-rs-sys")]
extern crate libbz2_rs_sys as ffi;
#[cfg(test)]
extern crate partial_io;
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
extern crate rand;

pub use mem::{Action, Compress, Decompress, Error, Status};

mod mem;

pub mod bufread;
pub mod read;
pub mod write;

/// When compressing data, the compression level can be specified by a value in
/// this enum.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Compression(u32);

impl Compression {
    /// Create a new compression spec with a specific numeric level in the range `1..=9`.
    ///
    /// # Panics
    ///
    /// A level outside of the `1..=9` range will throw a panic. Use [`Self::try_new`] to
    /// gracefully handle invalid levels (e.g. from user input).
    #[track_caller]
    pub const fn new(level: u32) -> Compression {
        match Self::try_new(level) {
            Some(v) => v,
            None => panic!("expected a compression level in the range 1..=9"),
        }
    }

    /// Create a new compression spec with a specific numeric level in the range `1..=9`.
    pub const fn try_new(level: u32) -> Option<Compression> {
        match level {
            1..=9 => Some(Compression(level)),
            _ => None,
        }
    }

    /// Do not compress.
    #[deprecated(since = "0.5.1", note = "libbz2 does not support compression level 0")]
    pub fn none() -> Compression {
        Compression(0)
    }

    /// Optimize for the best speed of encoding.
    pub const fn fast() -> Compression {
        Compression(1)
    }

    /// Optimize for smallest output size.
    pub const fn best() -> Compression {
        Compression(9)
    }

    /// Return the compression level as an integer.
    pub const fn level(&self) -> u32 {
        self.0
    }
}

impl Default for Compression {
    /// Choose the default compression, a balance between speed and size.
    fn default() -> Compression {
        Compression(6)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[should_panic]
    fn new_level_0() {
        Compression::new(0);
    }

    #[test]
    #[should_panic]
    fn new_level_10() {
        Compression::new(10);
    }

    #[test]
    fn try_new() {
        assert!(Compression::try_new(0).is_none());
        assert!(Compression::try_new(10).is_none());

        assert_eq!(Compression::try_new(1), Some(Compression::fast()));
        assert_eq!(Compression::try_new(6), Some(Compression::default()));
        assert_eq!(Compression::try_new(9), Some(Compression::best()));
    }
}