1use alloc::vec::Vec;
2
3use super::{
4 Read,
5 decoder::LzmaDecoder,
6 error_invalid_data, error_invalid_input,
7 lz::LzDecoder,
8 range_dec::{RangeDecoder, RangeDecoderBuffer},
9};
10use crate::{ByteReader, DICT_SIZE_MIN};
11
12pub const COMPRESSED_SIZE_MAX: u32 = 1 << 16;
13
14pub struct Lzma2Reader<R> {
31 inner: R,
32 lz: LzDecoder,
33 rc: RangeDecoder<RangeDecoderBuffer>,
34 lzma: Option<LzmaDecoder>,
35 uncompressed_size: usize,
36 is_lzma_chunk: bool,
37 need_dict_reset: bool,
38 need_props: bool,
39 end_reached: bool,
40}
41
42#[inline]
44pub fn get_memory_usage(dict_size: u32) -> u32 {
45 40 + COMPRESSED_SIZE_MAX / 1024 + get_dict_size(dict_size) / 1024
46}
47
48#[inline]
49fn get_dict_size(dict_size: u32) -> u32 {
50 if dict_size >= (u32::MAX - 15) {
51 return u32::MAX;
52 }
53
54 (dict_size + 15) & !15
55}
56
57fn decode_lzma2_props(props: u8) -> crate::Result<LzmaDecoder> {
58 if props > (4 * 5 + 4) * 9 + 8 {
59 return Err(error_invalid_input("corrupted input data (LZMA2:3)"));
60 }
61 let pb = props / (9 * 5);
62 let remainder = props - pb * 9 * 5;
63 let lp = remainder / 9;
64 let lc = remainder - lp * 9;
65 if lc + lp > 4 {
66 return Err(error_invalid_input("corrupted input data (LZMA2:4)"));
67 }
68 Ok(LzmaDecoder::new(lc as _, lp as _, pb as _))
69}
70
71impl<R> Lzma2Reader<R> {
72 pub fn into_inner(self) -> R {
74 self.inner
75 }
76
77 pub fn inner(&self) -> &R {
79 &self.inner
80 }
81
82 pub fn inner_mut(&mut self) -> &mut R {
84 &mut self.inner
85 }
86}
87
88impl<R: Read> Lzma2Reader<R> {
89 pub fn new(inner: R, dict_size: u32, preset_dict: Option<&[u8]>) -> Self {
93 let has_preset = preset_dict.as_ref().map(|a| !a.is_empty()).unwrap_or(false);
94 let lz = LzDecoder::new(get_dict_size(dict_size) as _, preset_dict);
95 let rc = RangeDecoder::new_buffer(COMPRESSED_SIZE_MAX as _);
96 Self {
97 inner,
98 lz,
99 rc,
100 lzma: None,
101 uncompressed_size: 0,
102 is_lzma_chunk: false,
103 need_dict_reset: !has_preset,
104 need_props: true,
105 end_reached: false,
106 }
107 }
108
109 fn decode_chunk_header(&mut self) -> crate::Result<()> {
128 let control = self.inner.read_u8()?;
129
130 if control == 0x00 {
131 self.end_reached = true;
132 return Ok(());
133 }
134
135 if control >= 0xE0 || control == 0x01 {
136 self.need_props = true;
137 self.need_dict_reset = false;
138 self.lz.reset();
140 } else if self.need_dict_reset {
141 return Err(error_invalid_input("corrupted input data (LZMA2:0)"));
142 }
143 if control >= 0x80 {
144 self.is_lzma_chunk = true;
145 self.uncompressed_size = ((control & 0x1F) as usize) << 16;
146 self.uncompressed_size += self.inner.read_u16_be()? as usize + 1;
147 let compressed_size = self.inner.read_u16_be()? as usize + 1;
148
149 if control >= 0xC0 {
150 self.need_props = false;
152 self.decode_props()?;
153 } else if self.need_props {
154 return Err(error_invalid_input("corrupted input data (LZMA2:1)"));
155 } else if control >= 0xA0 {
156 if let Some(l) = self.lzma.as_mut() {
158 l.reset()
159 }
160 }
161
162 self.rc.prepare(&mut self.inner, compressed_size)?;
163 } else if control > 0x02 {
164 return Err(error_invalid_input("corrupted input data (LZMA2:2)"));
165 } else {
166 self.is_lzma_chunk = false;
167 self.uncompressed_size = (self.inner.read_u16_be()? as usize) + 1;
168 }
169 Ok(())
170 }
171
172 fn decode_props(&mut self) -> crate::Result<()> {
173 let props = self.inner.read_u8()?;
174 self.lzma = Some(decode_lzma2_props(props)?);
175 Ok(())
176 }
177}
178
179impl<R: Read> Read for Lzma2Reader<R> {
180 fn read(&mut self, buf: &mut [u8]) -> crate::Result<usize> {
181 if buf.is_empty() {
182 return Ok(0);
183 }
184
185 if self.end_reached {
186 return Ok(0);
187 }
188
189 self.lz.ensure_capacity()?;
190
191 let mut size = 0;
192 let mut len = buf.len();
193 let mut off = 0;
194 while len > 0 {
195 if self.uncompressed_size == 0 {
196 self.decode_chunk_header()?;
197 if self.end_reached {
198 return Ok(size);
199 }
200 }
201
202 let copy_size_max = self.uncompressed_size.min(len);
203 if !self.is_lzma_chunk {
204 self.lz.copy_uncompressed(&mut self.inner, copy_size_max)?;
205 } else {
206 self.lz.set_limit(copy_size_max);
207 if let Some(lzma) = self.lzma.as_mut() {
208 lzma.decode(&mut self.lz, &mut self.rc)?;
209 }
210 }
211
212 {
213 let copied_size = self.lz.flush(buf, off)?;
214 off = off.saturating_add(copied_size);
215 len = len.saturating_sub(copied_size);
216 size = size.saturating_add(copied_size);
217 self.uncompressed_size = self.uncompressed_size.saturating_sub(copied_size);
218 if self.uncompressed_size == 0 && (!self.rc.is_finished() || self.lz.has_pending())
219 {
220 return Err(error_invalid_input("rc not finished or lz has pending"));
221 }
222 }
223 }
224
225 Ok(size)
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub enum Action {
234 Run,
236 Finish,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum Status {
243 Ok,
245 StreamEnd,
247}
248
249#[derive(Debug, Clone, Copy)]
251pub struct StreamResult {
252 pub bytes_consumed: usize,
254 pub bytes_produced: usize,
256 pub status: Status,
258}
259
260#[derive(Clone, Copy)]
261enum Lzma2State {
262 ChunkHeader,
263 CompressedData { remaining: usize },
264 UncompressedData { remaining: usize },
265 DrainUncompressed { remaining: usize },
266 Decode,
267 DrainOutput,
268 Finished,
269}
270
271pub struct Lzma2Stream {
276 state: Lzma2State,
277 accum: Vec<u8>,
278 accum_needed: usize,
279 lz: LzDecoder,
280 rc: RangeDecoder<RangeDecoderBuffer>,
281 lzma: Option<LzmaDecoder>,
282 compressed_buf: Vec<u8>,
283 uncompressed_size: usize,
284 need_dict_reset: bool,
285 need_props: bool,
286 total_in: u64,
287 total_out: u64,
288}
289
290impl Lzma2Stream {
291 pub fn new(dict_size: u32) -> Self {
293 let dict_size = get_dict_size(dict_size.max(DICT_SIZE_MIN)) as usize;
294 Self {
295 state: Lzma2State::ChunkHeader,
296 accum: Vec::with_capacity(8),
297 accum_needed: 1,
298 lz: LzDecoder::new(dict_size, None),
299 rc: RangeDecoder::new_buffer(65536),
300 lzma: None,
301 compressed_buf: Vec::new(),
302 uncompressed_size: 0,
303 need_dict_reset: true,
304 need_props: true,
305 total_in: 0,
306 total_out: 0,
307 }
308 }
309
310 pub fn total_in(&self) -> u64 {
312 self.total_in
313 }
314
315 pub fn total_out(&self) -> u64 {
317 self.total_out
318 }
319
320 pub fn is_finished(&self) -> bool {
322 matches!(self.state, Lzma2State::Finished)
323 }
324
325 pub fn has_output(&self) -> bool {
327 self.lz.has_output()
328 }
329
330 pub fn process(
332 &mut self,
333 input: &[u8],
334 output: &mut [u8],
335 action: Action,
336 ) -> crate::Result<StreamResult> {
337 self.lz.ensure_capacity()?;
338
339 let mut in_pos = 0;
340 let mut out_pos = 0;
341
342 loop {
343 match self.state {
344 Lzma2State::Finished => {
345 return Ok(StreamResult {
346 bytes_consumed: in_pos,
347 bytes_produced: out_pos,
348 status: Status::StreamEnd,
349 });
350 }
351
352 Lzma2State::DrainOutput | Lzma2State::DrainUncompressed { .. } => {
353 if out_pos >= output.len() {
354 return Ok(StreamResult {
355 bytes_consumed: in_pos,
356 bytes_produced: out_pos,
357 status: Status::Ok,
358 });
359 }
360 if !self.flush_output(output, &mut out_pos) {
361 return Ok(StreamResult {
362 bytes_consumed: in_pos,
363 bytes_produced: out_pos,
364 status: Status::Ok,
365 });
366 }
367 }
368
369 Lzma2State::Decode => {
370 self.decode_lzma()?;
371 }
372
373 Lzma2State::CompressedData { remaining } => {
374 if let Some(result) = self.process_compressed_data(
375 input,
376 action,
377 &mut in_pos,
378 out_pos,
379 remaining,
380 )? {
381 return Ok(result);
382 }
383 }
384
385 Lzma2State::UncompressedData { remaining } => {
386 if let Some(result) = self.process_uncompressed_data(
387 input,
388 action,
389 &mut in_pos,
390 out_pos,
391 remaining,
392 )? {
393 return Ok(result);
394 }
395 }
396
397 Lzma2State::ChunkHeader => {
398 if let Some(result) =
399 self.accumulate_chunk_header(input, action, &mut in_pos, out_pos)?
400 {
401 return Ok(result);
402 }
403 }
404 }
405 }
406 }
407
408 fn flush_output(&mut self, output: &mut [u8], out_pos: &mut usize) -> bool {
409 let n = self.lz.flush_partial(&mut output[*out_pos..]);
410 if n > 0 {
411 *out_pos += n;
412 self.total_out += n as u64;
413 }
414 if self.lz.has_output() {
415 return false;
416 }
417 self.finish_drain();
418 true
419 }
420
421 fn decode_lzma(&mut self) -> crate::Result<()> {
422 let pos_before = self.lz.get_pos();
423 self.lz.set_limit(self.uncompressed_size);
424 self.lzma
425 .as_mut()
426 .ok_or_else(|| error_invalid_input("corrupted input data (LZMA2:1)"))?
427 .decode(&mut self.lz, &mut self.rc)?;
428 let decoded = self.lz.get_pos() - pos_before;
429 self.uncompressed_size -= decoded;
430
431 if self.uncompressed_size == 0 && (!self.rc.is_finished() || self.lz.has_pending()) {
432 return Err(error_invalid_input("rc not finished or lz has pending"));
433 }
434
435 self.state = Lzma2State::DrainOutput;
436 Ok(())
437 }
438
439 fn process_compressed_data(
440 &mut self,
441 input: &[u8],
442 action: Action,
443 in_pos: &mut usize,
444 out_pos: usize,
445 remaining: usize,
446 ) -> crate::Result<Option<StreamResult>> {
447 if *in_pos >= input.len() {
448 if action == Action::Finish {
449 return Err(error_invalid_data("unexpected end of LZMA2 stream"));
450 }
451 return Ok(Some(StreamResult {
452 bytes_consumed: *in_pos,
453 bytes_produced: out_pos,
454 status: Status::Ok,
455 }));
456 }
457 let available = &input[*in_pos..];
458 let to_copy = remaining.min(available.len());
459 self.compressed_buf.extend_from_slice(&available[..to_copy]);
460 *in_pos += to_copy;
461 self.total_in += to_copy as u64;
462 let new_remaining = remaining - to_copy;
463 if new_remaining == 0 {
464 self.rc.prepare_from_slice(&self.compressed_buf)?;
465 self.compressed_buf.clear();
466 self.state = Lzma2State::Decode;
467 } else {
468 self.state = Lzma2State::CompressedData {
469 remaining: new_remaining,
470 };
471 }
472 Ok(None)
473 }
474
475 fn process_uncompressed_data(
476 &mut self,
477 input: &[u8],
478 action: Action,
479 in_pos: &mut usize,
480 out_pos: usize,
481 remaining: usize,
482 ) -> crate::Result<Option<StreamResult>> {
483 let lz_space = self.lz.available_space();
484 if lz_space == 0 {
485 self.state = Lzma2State::DrainUncompressed { remaining };
486 return Ok(None);
487 }
488 if *in_pos >= input.len() {
489 if action == Action::Finish {
490 return Err(error_invalid_data("unexpected end of LZMA2 stream"));
491 }
492 return Ok(Some(StreamResult {
493 bytes_consumed: *in_pos,
494 bytes_produced: out_pos,
495 status: Status::Ok,
496 }));
497 }
498 let available = &input[*in_pos..];
499 let to_copy = remaining.min(available.len()).min(lz_space);
500 self.lz
501 .copy_uncompressed_from_slice(&available[..to_copy])?;
502 *in_pos += to_copy;
503 self.total_in += to_copy as u64;
504 self.uncompressed_size -= to_copy;
505 let new_remaining = remaining - to_copy;
506 if new_remaining == 0 {
507 self.state = Lzma2State::DrainOutput;
508 } else if self.lz.available_space() == 0 {
509 self.state = Lzma2State::DrainUncompressed {
510 remaining: new_remaining,
511 };
512 } else {
513 self.state = Lzma2State::UncompressedData {
514 remaining: new_remaining,
515 };
516 }
517 Ok(None)
518 }
519
520 fn accumulate_chunk_header(
521 &mut self,
522 input: &[u8],
523 action: Action,
524 in_pos: &mut usize,
525 out_pos: usize,
526 ) -> crate::Result<Option<StreamResult>> {
527 if self.accum.len() < self.accum_needed {
528 if *in_pos >= input.len() {
529 if action == Action::Finish {
530 return Err(error_invalid_data("unexpected end of LZMA2 stream"));
531 }
532 return Ok(Some(StreamResult {
533 bytes_consumed: *in_pos,
534 bytes_produced: out_pos,
535 status: Status::Ok,
536 }));
537 }
538 let available = &input[*in_pos..];
539 let need = self.accum_needed - self.accum.len();
540 let to_copy = need.min(available.len());
541 self.accum.extend_from_slice(&available[..to_copy]);
542 *in_pos += to_copy;
543 self.total_in += to_copy as u64;
544 if self.accum.len() < self.accum_needed {
545 return Ok(Some(StreamResult {
546 bytes_consumed: *in_pos,
547 bytes_produced: out_pos,
548 status: Status::Ok,
549 }));
550 }
551 }
552 self.process_chunk_header()?;
553 Ok(None)
554 }
555
556 pub(crate) fn is_draining(&self) -> bool {
557 matches!(
558 self.state,
559 Lzma2State::DrainOutput | Lzma2State::DrainUncompressed { .. }
560 )
561 }
562
563 pub(crate) fn drain_with_filter(&mut self, output: &mut [u8], out_pos: &mut usize) -> usize {
564 if *out_pos >= output.len() {
565 return 0;
566 }
567 let n = self.lz.flush_partial(&mut output[*out_pos..]);
568 if n > 0 {
569 *out_pos += n;
570 self.total_out += n as u64;
571 }
572 if !self.lz.has_output() {
573 self.finish_drain();
574 }
575 n
576 }
577
578 pub(crate) fn drain_to_buf(&mut self, buf: &mut Vec<u8>, limit: usize) -> usize {
579 let mut tmp = [0u8; 4096];
580 let cap = limit.min(tmp.len());
581 let n = self.lz.flush_partial(&mut tmp[..cap]);
582 if n > 0 {
583 buf.extend_from_slice(&tmp[..n]);
584 self.total_out += n as u64;
585 }
586 if !self.lz.has_output() {
587 self.finish_drain();
588 }
589 n
590 }
591
592 fn finish_drain(&mut self) {
593 match self.state {
594 Lzma2State::DrainUncompressed { remaining } => {
595 self.state = Lzma2State::UncompressedData { remaining };
596 }
597 _ if self.uncompressed_size > 0 => {
598 self.state = Lzma2State::Decode;
599 }
600 _ => {
601 self.state = Lzma2State::ChunkHeader;
602 self.accum.clear();
603 self.accum_needed = 1;
604 }
605 }
606 }
607
608 fn process_chunk_header(&mut self) -> crate::Result<()> {
609 let control = self.accum[0];
610 if control == 0x00 {
611 self.state = Lzma2State::Finished;
612 Ok(())
613 } else if control >= 0x80 {
614 self.process_compressed_chunk_header(control)
615 } else if control <= 0x02 {
616 self.process_uncompressed_chunk_header(control)
617 } else {
618 Err(error_invalid_input("corrupted input data (LZMA2:2)"))
619 }
620 }
621
622 fn process_compressed_chunk_header(&mut self, control: u8) -> crate::Result<()> {
623 let needed = if control >= 0xC0 { 6 } else { 5 };
624 if self.accum.len() < needed {
625 self.accum_needed = needed;
626 return Ok(());
627 }
628
629 if control >= 0xE0 {
630 self.need_props = true;
631 self.need_dict_reset = false;
632 self.lz.reset();
633 } else if self.need_dict_reset {
634 return Err(error_invalid_input("corrupted input data (LZMA2:0)"));
635 }
636
637 self.uncompressed_size = ((control & 0x1F) as usize) << 16;
638 let uncompressed_hi = u16::from_be_bytes([self.accum[1], self.accum[2]]);
639 self.uncompressed_size += uncompressed_hi as usize + 1;
640 let compressed_size = u16::from_be_bytes([self.accum[3], self.accum[4]]) as usize + 1;
641
642 if control >= 0xC0 {
643 self.need_props = false;
644 self.lzma = Some(decode_lzma2_props(self.accum[5])?);
645 } else if self.need_props {
646 return Err(error_invalid_input("corrupted input data (LZMA2:1)"));
647 } else if control >= 0xA0 {
648 if let Some(l) = self.lzma.as_mut() {
649 l.reset();
650 }
651 }
652
653 self.compressed_buf.clear();
654 self.compressed_buf.reserve(compressed_size);
655 self.state = Lzma2State::CompressedData {
656 remaining: compressed_size,
657 };
658 self.accum.clear();
659 Ok(())
660 }
661
662 fn process_uncompressed_chunk_header(&mut self, control: u8) -> crate::Result<()> {
663 if self.accum.len() < 3 {
664 self.accum_needed = 3;
665 return Ok(());
666 }
667
668 if control == 0x01 {
669 self.need_props = true;
670 self.need_dict_reset = false;
671 self.lz.reset();
672 } else if self.need_dict_reset {
673 return Err(error_invalid_input("corrupted input data (LZMA2:0)"));
674 }
675
676 self.uncompressed_size = u16::from_be_bytes([self.accum[1], self.accum[2]]) as usize + 1;
677
678 self.state = Lzma2State::UncompressedData {
679 remaining: self.uncompressed_size,
680 };
681 self.accum.clear();
682 Ok(())
683 }
684}