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
161
extern crate snappy_sys;
extern crate libc;
#[cfg(test)]
extern crate rand;
use snappy_sys as snappy;
use libc::{c_char, size_t};
use std::fmt;
#[derive(Debug)]
pub struct InvalidInput;
impl std::error::Error for InvalidInput {
fn description(&self) -> &str {
"Attempted snappy decompression with invalid input"
}
}
impl fmt::Display for InvalidInput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Attempted snappy decompression with invalid input")
}
}
pub fn max_compressed_len(len: usize) -> usize {
unsafe { snappy::snappy_max_compressed_length(len as size_t) as usize }
}
pub fn decompressed_len(compressed: &[u8]) -> Result<usize, InvalidInput> {
let mut size: size_t = 0;
let len = compressed.len() as size_t;
let status = unsafe { snappy::snappy_uncompressed_length(compressed.as_ptr() as *const c_char, len, &mut size) };
if status == snappy::SNAPPY_INVALID_INPUT {
Err(InvalidInput)
} else {
Ok(size)
}
}
pub fn compress(input: &[u8]) -> Vec<u8> {
let mut buf = Vec::new();
let size = compress_into(input, &mut buf);
buf.truncate(size);
buf
}
pub fn compress_into(input: &[u8], output: &mut Vec<u8>) -> usize {
let mut len = max_compressed_len(input.len());
if output.len() < len {
output.resize(len, 0);
}
let status = unsafe {
snappy::snappy_compress(
input.as_ptr() as *const c_char,
input.len() as size_t,
output.as_mut_ptr() as *mut c_char,
&mut len as &mut size_t,
)
};
match status {
snappy::SNAPPY_OK => len,
snappy::SNAPPY_INVALID_INPUT => panic!("snappy compression has no concept of invalid input"),
snappy::SNAPPY_BUFFER_TOO_SMALL => panic!("buffer cannot be too small, the capacity was just ensured."),
_ => panic!("snappy returned unspecified status"),
}
}
pub fn decompress(input: &[u8]) -> Result<Vec<u8>, InvalidInput> {
let mut v = Vec::new();
decompress_into(input, &mut v).map(|_| v)
}
pub fn decompress_into(input: &[u8], output: &mut Vec<u8>) -> Result<usize, InvalidInput> {
let mut len = decompressed_len(input)?;
if output.len() < len {
output.resize(len, 0);
}
let status = unsafe {
snappy::snappy_uncompress(
input.as_ptr() as *const c_char,
input.len() as size_t,
output.as_mut_ptr() as *mut c_char,
&mut len as &mut size_t,
)
};
match status {
snappy::SNAPPY_OK => Ok(len as usize),
snappy::SNAPPY_INVALID_INPUT => Err(InvalidInput),
snappy::SNAPPY_BUFFER_TOO_SMALL => panic!("buffer cannot be too small, size was just set to large enough."),
_ => panic!("snappy returned unspecified status"),
}
}
pub fn validate_compressed_buffer(input: &[u8]) -> bool {
let status = unsafe { snappy::snappy_validate_compressed_buffer(input.as_ptr() as *const c_char, input.len() as size_t )};
status == snappy::SNAPPY_OK
}
#[cfg(test)]
mod tests {
use super::*;
use rand::prelude::*;
const ITERATIONS: usize = 100;
const INPUT_SIZE: usize = 1 << 18;
#[test]
fn it_works() {
let mut rng = thread_rng();
let mut input = [0u8; INPUT_SIZE];
for _ in 0..ITERATIONS {
rng.fill(&mut input[..]);
let output = decompress(&compress(&input));
match output {
Err(err) => panic!("failed with error: {} for input: {:?}", err, input.to_vec()),
Ok(output) => assert_eq!(input.to_vec(), output),
}
}
}
}