diff --git a/ext/zstdruby/streaming_decompress.c b/ext/zstdruby/streaming_decompress.c index 58d623c..0528282 100644 --- a/ext/zstdruby/streaming_decompress.c +++ b/ext/zstdruby/streaming_decompress.c @@ -119,9 +119,8 @@ rb_streaming_decompress_decompress(VALUE obj, VALUE src) if (output.pos > 0) { rb_str_cat(result, output.dst, output.pos); } - if (ret == 0 && output.pos == 0) { - break; - } + /* Carry on after the end of a frame, even one without output such as an + empty or skippable frame: the rest of the input can hold more frames. */ } return result; } diff --git a/spec/zstd-ruby-streaming-decompress_spec.rb b/spec/zstd-ruby-streaming-decompress_spec.rb index 053c4ed..8a17068 100644 --- a/spec/zstd-ruby-streaming-decompress_spec.rb +++ b/spec/zstd-ruby-streaming-decompress_spec.rb @@ -17,6 +17,31 @@ end end + describe 'streaming decompress of several frames' do + it 'decompresses every frame' do + input = Zstd.compress('Hello, ') + Zstd.compress('World!') + expect(Zstd::StreamingDecompress.new.decompress(input)).to eq('Hello, World!') + end + + it 'decompresses the frames after an empty frame' do + input = Zstd.compress('') + Zstd.compress('Hello, ') + Zstd.compress('') + Zstd.compress('World!') + expect(Zstd::StreamingDecompress.new.decompress(input)).to eq('Hello, World!') + end + + it 'decompresses the frames after a skippable frame' do + # pzstd writes a skippable frame before each frame. + skippable = [0x184D2A50, 4].pack('VV') + 'meta' + input = skippable + Zstd.compress('Hello, ') + skippable + Zstd.compress('World!') + expect(Zstd::StreamingDecompress.new.decompress(input)).to eq('Hello, World!') + + # Split within the frame that follows the first skippable frame + stream = Zstd::StreamingDecompress.new + result = stream.decompress(input.byteslice(0, 15)) + result << stream.decompress(input.byteslice(15..-1)) + expect(result).to eq('Hello, World!') + end + end + describe 'decompress_with_pos' do it 'should return decompressed data and consumed input position' do str = "hello world test data"