As of v0.21, the library is documented to close wrapped streams by default.
However, the ReaderOptions.LeaveStreamOpen property defaulted to true, which
contradicted the documented behavior and caused file locks when deleting
archives after extraction.
Changes:
- Set LeaveStreamOpen = false as the default (consistent with v0.21 design)
- Updated ReaderOptions.cs with comprehensive documentation explaining:
* File-based vs caller-provided stream handling
* When to use LeaveStreamOpen = true
* Usage examples for both scenarios
- Updated AGENTS.md Stream Handling Rules section with:
* Clear explanation of default disposal behavior
* Overload differences (file-based vs stream-based)
* Guidance on using ForExternalStream preset
This ensures the implementation matches the documented behavior and prevents
unexpected file locking issues during archive deletion.
Fixes: File deletion failures after archive extraction
* Initial plan
* Fix IsSolidAsync() returning false for 7z solid archives
SevenZipArchive was missing an override for IsSolidAsync(), so the
base class default (always returning false) was used. Added an override
that uses the same logic as the sync IsSolid property: load entries
asynchronously and check if any folder has more than one file.
Also updated existing async tests to use IsSolidAsync() instead of
casting to SevenZipArchive to call the sync IsSolid, and added a new
SevenZipArchive_TestSolidDetectionAsync test that mirrors the existing
sync SevenZipArchive_TestSolidDetection test.
Agent-Logs-Url: https://github.com/adamhathcock/sharpcompress/sessions/60f8daec-e784-4845-a65c-5a493fbb53ef
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
1. Fix off-by-one in Update(BLAKE2SP): pos was masked with 448 (64×7)
instead of 511 (64×8−1), causing incorrect leaf assignment when update
chunks are not multiples of 64 bytes. This produced wrong hashes
whenever streaming reads didn't align to 64-byte boundaries.
2. Fix double-finalization when Read() is called again after returning 0.
Final() was called unconditionally on each EOF read, re-running
compression on an already-finalized state and corrupting the hash.
EnsureHash() guards with a null-check and is idempotent; both sync
and async Read paths share the fix. _blake2sp is set to null after
finalization so any erroneous post-final calls fail fast rather than
silently corrupting state.
3. Fix false-positive CRC check when stream is not fully drained.
_hash was initialized to fileHeader.FileCrc in the constructor, so
GetCrc() returned the expected CRC rather than the computed one if
the stream was abandoned early. The check would then compare the
expected hash against itself and always succeed, silently accepting
a corrupt or incomplete file. _hash is now null until the stream is
fully read; GetCrc() throws if called early rather than returning a
misleading value.
Additional changes:
- Compress: stackalloc m/v arrays instead of heap; LE fast path via
MemoryMarshal.Cast replaces 16 BitConverter.ToUInt32 calls per block
- Final(BLAKE2S): write directly to Span<byte>, eliminating MemoryStream
and BitConverter.GetBytes allocations; 8 leaf digests now stackalloc'd
- Inner classes and fields: private, sealed, readonly where applicable