From 31fcb8526ca7c4b0fbf6e405cc985e81513f45b2 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 7 Jun 2026 11:20:34 +0100 Subject: [PATCH] add note about providers --- AGENTS.md | 2 +- docs/API.md | 39 +++++++++++++++++++++++++++++++++++++-- docs/USAGE.md | 11 ++++++++--- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f4a0e8de..eaa3e326 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -210,7 +210,7 @@ SharpCompress supports multiple archive and compression formats: ### Public API Documentation Checklist - When adding, removing, renaming, or changing public APIs, update the public docs in the same change. -- Check `docs/API.md` for new factory methods, options, interfaces, extension methods, enums, archive/reader/writer APIs, and examples. +- Check `docs/API.md` for new factory methods, options, interfaces, extension methods, enums, archive/reader/writer APIs, compression provider APIs, and examples. - Check `docs/USAGE.md` when the API change affects recommended usage patterns or requires a new example. - Check `docs/FORMATS.md` when the API change affects supported archive formats, compression methods, reader/archive/writer availability, or detection behavior. - Check `README.md` when the change affects the top-level support summary, target frameworks, major capabilities, or user-facing feature list. diff --git a/docs/API.md b/docs/API.md index 5ce0213e..6e6efce1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -435,10 +435,12 @@ ZipWriterEntryOptions: per-entry ZIP overrides (compression, level, timestamps, ### Compression Providers -`ReaderOptions` and `WriterOptions` expose a `Providers` registry that controls which `ICompressionProvider` implementations are used for each `CompressionType`. The registry defaults to `CompressionProviderRegistry.Default`, so you only need to set it if you want to swap in a custom provider (for example the `SystemGZipCompressionProvider`). The selected registry is honored by Reader/Writer APIs, Archive APIs, and async entry-stream extraction paths. +`ReaderOptions` and `WriterOptions` expose a `Providers` registry that controls which `ICompressionProvider` implementations are used for each `CompressionType`. The registry defaults to `CompressionProviderRegistry.Default`, so you only need to set it if you want to swap in a custom provider (for example the `SystemGZipCompressionProvider` or `SystemDeflateCompressionProvider`). The selected registry is honored by Reader/Writer APIs, Archive APIs, and async entry-stream extraction paths. ```csharp -var registry = CompressionProviderRegistry.Default.With(new SystemGZipCompressionProvider()); +var registry = CompressionProviderRegistry.Default + .With(new SystemGZipCompressionProvider()) + .With(new SystemDeflateCompressionProvider()); var readerOptions = ReaderOptions.ForFilePath.WithProviders(registry); var writerOptions = new WriterOptions(CompressionType.GZip) { @@ -449,6 +451,39 @@ using var reader = ReaderFactory.OpenReader(input, readerOptions); using var writer = WriterFactory.OpenWriter(output, ArchiveType.GZip, writerOptions); ``` +Registry API summary: + +```text +CompressionProviderRegistry.Default: built-in provider registry +CompressionProviderRegistry.Empty: empty registry, primarily useful for tests +With(provider): returns a new registry with that provider added or replaced +GetProvider(type): returns the registered ICompressionProvider, or null +CreateCompressStream(...): creates a compression stream or throws if unsupported +CreateDecompressStream(...): creates a decompression stream or throws if unsupported +CreateCompressStreamAsync(...): async stream creation counterpart +CreateDecompressStreamAsync(...): async stream creation counterpart +GetCompressingProvider(type): returns ICompressionProviderHooks when available +``` + +`ICompressionProvider` implementations declare their `CompressionType`, whether they support compression/decompression, and create sync/async streams. For simple providers, derive from `CompressionProviderBase`; it supplies default async implementations that delegate to the synchronous methods. For read-only codecs, derive from `DecompressionOnlyProviderBase`. + +```csharp +public sealed class CustomGZipProvider : CompressionProviderBase +{ + public override CompressionType CompressionType => CompressionType.GZip; + public override bool SupportsCompression => true; + public override bool SupportsDecompression => true; + + public override Stream CreateCompressStream(Stream destination, int compressionLevel) => + new GZipStream(destination, CompressionMode.Compress, leaveOpen: false); + + public override Stream CreateDecompressStream(Stream source) => + new GZipStream(source, CompressionMode.Decompress, leaveOpen: false); +} +``` + +`CompressionContext` carries metadata that providers may need when creating streams, including `InputSize`, `OutputSize`, `Properties`, `CanSeek`, `FormatOptions`, and `ReaderOptions`. Use `CompressionContext.FromStream(stream)` to populate stream-derived values, `WithReaderOptions(...)` to attach reader metadata, and `ResolveArchiveEncoding()` to get the archive header encoding from the context. + When a format needs additional initialization/finalization data (LZMA, PPMd, etc.) the registry exposes `GetCompressingProvider` which returns the `ICompressionProviderHooks` contract; the rest of the API continues to flow through `Providers`, including pre/properties/post compression hook data. --- diff --git a/docs/USAGE.md b/docs/USAGE.md index 0fc145b1..3c91431b 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -343,11 +343,12 @@ By default `ReaderOptions` and `WriterOptions` already include `CompressionProvi The configured registry is used consistently across Reader APIs, Writer APIs, Archive APIs, and async entry-stream extraction, including compressed TAR wrappers and ZIP async decompression. -To replace a specific algorithm (for example to use `System.IO.Compression` for GZip or Deflate), create a modified registry and pass it through the same options: +To replace specific algorithms (for example to use `System.IO.Compression` for GZip or Deflate), create a modified registry and pass it through the same options: ```C# -var systemGZip = new SystemGZipCompressionProvider(); -var customRegistry = CompressionProviderRegistry.Default.With(systemGZip); +var customRegistry = CompressionProviderRegistry.Default + .With(new SystemGZipCompressionProvider()) + .With(new SystemDeflateCompressionProvider()); var readerOptions = ReaderOptions.ForFilePath .WithProviders(customRegistry); @@ -358,6 +359,10 @@ var writerOptions = new WriterOptions(CompressionType.GZip) using var writer = WriterFactory.OpenWriter(outputStream, ArchiveType.GZip, writerOptions); ``` +The registry is immutable. `With(provider)` returns a new registry and replaces any existing provider for that provider's `CompressionType`. You can inspect or use providers directly with `GetProvider`, `CreateCompressStream`, `CreateDecompressStream`, and their async/context overloads, but most application code should flow the registry through `ReaderOptions` or `WriterOptions` so archive readers and writers can supply the right `CompressionContext`. + +To implement a custom provider, implement `ICompressionProvider` directly or derive from `CompressionProviderBase` for default async methods. Derive from `DecompressionOnlyProviderBase` for read-only codecs. Providers can use `CompressionContext` for stream size, seekability, reader options, compression properties, and format-specific metadata. + The registry also exposes `GetCompressingProvider` (now returning `ICompressionProviderHooks`) when a compression format needs pre- or post-stream data (e.g., LZMA/PPMd). Implementations that need extra headers can supply those bytes through the `ICompressionProviderHooks` members while the rest of the API still works through the `Providers` property. ## Async Examples