redux/
lib.rs

1//! Adaptive arithmetic compression library.
2//!
3//! This crate provides standard [arithmetic coding](https://en.wikipedia.org/wiki/Arithmetic_coding)
4//! implementation that can use customized symbol probability models.
5//! This crate offers two adaptive models: `AdaptiveLinearModel` and `AdaptiveTreeModel`. Adaptive
6//! models continuously update the symbol probability distribution with each encoded symbol.
7//!
8//! * `AdaptiveLinearModel` is a straightforward, but slow implementation, present mainly for
9//! tasting and benchmarking purposes.
10//!
11//! * `AdaptiveTreeModel` is a [Fenwick tree](https://en.wikipedia.org/wiki/Fenwick_tree)-based
12//! implementation, it is advised to use this model for any uses.
13//!
14//! It is possible to use a custom model (it may or may not be adaptive) by implementing the
15//! `model::Model` trait.
16//!
17//! # Examples
18//!
19//! Any byte stream can be encoded and decoded that implements the `std::io::Read` trait, and the
20//! output can be anything that implements `std::io::Write` trait. Thus, it is possible to process
21//! files or memory objects as well.
22//!
23//! ```rust
24//! use redux::model::*;
25//!
26//! let data = vec![0x72u8, 0x65u8, 0x64u8, 0x75u8, 0x78u8];
27//!
28//! // Encode
29//! let mut cursor1 = std::io::Cursor::new(&data);
30//! let mut compressed = Vec::<u8>::new();
31//! assert!(redux::compress(&mut cursor1, &mut compressed, AdaptiveTreeModel::new(Parameters::new(8, 14, 16).unwrap())).is_ok());
32//!
33//! // Decode
34//! let mut cursor2 = std::io::Cursor::new(&compressed);
35//! let mut decompressed = Vec::<u8>::new();
36//! assert!(redux::decompress(&mut cursor2, &mut decompressed, AdaptiveTreeModel::new(Parameters::new(8, 14, 16).unwrap())).is_ok());
37//!
38//! assert_eq!(decompressed, data);
39//! ```
40
41#![warn(missing_docs)]
42
43use std::boxed::Box;
44use std::io;
45use std::result;
46use std::fmt;
47use self::codec::Codec;
48use self::bitio::ByteCount;
49use self::bitio::BitReader;
50use self::bitio::BitWriter;
51
52pub mod bitio;
53pub mod codec;
54pub mod model;
55
56/// Possible errors that occur throughout this library
57pub enum Error {
58    /// The input stream has ended (unexpectedly)
59    Eof,
60    /// An invalid combination of data has occured on the input that the library was unable to process.
61    InvalidInput,
62    /// An I/O error occured.
63    IoError(io::Error),
64}
65
66impl fmt::Display for Error {
67    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
68        match *self {
69            Error::Eof => f.write_str("Unexpected end of file"),
70            Error::InvalidInput => f.write_str("Invalid data found while processing input"),
71            Error::IoError(ref e) => f.write_fmt(format_args!("I/O error: {}", e)),
72        }
73    }
74}
75
76impl fmt::Debug for Error {
77    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
78        match *self {
79            Error::Eof => f.write_str("Eof"),
80            Error::InvalidInput => f.write_str("InvalidInput"),
81            Error::IoError(ref e) => f.write_fmt(format_args!("IoError({:?})", e)),
82        }
83    }
84}
85
86#[cfg(test)]
87impl PartialEq<Error> for Error {
88    fn eq(&self, other: &Error) -> bool {
89       match *self {
90           Error::Eof => match *other { Error::Eof => true, _ => false },
91           Error::InvalidInput => match *other { Error::InvalidInput => true, _ => false },
92           Error::IoError(_)  => match *other { Error::IoError(_) => true, _ => false },
93       }
94    }
95}
96
97/// Specialized `Result` type for the `redux` library.
98pub type Result<T> = result::Result<T, Error>;
99
100/// Compresses `istream` into `ostream` using the given `model`.
101/// Returns the number of bytes both in the decompressed and compressed stream.
102pub fn compress(istream: &mut io::Read, ostream: &mut io::Write, model: Box<model::Model>) -> Result<(u64, u64)> {
103    let mut codec = Codec::new(model);
104    let mut input = BitReader::new(istream);
105    let mut output = BitWriter::new(ostream);
106
107    try!(codec.compress_stream(&mut input, &mut output));
108    return Ok((input.get_count(), output.get_count()));
109}
110
111/// Decompresses `istream` into `ostream` using the given `model`.
112/// Returns the number of bytes both in the compressed and decompressed stream.
113pub fn decompress(istream: &mut io::Read, ostream: &mut io::Write, model: Box<model::Model>) -> Result<(u64, u64)> {
114    let mut codec = Codec::new(model);
115    let mut input = BitReader::new(istream);
116    let mut output = BitWriter::new(ostream);
117
118    try!(codec.decompress_stream(&mut input, &mut output));
119    return Ok((input.get_count(), output.get_count()));
120}
121
122#[cfg(test)]
123mod tests {
124    use super::Error::*;
125    use std::io;
126
127    macro_rules! assert_ne {
128        ($a:expr, $b:expr) => ($a != $b)
129    }
130
131    #[test]
132    fn error_eq() {
133        assert_eq!(Eof, Eof);
134        assert_eq!(InvalidInput, InvalidInput);
135        assert_eq!(IoError(io::Error::new(io::ErrorKind::Other, "Other")), IoError(io::Error::new(io::ErrorKind::NotFound, "NotFound")));
136        assert_ne!(Eof, InvalidInput);
137        assert_ne!(InvalidInput, IoError(io::Error::new(io::ErrorKind::Other, "Other")));
138        assert_ne!(IoError(io::Error::new(io::ErrorKind::NotFound, "NotFound")), Eof);
139    }
140}