lz_fear/raw/
decompress.rs1use byteorder::{ReadBytesExt, LE};
2use std::io::{self, Cursor, Read, ErrorKind};
3use thiserror::Error;
4use culpa::{throws, throw};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Error)]
8pub enum DecodeError {
9 #[error("Block stream ended prematurely. Either your input was truncated or you're trying to decompress garbage.")]
10 UnexpectedEnd,
11 #[error("Refusing to decode a repetition that would exceed the memory limit. If you're using framed mode, this is either garbage input or an OOM attack. If you're using raw mode, good luck figuring out whether this input is valid or not.")]
12 MemoryLimitExceeded,
13 #[error("The offset for a deduplication is zero. This is always invalid. You are probably decoding corrupted input.")]
14 ZeroDeduplicationOffset,
15 #[error("The offset for a deduplication is out of bounds. This may be caused by a missing or incomplete dictionary.")]
16 InvalidDeduplicationOffset,
17}
18type Error = DecodeError; impl From<io::Error> for Error {
21 fn from(e: io::Error) -> Error {
22 assert_eq!(e.kind(), ErrorKind::UnexpectedEof);
24 Error::UnexpectedEnd
25 }
26}
27
28#[throws]
31fn read_lsic(initial: u8, cursor: &mut Cursor<&[u8]>) -> usize {
32 let mut value: usize = initial.into();
33 if value == 0xF {
34 loop {
35 let more = cursor.read_u8()?;
36 value += usize::from(more);
37 if more != 0xff {
38 break;
39 }
40 }
41 }
42 value
43}
44
45#[throws]
59pub fn decompress_raw(input: &[u8], prefix: &[u8], output: &mut Vec<u8>, output_limit: usize) {
60 let mut reader = Cursor::new(input);
61 while let Ok(token) = reader.read_u8() {
62 let literal_length = read_lsic(token >> 4, &mut reader)?;
64
65 let output_pos_pre_literal = output.len();
66 output.resize(output_pos_pre_literal + literal_length, 0);
67 reader.read_exact(&mut output[output_pos_pre_literal..])?;
68
69 if let Ok(offset) = reader.read_u16::<LE>() {
71 let match_len = 4 + read_lsic(token & 0xf, &mut reader)?;
72 if (output.len() + match_len) > output_limit {
73 throw!(Error::MemoryLimitExceeded);
74 }
75 copy_overlapping(offset.into(), match_len, prefix, output)?;
76 }
77 }
78}
79
80fn copy_overlapping(offset: usize, match_len: usize, prefix: &[u8], output: &mut Vec<u8>) -> Result<(), Error> {
81 let old_len = output.len();
82 match offset {
83 0 => return Err(Error::ZeroDeduplicationOffset),
84 i if i > old_len => {
85 let prefix_needed = i - old_len;
87 if prefix_needed > prefix.len() {
88 return Err(Error::InvalidDeduplicationOffset);
89 }
90 let how_many_bytes_from_prefix = std::cmp::min(prefix_needed, match_len);
91 output.extend_from_slice(
92 &prefix[prefix.len() - prefix_needed..][..how_many_bytes_from_prefix],
93 );
94 let remaining_len = match_len - how_many_bytes_from_prefix;
95 if remaining_len != 0 {
96 return copy_overlapping(offset, remaining_len, &[], output);
98 }
99 }
100
101 1 => output.resize(old_len + match_len, output[old_len - 1]),
103
104 o if match_len <= o => {
105 output.resize(old_len + match_len, 0);
109 let (head, tail) = output.split_at_mut(old_len);
110 tail.copy_from_slice(&head[old_len - offset..][..match_len]);
111 }
112 2 | 4 | 8 => {
113 let mut buf = [0u8; 16];
117 for chunk in buf.chunks_mut(offset) {
118 chunk.copy_from_slice(&output[old_len - offset..][..offset]);
120 }
121 output.resize(old_len + match_len, 0);
123 for target in output[old_len..].chunks_mut(buf.len()) {
125 target.copy_from_slice(&buf[..target.len()]);
126 }
127 }
128 _ => {
129 output.reserve(match_len);
131 for i in 0..match_len {
132 let b = output[old_len - offset + i];
133 output.push(b);
134 }
135 }
136 }
137 Ok(())
138}
139
140
141#[cfg(test)]
142pub mod test {
143 use culpa::throws;
144 use super::{decompress_raw, Error};
145
146 #[throws]
147 pub fn decompress(input: &[u8]) -> Vec<u8> {
148 let mut vec = Vec::new();
149 decompress_raw(input, &[], &mut vec, std::usize::MAX)?;
150 vec
151 }
152
153 #[test]
154 fn aaaaaaaaaaa_lots_of_aaaaaaaaa() {
155 assert_eq!(decompress(&[0x11, b'a', 1, 0]).unwrap(), b"aaaaaa");
156 }
157
158 #[test]
159 fn multiple_repeated_blocks() {
160 assert_eq!(
161 decompress(&[0x11, b'a', 1, 0, 0x22, b'b', b'c', 2, 0]).unwrap(),
162 b"aaaaaabcbcbcbc"
163 );
164 }
165
166 #[test]
167 fn all_literal() {
168 assert_eq!(decompress(&[0x30, b'a', b'4', b'9']).unwrap(), b"a49");
169 }
170
171 #[test]
172 fn offset_oob() {
173 decompress(&[0x10, b'a', 2, 0]).unwrap_err();
174 decompress(&[0x40, b'a', 1, 0]).unwrap_err();
175 }
176}