Skip to content

Fixes Async Writing#1311

Closed
adamhathcock wants to merge 24 commits into
releasefrom
adam/write-async
Closed

Fixes Async Writing#1311
adamhathcock wants to merge 24 commits into
releasefrom
adam/write-async

Conversation

@adamhathcock
Copy link
Copy Markdown
Owner

This pull request introduces several improvements and refactorings to the handling of TAR and 7z archives, focusing on modernizing async support and streamlining API usage. The main changes include updating the TAR archive factory to simplify how compression types are determined, adding asynchronous methods for writing 7z signature headers, and improving resource management in 7z compression streams.

Key changes:

TAR Archive Factory Refactoring

  • Refactored calls to TarFactory.GetCompressionType and GetCompressionTypeAsync in TarArchive.Factory.cs to pass the full ReaderOptions object instead of just the Providers property, simplifying the API and aligning with updated method signatures. [1] [2] [3] [4] [5] [6]

7z Archive Async Support

  • Added WritePlaceholderAsync and WriteFinalAsync methods to SevenZipSignatureHeader.cs to allow asynchronous writing of 7z signature headers, supporting cancellation tokens and non-blocking I/O. [1] [2]
  • Refactored header construction logic into a private BuildFinalHeader method to reduce code duplication and improve maintainability. [1] [2]

7z Compression Stream Improvements

  • Updated SevenZipStreamsCompressor.cs to use await using for Lzma2EncoderStream and, where supported, for LzmaStream, ensuring proper disposal of async streams and compatibility with newer .NET versions. [1] [2]

Dependency Management

  • Added a new .opencode/package-lock.json file to track and lock dependencies for the .opencode package, ensuring reproducible builds and consistent environments.

Copilot AI and others added 17 commits April 28, 2026 09:33
Agent-Logs-Url: https://github.com/adamhathcock/sharpcompress/sessions/6e4dec7d-12cd-4044-9935-4d354b911d11

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
… writers and compressors

Agent-Logs-Url: https://github.com/adamhathcock/sharpcompress/sessions/6e4dec7d-12cd-4044-9935-4d354b911d11

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
…writes in async code paths

Agent-Logs-Url: https://github.com/adamhathcock/sharpcompress/sessions/6e4dec7d-12cd-4044-9935-4d354b911d11

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes async-writing behavior across multiple archive/compression writers (notably ZIP, TAR, and 7z) to better support async-only streams, adds async finalization APIs, and refactors TAR compression detection and writer construction to streamline options/provider usage.

Changes:

  • Added/extended async disposal + async write paths for writers and compressor streams (ZIP/TAR/7z, plus several compressor primitives).
  • Refactored TAR factory compression probing APIs to accept IReaderOptions (instead of just provider registries) and improved async writer creation for compressed TAR outputs.
  • Expanded test coverage and test utilities to enforce “async-only” stream behavior and validate new async methods.

Reviewed changes

