When SeekableSharpCompressStream.StopRecording() was called (e.g. after the tar
probe in GZipFactory.TryOpenReader), it cleared _recordedPosition without seeking
back to it. This left the underlying FileStream at the advanced position reached
during the probe, so the subsequent GZipReader would start reading from the
wrong offset, producing Key=null and 0 extracted bytes.
Fix: seek back to _recordedPosition before clearing it, matching the behavior of
the non-seekable SharpCompressStream.StopRecording() which rewinds _logicalPosition.
Also enhance the existing GZip_ReaderFactory_FlatGZip test to actually verify the
extracted content (not just MoveToNextEntry returns true), and add an async variant.
Closes#1391
Two releases since the generator was adopted here, both fixing things this
repository ran into.
2.0.41 emits the using directives of the source file into the generated one.
Documentation is copied across verbatim and a cref in it resolves against the
file it lands in, so crefs which relied on a using went unresolved and the
compiler reported CS1574 for each.
2.0.42 spaces a rewritten argument list the way it was written. The generated
extension calls here were coming out as
IArchiveEntryExtensions.WriteTo(archiveEntry, streamToWriteTo,
Constants.BufferSize,
progress: progress) ;
and now come out as
IArchiveEntryExtensions.WriteTo(archiveEntry, streamToWriteTo,
Constants.BufferSize,
progress: progress);
No change to what is generated, only to how it is spaced and what its
documentation can refer to. Full test suite passes unchanged.
The Zip64 branch of the streaming header reader assumed a data descriptor always follows a >=4GB entry. When the entry instead has back-patched sizes (no descriptor), the following header was parsed as descriptor fields, overwriting the entry's correct size/CRC with central-directory 0xFFFFFFFF sentinels and, on non-seekable async streams, leaving the reader misaligned so the next entry threw.
Now it detects a header signature after the entry data and leaves the already-correct metadata untouched, parsing that header normally. The fix has been applied to both the sync and async readers.
Add a `tolerateTruncatedStream` option (default false) to `BZip2Stream.Create`/`CreateAsync`,
threaded through to `CBZip2InputStream`. When enabled, the decoder accepts a stream that has no
trailing footer - e.g. a truncated stream, or a sub-range of blocks extracted for random access:
- An end-of-input reached while reading a block/footer header (a true block boundary, tracked by
`expectingBlockStart`) is treated as a normal end of stream instead of throwing
`ArchiveOperationException("BZip2 compressed file ends unexpectedly")`. EOF in the middle of a
block, the block CRC, or the Huffman tables still throws.
- The whole-stream combined CRC in the footer is not verified, since a partial decode's running
combined CRC won't match the stored whole-stream value. Per-block CRCs are still enforced.
Default behaviour is unchanged (the flag defaults to false) and the change is applied symmetrically
to the sync and async read paths.
Tests (BZip2StreamTests):
- a footerless header-only stream decodes to empty with the flag and throws without it;
- a real block followed by end-of-input at the next block boundary decodes with the flag and throws
without it;
- a corrupted whole-stream combined CRC is tolerated with the flag and fatal without it;
- a complete, well-formed stream still round-trips with the flag set.
All existing BZip2 tests continue to pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>