1mod bitstream;
9mod jb;
10
11use crate::{Encoding, Error};
12use bitstream::BitStream;
13
14const HEADER_LEN: usize = 8;
15const MAGIC_EA04: &[u8; 4] = b"EA04";
16const MAGIC_EA05: &[u8; 4] = b"EA05";
17const MAGIC_EA06: &[u8; 4] = b"EA06";
18const MAGIC_JB00: &[u8; 4] = b"JB00";
19const MAGIC_JB01: &[u8; 4] = b"JB01";
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct Limits {
24 pub max_output_size: usize,
26}
27
28impl Default for Limits {
29 fn default() -> Self {
35 Self {
36 max_output_size: 64 * 1024 * 1024,
37 }
38 }
39}
40
41pub fn decompress(data: &[u8], limits: Limits) -> Result<Vec<u8>, Error> {
64 let header = data.get(0..HEADER_LEN).ok_or_else(Error::truncated)?;
65 let magic = header.get(0..4).ok_or_else(Error::truncated)?;
66 let size_bytes: [u8; 4] = header
67 .get(4..8)
68 .ok_or_else(Error::truncated)?
69 .try_into()
70 .map_err(|_err| Error::truncated())?;
71 let output_size =
72 usize::try_from(u32::from_be_bytes(size_bytes)).map_err(|_err| Error::limit_exceeded())?;
73 if output_size > limits.max_output_size {
74 return Err(Error::limit_exceeded());
75 }
76 let payload = data.get(HEADER_LEN..).ok_or_else(Error::truncated)?;
77
78 if magic == MAGIC_EA04 {
79 decompress_lz(payload, output_size, Encoding::Ea04)
80 } else if magic == MAGIC_EA05 {
81 decompress_lz(payload, output_size, Encoding::Ea05)
82 } else if magic == MAGIC_EA06 {
83 decompress_lz(payload, output_size, Encoding::Ea06)
84 } else if magic == MAGIC_JB00 || magic == MAGIC_JB01 {
85 jb::decompress_jb01(payload, output_size)
86 } else {
87 Err(Error::compression_error())
88 }
89}
90
91fn decompress_lz(data: &[u8], output_size: usize, encoding: Encoding) -> Result<Vec<u8>, Error> {
116 let literal_symbol = match encoding {
117 Encoding::Ea04 | Encoding::Ea05 => 0,
118 Encoding::Ea06 => 1,
119 Encoding::Jb01 => return Err(Error::unsupported_encoding()),
120 };
121 let mut bits = BitStream::new(data);
122 let mut output = Vec::with_capacity(output_size);
123 while output.len() < output_size {
124 let control = bits.read_bits(1)?;
125 if control == literal_symbol {
126 let byte =
127 u8::try_from(bits.read_bits(8)?).map_err(|_err| Error::compression_error())?;
128 output.push(byte);
129 } else {
130 let offset =
131 usize::try_from(bits.read_bits(15)?).map_err(|_err| Error::compression_error())?;
132 let match_len = read_match_len(&mut bits)?;
133 copy_match(&mut output, offset, match_len, output_size)?;
134 }
135 }
136 Ok(output)
137}
138
139fn read_match_len(bits: &mut BitStream<'_>) -> Result<usize, Error> {
161 let stages = [
162 (3usize, 2usize, 0b11u32),
163 (6, 3, 0b111),
164 (13, 5, 0b1_1111),
165 (44, 8, 0xff),
166 (299, 8, 0xff),
167 ];
168 for (base, width, more) in stages {
169 let add = bits.read_bits(width)?;
170 if add != more {
171 return usize::try_from(add)
172 .ok()
173 .and_then(|value| base.checked_add(value))
174 .ok_or_else(Error::compression_error);
175 }
176 if base == 299 {
177 let mut length = base;
178 loop {
179 length = length
180 .checked_add(usize::try_from(more).map_err(|_err| Error::compression_error())?)
181 .ok_or_else(Error::compression_error)?;
182 let extra = bits.read_bits(width)?;
183 if extra != more {
184 return length
185 .checked_add(
186 usize::try_from(extra).map_err(|_err| Error::compression_error())?,
187 )
188 .ok_or_else(Error::compression_error);
189 }
190 }
191 }
192 }
193 Err(Error::compression_error())
194}
195
196fn copy_match(
219 output: &mut Vec<u8>,
220 offset: usize,
221 match_len: usize,
222 output_size: usize,
223) -> Result<(), Error> {
224 if offset == 0 || offset > output.len() {
225 return Err(Error::compression_error());
226 }
227 let end = output
228 .len()
229 .checked_add(match_len)
230 .ok_or_else(Error::compression_error)?;
231 if end > output_size {
232 return Err(Error::compression_error());
233 }
234 for _ in 0..match_len {
235 let source = output
236 .len()
237 .checked_sub(offset)
238 .ok_or_else(Error::compression_error)?;
239 let byte = *output.get(source).ok_or_else(Error::compression_error)?;
240 output.push(byte);
241 }
242 Ok(())
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn decompresses_ea06_literal_only() -> Result<(), String> {
251 let mut blob = Vec::from(*MAGIC_EA06);
252 blob.extend_from_slice(&3u32.to_be_bytes());
253 blob.extend_from_slice(
254 pack_bits(&[
255 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, ])?
259 .as_slice(),
260 );
261
262 let decompressed =
263 decompress(blob.as_slice(), Limits::default()).map_err(|err| err.to_string())?;
264 check_eq(decompressed, b"ABC".to_vec(), "decompressed")
265 }
266
267 #[test]
268 fn decompresses_ea05_literal_only() -> Result<(), String> {
269 let mut blob = Vec::from(*MAGIC_EA05);
270 blob.extend_from_slice(&2u32.to_be_bytes());
271 blob.extend_from_slice(
272 pack_bits(&[
273 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, ])?
276 .as_slice(),
277 );
278
279 let decompressed =
280 decompress(blob.as_slice(), Limits::default()).map_err(|err| err.to_string())?;
281 check_eq(decompressed, b"HI".to_vec(), "decompressed")
282 }
283
284 #[test]
285 fn decompresses_overlapping_match() -> Result<(), String> {
286 let mut blob = Vec::from(*MAGIC_EA06);
287 blob.extend_from_slice(&5u32.to_be_bytes());
288 blob.extend_from_slice(
289 pack_bits(&[
290 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, ])?
296 .as_slice(),
297 );
298
299 let decompressed =
300 decompress(blob.as_slice(), Limits::default()).map_err(|err| err.to_string())?;
301 check_eq(decompressed, b"ABABA".to_vec(), "decompressed")
302 }
303
304 #[test]
305 fn rejects_invalid_match_offset() -> Result<(), String> {
306 let mut blob = Vec::from(*MAGIC_EA06);
307 blob.extend_from_slice(&3u32.to_be_bytes());
308 blob.extend_from_slice(
309 pack_bits(&[
310 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ])?
314 .as_slice(),
315 );
316
317 let Err(err) = decompress(blob.as_slice(), Limits::default()) else {
318 return Err("unexpected decompression success".to_string());
319 };
320 check_eq(
321 err.recognition_failure(),
322 Some(crate::RecognitionFailure::CompressionError),
323 "error",
324 )
325 }
326
327 #[test]
328 fn rejects_truncated_bitstream() -> Result<(), String> {
329 let mut blob = Vec::from(*MAGIC_EA06);
330 blob.extend_from_slice(&1u32.to_be_bytes());
331 blob.push(0b1000_0000);
332
333 let Err(err) = decompress(blob.as_slice(), Limits::default()) else {
334 return Err("unexpected decompression success".to_string());
335 };
336 check_eq(
337 err.recognition_failure(),
338 Some(crate::RecognitionFailure::Truncated),
339 "error",
340 )
341 }
342
343 #[test]
344 fn rejects_advertised_size_above_limit() -> Result<(), String> {
345 let mut blob = Vec::from(*MAGIC_EA06);
346 blob.extend_from_slice(&2u32.to_be_bytes());
347
348 let Err(err) = decompress(blob.as_slice(), Limits { max_output_size: 1 }) else {
349 return Err("unexpected decompression success".to_string());
350 };
351 check_eq(
352 err.recognition_failure(),
353 Some(crate::RecognitionFailure::LimitExceeded),
354 "error",
355 )
356 }
357
358 fn pack_bits(bits: &[u8]) -> Result<Vec<u8>, String> {
359 let mut out = Vec::new();
360 let mut cursor = 0usize;
361 while cursor < bits.len() {
362 let mut byte = 0u8;
363 for bit_index in 0..8usize {
364 let source_index = cursor
365 .checked_add(bit_index)
366 .ok_or_else(|| "bit offset overflow".to_string())?;
367 let bit = bits
368 .get(source_index)
369 .copied()
370 .map_or(0, core::convert::identity);
371 byte = (byte << 1) | bit;
372 }
373 out.push(byte);
374 cursor = cursor
375 .checked_add(8)
376 .ok_or_else(|| "bit offset overflow".to_string())?;
377 }
378 Ok(out)
379 }
380
381 fn check_eq<T>(actual: T, expected: T, context: &str) -> Result<(), String>
382 where
383 T: core::fmt::Debug + PartialEq,
384 {
385 if actual == expected {
386 Ok(())
387 } else {
388 Err(format!("{context}: got {actual:?}, expected {expected:?}"))
389 }
390 }
391}