Copilot reviewed 47 out of 48 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/SharpCompress.Test/Zip/ZipMemoryArchiveWithCrcAsyncTests.cs Switches writer usage to await using for async disposal correctness.
tests/SharpCompress.Test/Tar/TarWriterAsyncTests.cs Uses await using and updates AsyncOnlyStream usage to control underlying disposal.
tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs Uses await using for async writer lifetime in TAR async tests.
tests/SharpCompress.Test/Streams/LzmaStreamAsyncTests.cs Validates async disposal and async-only output stream behavior for LZMA encoding.
tests/SharpCompress.Test/Streams/LeaveOpenBehaviorTests.cs Updates LZip tests to use new LZipStream.Create(...) factory API.
tests/SharpCompress.Test/Streams/DisposalTests.cs Updates disposal tests to use new LZip creation API.
tests/SharpCompress.Test/Mocks/AsyncOnlyStream.cs Strengthens mock to throw on sync APIs and adds disposeStream control.
tests/SharpCompress.Test/GZip/GZipWriterAsyncTests.cs Updates writer lifetimes to await using for async disposal.
tests/SharpCompress.Test/GZip/AsyncTests.cs Uses await using for async writer usage.
tests/SharpCompress.Test/BZip2/BZip2StreamAsyncTests.cs Switches to FinishAsync in async BZip2 tests.
tests/SharpCompress.Test/ADCTest.cs Adds CRC32 async write tests for Crc32Stream.
src/SharpCompress/Writers/Zip/ZipWriter.cs Refactors header/footer writing to accept explicit streams; adds async-capable ZipWritingStream write/limit logic.
src/SharpCompress/Writers/Zip/ZipWriter.Async.cs Adds async DisposeAsync, async header emission, and async entry writing path.
src/SharpCompress/Writers/Tar/TarWriter.cs Refactors TAR writer initialization and disposal semantics; stream creation extracted.
src/SharpCompress/Writers/Tar/TarWriter.Async.cs Adds async DisposeAsync and async TAR writing helpers.
src/SharpCompress/Writers/SevenZip/SevenZipWriter.cs Makes 7z placeholder header lazy and introduces sync/async placeholder helpers.
src/SharpCompress/Writers/SevenZip/SevenZipWriter.Async.cs Adds async DisposeAsync and async finalize/header writing path.
src/SharpCompress/Writers/IWriter.cs Removes embedded IAsyncWriter definition (moved to its own file).
src/SharpCompress/Writers/IAsyncWriter.cs Introduces standalone async writer interface.
src/SharpCompress/Writers/GZip/GZipWriter.cs Adds async disposal path for GZip writer output stream.
src/SharpCompress/Writers/AbstractWriter.cs Makes disposal respect LeaveStreamOpen and exposes _isDisposed to derived writers.
src/SharpCompress/Writers/AbstractWriter.Async.cs Makes DisposeAsync virtual for derived overrides.
src/SharpCompress/Providers/IFinishable.cs Adds FinishAsync for async finalization hooks.
src/SharpCompress/Providers/Default/LZipCompressionProvider.cs Adds async create methods and switches to LZipStream.Create/CreateAsync.
src/SharpCompress/IO/CountingStream.cs Adds async flush/write overrides that track written byte counts.
src/SharpCompress/Factories/TarWrapper.cs Updates LZip wrapper to use new LZip factory APIs (sync + async).
src/SharpCompress/Factories/TarFactory.cs Refactors compression probing to use reader options; implements async-compressed TAR writer construction.
src/SharpCompress/Crypto/Crc32Stream.cs Adds async write/flush overrides to support async-only pipelines and new tests.
src/SharpCompress/Compressors/ZStandard/CompressionStream.cs Adjusts async-disposal interface implementation for legacy TFMs.
src/SharpCompress/Compressors/ZStandard/CompressionStream.Async.cs Fixes partial class declaration to avoid duplicate base type.
src/SharpCompress/Compressors/LZMA/LzmaStream.cs Adds IAsyncDisposable to LZMA stream type.
src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs Implements real async write + async disposal behavior for LZMA encoding streams.
src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs Adds async encoding primitives and async coding loop support.
src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs Adds async writes and async disposal; async chunk flush path.
src/SharpCompress/Compressors/LZMA/LZipStream.cs Makes constructor private and removes header write from ctor (moved to factory).
src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs Adds Create/CreateAsync and FinishAsync for LZip; async header write helper.
src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs Improves async disposal compatibility across TFMs and stream types.
src/SharpCompress/Compressors/Deflate/GZipStream.cs Refactors header writing into reusable builder and introduces async header emission.
src/SharpCompress/Compressors/Deflate/GZipStream.Async.cs Uses async header emission and improves async disposal compatibility.
src/SharpCompress/Compressors/Deflate/DeflateStream.cs Adds legacy-TFM async-disposable support.
src/SharpCompress/Compressors/Deflate/DeflateStream.Async.cs Expands async disposal support for legacy TFMs.
src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs Defers header emission; adds async header write + async finish buffering strategy.
src/SharpCompress/Compressors/BZip2/BZip2Stream.cs Adds FinishAsync forwarding.
src/SharpCompress/Compressors/BZip2/BZip2Stream.Async.cs Adds async FinishAsync helper.
src/SharpCompress/Common/SevenZip/SevenZipStreamsCompressor.cs Uses await using for async-disposable LZMA/LZMA2 encoder streams.
src/SharpCompress/Common/SevenZip/SevenZipSignatureHeader.cs Adds async placeholder/final header writing and refactors shared header construction.
src/SharpCompress/Archives/Tar/TarArchive.Factory.cs Updates TAR compression detection calls to pass ReaderOptions.
.opencode/package-lock.json Adds dependency lockfile for .opencode package reproducibility.
Files not reviewed (1)
  • .opencode/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/SharpCompress/Writers/IAsyncWriter.cs
Comment thread src/SharpCompress/Writers/Zip/ZipWriter.cs Outdated
Comment thread src/SharpCompress/Writers/Zip/ZipWriter.cs Outdated
Comment on lines +57 to +65
private void EnsurePlaceholderWritten()
{
if (!_placeholderWritten)
{
_placeholderWritten = true;
// Write placeholder signature header (32 bytes) - will be back-patched on finalize
SevenZipSignatureHeaderWriter.WritePlaceholder(OutputStream.NotNull());
}
}
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SevenZipWriter now writes the 7z signature header placeholder lazily, but FinalizeArchive()/WriteFinal always seek to position 0 and assume the 32-byte placeholder already exists. If the first operation is WriteDirectory (or if the writer is disposed without any Write/WriteAsync), the placeholder is never written, so finalization will back-patch over real data and compute a negative nextHeaderOffset (cast to ulong). Ensure the placeholder is written before any entry (including directory entries) and before finalization when no entries were written.

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +80
private async Task EnsurePlaceholderWrittenAsync(CancellationToken cancellationToken)
{
if (!_placeholderWritten)
{
_placeholderWritten = true;
// Write placeholder signature header (32 bytes) - will be back-patched on finalize
await SevenZipSignatureHeaderWriter
.WritePlaceholderAsync(OutputStream.NotNull(), cancellationToken)
.ConfigureAwait(false);
}
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnsurePlaceholderWrittenAsync sets _placeholderWritten=true before awaiting the actual write. If the write is canceled or fails, the flag remains true and the archive will later finalize by back-patching a header that was never reserved (corrupt output). Set the flag only after the placeholder write completes successfully (or reset it on failure).

Copilot uses AI. Check for mistakes.
Comment thread src/SharpCompress/Writers/SevenZip/SevenZipWriter.Async.cs
Comment thread src/SharpCompress/Writers/Tar/TarWriter.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants