@@ -350,14 +350,17 @@ impl LzmaStream {
350350 }
351351 }
352352
353- fn process ( & mut self , input : & [ u8 ] , output : & mut Vec < u8 > ) -> Result < Status , XzError > {
353+ fn process ( & mut self , input : & [ u8 ] , output : & mut [ u8 ] ) -> Result < Status , XzError > {
354+ let previous_in = self . stream . total_in ( ) ;
355+ let result = self . stream . process ( input, output, Action :: Run ) ;
356+ let consumed = ( self . stream . total_in ( ) - previous_in) as usize ;
354357 if self . track_header && self . header_collected < 8 {
355- let count = ( 8 - usize:: from ( self . header_collected ) ) . min ( input . len ( ) ) ;
358+ let count = ( 8 - usize:: from ( self . header_collected ) ) . min ( consumed ) ;
356359 let start = usize:: from ( self . header_collected ) ;
357360 self . header_buf [ start..start + count] . copy_from_slice ( & input[ ..count] ) ;
358361 self . header_collected += count as u8 ;
359362 }
360- match self . stream . process_vec ( input , output , Action :: Run ) {
363+ match result {
361364 Ok ( Status :: GetCheck ) => {
362365 if self . header_collected >= 8 {
363366 self . check = i32:: from ( self . header_buf [ 7 ] & 0x0f ) ;
@@ -377,35 +380,39 @@ fn decompress_chunks(
377380 chunks : & mut Chunker < ' _ > ,
378381 stream : & mut LzmaStream ,
379382 max_length : Option < usize > ,
380- ) -> Result < ( Vec < u8 > , bool ) , XzError > {
381- // No input to feed the decoder: not a stream end, so `eof` stays false and
382- // the caller can still supply compressed data later.
383- if chunks. is_empty ( ) {
384- return Ok ( ( Vec :: new ( ) , false ) ) ;
385- }
383+ ) -> Result < ( Vec < u8 > , bool , bool ) , Error > {
386384 let max_length = max_length. unwrap_or ( usize:: MAX ) ;
387385 let mut output = Vec :: new ( ) ;
388- ' outer: loop {
386+ let mut block = vec ! [ 0 ; BUFSIZ . min( max_length) ] ;
387+ loop {
389388 let chunk = chunks. chunk ( ) ;
390- loop {
391- let additional = BUFSIZ . min ( max_length - output. capacity ( ) ) ;
392- if additional == 0 {
393- return Ok ( ( output, false ) ) ;
394- }
395- output. reserve_exact ( additional) ;
396- let previous_in = stream. stream . total_in ( ) ;
397- let result = stream. process ( chunk, & mut output) ;
398- let consumed = ( stream. stream . total_in ( ) - previous_in) as usize ;
399- chunks. advance ( consumed) ;
400- let status = result?;
401- if status == Status :: StreamEnd || chunks. is_empty ( ) {
402- output. shrink_to_fit ( ) ;
403- return Ok ( ( output, status == Status :: StreamEnd ) ) ;
404- }
405- if !chunk. is_empty ( ) && consumed == 0 {
406- continue ;
389+ let available = block. len ( ) . min ( max_length - output. len ( ) ) ;
390+ let previous_in = stream. stream . total_in ( ) ;
391+ let previous_out = stream. stream . total_out ( ) ;
392+ let result = stream. process ( chunk, & mut block[ ..available] ) ;
393+ let consumed = ( stream. stream . total_in ( ) - previous_in) as usize ;
394+ let produced = ( stream. stream . total_out ( ) - previous_out) as usize ;
395+ chunks. advance ( consumed) ;
396+ let status = result?;
397+ output. extend_from_slice ( & block[ ..produced] ) ;
398+ let filled = produced == available;
399+
400+ // liblzma reports BUF_ERROR after consecutive calls without progress.
401+ // Exhausted input with output space is just a request for more input.
402+ if status == Status :: MemNeeded && !( consumed == chunk. len ( ) && !filled) {
403+ return Err ( Error :: Lzma ( "Insufficient buffer space" . to_owned ( ) ) ) ;
404+ }
405+ if status == Status :: StreamEnd {
406+ return Ok ( ( output, true , false ) ) ;
407+ }
408+ // Check output before input: liblzma may have buffered output even
409+ // after consuming the last supplied input byte.
410+ if filled {
411+ if output. len ( ) == max_length {
412+ return Ok ( ( output, false , true ) ) ;
407413 }
408- continue ' outer;
414+ } else if chunks. is_empty ( ) {
415+ return Ok ( ( output, false , false ) ) ;
409416 }
410417 }
411418}
@@ -477,9 +484,9 @@ impl Decompressor {
477484 let mut chunks = Chunker :: chain ( input_buffer, data) ;
478485 let previous_len = chunks. len ( ) ;
479486 let result = decompress_chunks ( & mut chunks, stream, max_length) ;
480- let stream_end = match & result {
481- Ok ( ( _, stream_end) ) => * stream_end,
482- Err ( _) => false ,
487+ let ( stream_end, capped ) = match & result {
488+ Ok ( ( _, stream_end, capped ) ) => ( * stream_end, * capped ) ,
489+ Err ( _) => ( false , false ) ,
483490 } ;
484491 let consumed = previous_len - chunks. len ( ) ;
485492 self . eof |= stream_end;
@@ -490,7 +497,7 @@ impl Decompressor {
490497 }
491498 } else if chunks. is_empty ( ) {
492499 input_buffer. clear ( ) ;
493- self . needs_input = true ;
500+ self . needs_input = !capped ;
494501 } else {
495502 self . needs_input = false ;
496503 if let Some ( consumed_from_data) = consumed. checked_sub ( input_buffer. len ( ) ) {
@@ -501,7 +508,7 @@ impl Decompressor {
501508 input_buffer. extend_from_slice ( data) ;
502509 }
503510 }
504- result. map ( |( output, _) | output) . map_err ( Error :: from )
511+ result. map ( |( output, _, _ ) | output)
505512 }
506513
507514 #[ must_use]
0 commit comments