lz4_compression/decompress.rs
1//! The decompression algorithm.
2
3use std::convert::TryInto;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
6pub enum Error {
7 /// Expected more bytes, but found none
8 UnexpectedEnd,
9
10 /// The offset for a deduplication is out of bounds
11 InvalidDeduplicationOffset,
12}
13
14/// An LZ4 decoder.
15///
16/// This will decode in accordance to the LZ4 format. It represents a particular state of the
17/// decompressor.
18struct Decoder<'a> {
19
20 /// The compressed input.
21 input: &'a [u8],
22
23 /// The decompressed output.
24 output: &'a mut Vec<u8>,
25
26 /// The current block's "token".
27 ///
28 /// This token contains to 4-bit "fields", a higher and a lower, representing the literals'
29 /// length and the back reference's length, respectively. LSIC is used if either are their
30 /// maximal values.
31 token: u8,
32}
33
34impl<'a> Decoder<'a> {
35 /// Internal (partial) function for `take`.
36 #[inline]
37 fn take_imp(input: &mut &'a [u8], n: usize) -> Result<&'a [u8], Error> {
38 // Check if we have enough bytes left.
39 if input.len() < n {
40 // No extra bytes. This is clearly not expected, so we return an error.
41 Err(Error::UnexpectedEnd)
42 }
43 else {
44 // Take the first n bytes.
45 let res = Ok(&input[..n]);
46
47 // Shift the stream to left, so that it is no longer the first byte.
48 *input = &input[n..];
49
50 // Return the former first byte.
51 res
52 }
53 }
54
55 /// Pop n bytes from the start of the input stream.
56 fn take(&mut self, n: usize) -> Result<&[u8], Error> {
57 Self::take_imp(&mut self.input, n)
58 }
59
60 /// Write a buffer to the output stream.
61 ///
62 /// The reason this doesn't take `&mut self` is that we need partial borrowing due to the rules
63 /// of the borrow checker. For this reason, we instead take some number of segregated
64 /// references so we can read and write them independently.
65 fn output(output: &mut Vec<u8>, buf: &[u8]) {
66 // We use simple memcpy to extend the vector.
67 output.extend_from_slice(&buf[..buf.len()]);
68 }
69
70 /// Write an already decompressed match to the output stream.
71 ///
72 /// This is used for the essential part of the algorithm: deduplication. We start at some
73 /// position `start` and then keep pushing the following element until we've added
74 /// `match_length` elements.
75 fn duplicate(&mut self, start: usize, match_length: usize) {
76 // We cannot simply use memcpy or `extend_from_slice`, because these do not allow
77 // self-referential copies: http://ticki.github.io/img/lz4_runs_encoding_diagram.svg
78 for i in start..start + match_length {
79 let b = self.output[i];
80 self.output.push(b);
81 }
82 }
83
84 /// Read an integer LSIC (linear small integer code) encoded.
85 ///
86 /// In LZ4, we encode small integers in a way that we can have an arbitrary number of bytes. In
87 /// particular, we add the bytes repeatedly until we hit a non-0xFF byte. When we do, we add
88 /// this byte to our sum and terminate the loop.
89 ///
90 /// # Example
91 ///
92 /// ```notest
93 /// 255, 255, 255, 4, 2, 3, 4, 6, 7
94 /// ```
95 ///
96 /// is encoded to _255 + 255 + 255 + 4 = 769_. The bytes after the first 4 is ignored, because
97 /// 4 is the first non-0xFF byte.
98 #[inline]
99 fn read_integer(&mut self) -> Result<usize, Error> {
100 // We start at zero and count upwards.
101 let mut n = 0;
102
103 // If this byte takes value 255 (the maximum value it can take), another byte is read
104 // and added to the sum. This repeats until a byte lower than 255 is read.
105 while {
106 // We add the next byte until we get a byte which we add to the counting variable.
107 let extra = self.take(1)?[0];
108 n += extra as usize;
109
110 // We continue if we got 255.
111 extra == 0xFF
112 } {}
113
114 Ok(n)
115 }
116
117 /// Read a little-endian 16-bit integer from the input stream.
118 #[inline]
119 fn read_u16(&mut self) -> Result<u16, Error> {
120 // Read a u16 in little endian.
121 let bytes = self.take(2)?;
122 Ok(u16::from_le_bytes(bytes.try_into().unwrap()))
123 }
124
125 /// Read the literals section of a block.
126 ///
127 /// The literals section encodes some bytes which are to be copied to the output without any
128 /// modification.
129 ///
130 /// It consists of two parts:
131 ///
132 /// 1. An LSIC integer extension to the literals length as defined by the first part of the
133 /// token, if it takes the highest value (15).
134 /// 2. The literals themself.
135 fn read_literal_section(&mut self) -> Result<(), Error> {
136 // The higher token is the literals part of the token. It takes a value from 0 to 15.
137 let mut literal = (self.token >> 4) as usize;
138
139 // If the initial value is 15, it is indicated that another byte will be read and added to
140 // it.
141 if literal == 15 {
142 // The literal length took the maximal value, indicating that there is more than 15
143 // literal bytes. We read the extra integer.
144 literal += self.read_integer()?;
145 }
146
147 // Now we know the literal length. The number will be used to indicate how long the
148 // following literal copied to the output buffer is.
149
150 // Read the literals segment and output them without processing.
151 Self::output(&mut self.output, Self::take_imp(&mut self.input, literal)?);
152
153 Ok(())
154 }
155
156 /// Read the duplicates section of the block.
157 ///
158 /// The duplicates section serves to reference an already decoded segment. This consists of two
159 /// parts:
160 ///
161 /// 1. A 16-bit little-endian integer defining the "offset", i.e. how long back we need to go
162 /// in the decoded buffer and copy.
163 /// 2. An LSIC integer extension to the duplicate length as defined by the first part of the
164 /// token, if it takes the highest value (15).
165 fn read_duplicate_section(&mut self) -> Result<(), Error> {
166 // Now, we will obtain the offset which we will use to copy from the output. It is an
167 // 16-bit integer.
168 let offset = self.read_u16()?;
169
170 // Obtain the initial match length. The match length is the length of the duplicate segment
171 // which will later be copied from data previously decompressed into the output buffer. The
172 // initial length is derived from the second part of the token (the lower nibble), we read
173 // earlier. Since having a match length of less than 4 would mean negative compression
174 // ratio, we start at 4.
175 let mut match_length = (4 + (self.token & 0xF)) as usize;
176
177 // The intial match length can maximally be 19. As with the literal length, this indicates
178 // that there are more bytes to read.
179 if match_length == 4 + 15 {
180 // The match length took the maximal value, indicating that there is more bytes. We
181 // read the extra integer.
182 match_length += self.read_integer()?;
183 }
184
185 // We now copy from the already decompressed buffer. This allows us for storing duplicates
186 // by simply referencing the other location.
187
188 // Calculate the start of this duplicate segment. We use wrapping subtraction to avoid
189 // overflow checks, which we will catch later.
190 let start = self.output.len().wrapping_sub(offset as usize);
191
192 // We'll do a bound check to avoid panicking.
193 if start < self.output.len() {
194 // Write the duplicate segment to the output buffer.
195 self.duplicate(start, match_length);
196
197 Ok(())
198 }
199 else {
200 Err(Error::InvalidDeduplicationOffset)
201 }
202 }
203
204 /// Complete the decompression by reading all the blocks.
205 ///
206 /// # Decompressing a block
207 ///
208 /// Blocks consists of:
209 /// - A 1 byte token
210 /// * A 4 bit integer $t_1$.
211 /// * A 4 bit integer $t_2$.
212 /// - A $n$ byte sequence of 0xFF bytes (if $t_1 \neq 15$, then $n = 0$).
213 /// - $x$ non-0xFF 8-bit integers, L (if $t_1 = 15$, $x = 1$, else $x = 0$).
214 /// - $t_1 + 15n + L$ bytes of uncompressed data (literals).
215 /// - 16-bits offset (little endian), $a$.
216 /// - A $m$ byte sequence of 0xFF bytes (if $t_2 \neq 15$, then $m = 0$).
217 /// - $y$ non-0xFF 8-bit integers, $c$ (if $t_2 = 15$, $y = 1$, else $y = 0$).
218 ///
219 /// First, the literals are copied directly and unprocessed to the output buffer, then (after
220 /// the involved parameters are read) $t_2 + 15m + c$ bytes are copied from the output buffer
221 /// at position $a + 4$ and appended to the output buffer. Note that this copy can be
222 /// overlapping.
223 #[inline]
224 fn complete(&mut self) -> Result<(), Error> {
225 // Exhaust the decoder by reading and decompressing all blocks until the remaining buffer
226 // is empty.
227 while !self.input.is_empty() {
228 // Read the token. The token is the first byte in a block. It is divided into two 4-bit
229 // subtokens, the higher and the lower.
230 self.token = self.take(1)?[0];
231
232 // Now, we read the literals section.
233 self.read_literal_section()?;
234
235 // If the input stream is emptied, we break out of the loop. This is only the case
236 // in the end of the stream, since the block is intact otherwise.
237 if self.input.is_empty() { break; }
238
239 // Now, we read the duplicates section.
240 self.read_duplicate_section()?;
241 }
242
243 Ok(())
244 }
245}
246
247/// Decompress all bytes of `input` into `output`.
248pub fn decompress_into(input: &[u8], output: &mut Vec<u8>) -> Result<(), Error> {
249 // Decode into our vector.
250 Decoder {
251 input,
252 output,
253 token: 0,
254 }.complete()?;
255
256 Ok(())
257}
258
259/// Decompress all bytes of `input`.
260pub fn decompress(input: &[u8]) -> Result<Vec<u8>, Error> {
261 // Allocate a vector to contain the decompressed stream.
262 let mut vec = Vec::with_capacity(4096);
263
264 decompress_into(input, &mut vec)?;
265
266 Ok(vec)
267}
268
269#[cfg(test)]
270mod test {
271 use super::*;
272
273 #[test]
274 fn aaaaaaaaaaa_lots_of_aaaaaaaaa() {
275 assert_eq!(decompress(&[0x11, b'a', 1, 0]).unwrap(), b"aaaaaa");
276 }
277
278 #[test]
279 fn multiple_repeated_blocks() {
280 assert_eq!(decompress(&[0x11, b'a', 1, 0, 0x22, b'b', b'c', 2, 0]).unwrap(), b"aaaaaabcbcbcbc");
281 }
282
283 #[test]
284 fn all_literal() {
285 assert_eq!(decompress(&[0x30, b'a', b'4', b'9']).unwrap(), b"a49");
286 }
287
288 #[test]
289 fn offset_oob() {
290 decompress(&[0x10, b'a', 2, 0]).unwrap_err();
291 decompress(&[0x40, b'a', 1, 0]).unwrap_err();
292 }
293}