1const WINDOW: usize = 4096;
19const MIN_MATCH: usize = 3;
20const MAX_MATCH: usize = MIN_MATCH + 15; pub fn compress(src: &[u8], dst: &mut [u8]) -> Option<usize> {
30 let mut out = Emitter { dst, pos: 0 };
31 let mut i = 0;
32 while i < src.len() {
33 let flag_pos = out.reserve()?;
35 let mut flag = 0u8;
36 let mut bit = 0;
37 while bit < 8 && i < src.len() {
38 let (dist, len) = longest_match(src, i);
39 if len >= MIN_MATCH {
40 let token = (((dist - 1) as u16) << 4) | (len - MIN_MATCH) as u16;
41 out.push((token >> 8) as u8)?;
42 out.push(token as u8)?;
43 i += len;
44 } else {
45 flag |= 1 << bit;
46 out.push(src[i])?;
47 i += 1;
48 }
49 bit += 1;
50 }
51 out.set(flag_pos, flag);
52 }
53 Some(out.pos)
54}
55
56pub fn decompress(src: &[u8], dst: &mut [u8]) -> Option<usize> {
60 let mut i = 0;
61 decompress_from(
62 || {
63 let b = src.get(i).copied();
64 if b.is_some() {
65 i += 1;
66 }
67 b
68 },
69 dst,
70 )
71}
72
73pub fn decompress_from<F: FnMut() -> Option<u8>>(mut next: F, dst: &mut [u8]) -> Option<usize> {
80 let mut pos = 0;
81 while pos < dst.len() {
82 let flag = next()?;
83 let mut bit = 0;
84 while bit < 8 && pos < dst.len() {
85 if flag & (1 << bit) != 0 {
86 dst[pos] = next()?;
87 pos += 1;
88 } else {
89 let b0 = next()? as u16;
90 let b1 = next()? as u16;
91 let token = (b0 << 8) | b1;
92 let dist = ((token >> 4) + 1) as usize;
93 let len = (token & 0xF) as usize + MIN_MATCH;
94 if dist > pos {
95 return None; }
97 for _ in 0..len {
98 if pos >= dst.len() {
99 break;
100 }
101 dst[pos] = dst[pos - dist];
102 pos += 1;
103 }
104 }
105 bit += 1;
106 }
107 }
108 Some(pos)
109}
110
111fn longest_match(src: &[u8], at: usize) -> (usize, usize) {
114 let start = at.saturating_sub(WINDOW);
115 let max_len = MAX_MATCH.min(src.len() - at);
116 let mut best_len = 0;
117 let mut best_dist = 0;
118 let mut j = start;
119 while j < at {
120 let mut l = 0;
121 while l < max_len && src[j + l] == src[at + l] {
122 l += 1;
123 }
124 if l > best_len {
125 best_len = l;
126 best_dist = at - j;
127 if l == max_len {
128 break;
129 }
130 }
131 j += 1;
132 }
133 (best_dist, best_len)
134}
135
136struct Emitter<'a> {
138 dst: &'a mut [u8],
139 pos: usize,
140}
141
142impl Emitter<'_> {
143 fn push(&mut self, b: u8) -> Option<()> {
144 *self.dst.get_mut(self.pos)? = b;
145 self.pos += 1;
146 Some(())
147 }
148 fn reserve(&mut self) -> Option<usize> {
150 let p = self.pos;
151 self.push(0)?;
152 Some(p)
153 }
154 fn set(&mut self, at: usize, b: u8) {
155 self.dst[at] = b;
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 fn roundtrip(data: &[u8]) {
164 let mut comp = [0u8; 4096];
165 let clen = compress(data, &mut comp).expect("fits");
166 let mut out = [0u8; 2048];
167 let n = decompress(&comp[..clen], &mut out[..data.len()]).expect("decode");
168 assert_eq!(n, data.len());
169 assert_eq!(&out[..data.len()], data);
170 }
171
172 #[test]
173 fn empty() {
174 roundtrip(b"");
175 }
176
177 #[test]
178 fn incompressible_and_runs() {
179 roundtrip(b"A");
180 roundtrip(b"AAAAAAAAAAAAAAAAAAAAAAAA");
181 roundtrip(b"the quick brown fox the quick brown fox the quick brown fox");
182 let mut seq = [0u8; 500];
183 for (i, b) in seq.iter_mut().enumerate() {
184 *b = (i * 7 + (i / 13)) as u8;
185 }
186 roundtrip(&seq);
187 }
188
189 #[test]
190 fn sparse_like_a_save() {
191 let mut data = [0u8; 1024];
193 data[0] = b'S';
194 data[1] = b'C';
195 for k in 0..8 {
196 data[64 * k + 3] = k as u8;
197 data[64 * k + 4] = 0xFF;
198 }
199 roundtrip(&data);
200 let mut comp = [0u8; 2048];
201 let clen = compress(&data, &mut comp).unwrap();
202 assert!(clen < data.len() / 2, "sparse data should shrink a lot");
203 }
204
205 #[test]
206 fn dst_too_small_is_none() {
207 let mut tiny = [0u8; 2];
208 assert!(compress(b"hello world this will not fit", &mut tiny).is_none());
209 }
210}