instantiations / es_compression

Compression framework for Dart providing FFI implementations for Brotli, Lz4, Zstd (Zstandard) with ready-to-use prebuilt binaries for Win/Linux/Mac.
https://www.instantiations.com
BSD 3-Clause "New" or "Revised" License
41 stars 8 forks source link

Closing a chunked conversion causes an extra empty chunk to be emitted #52

Closed abitofevrything closed 2 months ago

abitofevrything commented 2 months ago

When the input stream to a chunked stream decompression is closed, an extra chunk with no data is emitted on the output stream.

As a simple example, an empty input stream should produce no output chunks (note that this is different from decompressing an empty list, which should yield an empty list):

await for (final chunk in Stream<List<int>>.empty().transform(zstd.decoder)) {
  print('Got chunk: $chunk'); // Prints "Got chunk: []" once
}

The root of the issue seems to be CodecFilter.processed, which always returns a non-null list if end is true, even if no input data is left to process:

// ...

final builder = BytesBuilder(copy: false);
if (_outputBuffer.unreadCount > 0) {
  final bufferedBytes = _outputBuffer.writtenBytes(reset: true);
  builder.add(bufferedBytes);
} else {
  if (_toProcess.isNotEmpty) _toProcess = _toProcess.drainTo(_inputBuffer);
  _codeOrDecode();
  final bufferedBytes = _outputBuffer.writtenBytes(reset: true);
  builder.add(bufferedBytes);
  if (flush == true) _flush(builder);
  if (end == true) _finalize(builder);
}
return builder.takeBytes();

This then causes CodecSink to emit the extra empty chunk in CodecSink.close.

This issue could be resolved by simply returning null if builder.isEmpty at the end of CodecFilter.processed. I'm not sure how correct this fix would be; I'll leave that to the experts on compression and whether empty chunks are possible.

sethloco commented 2 months ago

Hello, thank you for reporting this and providing good information and analysis...very helpful. I have published a new version to pub.dev.