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
//! lzf is a very small data compression library.
//!
//! Originally written as [LibLZF](http://software.schmorp.de/pkg/liblzf.html)
//! by Marc Lehmann in portable C.
//!
//! This Rust library is a rewrite of the original C code
//! and fully compatible with compressed data from the C code (and vice versa).
//!
//! # Basic Operation
//!
//! ```rust
//! # use lzf;
//! let data = "aaaaaaaaa";
//!
//! let compressed = lzf::compress(data.as_bytes()).unwrap();
//!
//! let decompressed = lzf::decompress(&compressed, data.len()).unwrap();
//! ```
#![deny(missing_docs)]

use std::fmt;

mod compress;
mod decompress;
pub use compress::compress;
pub use decompress::decompress;

/// Errors that can occur during Compression or Decompression.
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum LzfError {
    /// The provided buffer is too small to handle the uncompressed data
    BufferTooSmall,
    /// The given compressed data is corrupted
    DataCorrupted,
    /// The given data can't be compressed
    NoCompressionPossible,
    /// An unknown error occured
    UnknownError(i32),
}

impl fmt::Display for LzfError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            LzfError::BufferTooSmall => {
                write!(
                    f,
                    "the given buffer is too small to handle the uncompressed data"
                )
            }
            LzfError::DataCorrupted => {
                write!(f, "the given data is corrupted")
            }
            LzfError::NoCompressionPossible => {
                write!(f, "the input data cannot be compressed")
            }
            LzfError::UnknownError(err) => {
                write!(f, "unknown error, code {}", err)
            }
        }
    }
}

/// A Result providing the underlying data or a compression/decompression error
pub type LzfResult<T> = Result<T, LzfError>;

#[test]
fn test_compress_skips_short() {
    match compress("foo".as_bytes()) {
        Ok(_) => panic!("Compression did _something_, which is wrong for 'foo'"),
        Err(err) => assert_eq!(LzfError::NoCompressionPossible, err),
    }
}

#[test]
fn test_compress_lorem() {
    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    match compress(lorem.as_bytes()) {
        Ok(compressed) => {
            assert_eq!(272, compressed.len())
        }
        Err(err) => panic!("Compression failed with error {:?}", err),
    }
}

#[test]
fn test_compress_decompress_lorem_round() {
    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = match compress(lorem.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    match decompress(&compressed, lorem.len()) {
        Ok(decompressed) => {
            assert_eq!(lorem.len(), decompressed.len());
            assert_eq!(lorem.as_bytes(), &decompressed[..]);
        }
        Err(err) => panic!("Decompression failed with error {:?}", err),
    };
}

#[cfg(test)]
mod quickcheck_test {
    use super::*;
    use quickcheck::{quickcheck, TestResult};

    fn compress_decompress_round(data: Vec<u8>) -> TestResult {
        let compr = match compress(&data) {
            Ok(compr) => compr,
            Err(LzfError::NoCompressionPossible) => return TestResult::discard(),
            Err(LzfError::DataCorrupted) => return TestResult::discard(),
            e @ _ => panic!("{:?}", e),
        };
        let decompr = decompress(&compr, data.len()).unwrap();
        TestResult::from_bool(data == decompr)
    }

    #[test]
    fn qc_roundtrip() {
        quickcheck(compress_decompress_round as fn(_) -> _);
    }
}