From fb5b5e9a6b59f7e6617ad533d2e292fe429a115d Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 17 Feb 2026 11:08:34 +0000 Subject: [PATCH] API interface with mermaid diagrams --- docs/INTERFACES.md | 559 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 559 insertions(+) create mode 100644 docs/INTERFACES.md diff --git a/docs/INTERFACES.md b/docs/INTERFACES.md new file mode 100644 index 00000000..8320ee69 --- /dev/null +++ b/docs/INTERFACES.md @@ -0,0 +1,559 @@ +# SharpCompress Interface Architecture + +This document describes the interface hierarchy for archives, readers, and writers in SharpCompress. + +## Overview + +SharpCompress provides three main APIs for working with compressed archives: + +| API | Use Case | Stream Requirements | Access Pattern | +|-----|----------|---------------------|----------------| +| **Archive** | Random access to entries | Seekable stream | Load all metadata, extract any entry | +| **Reader** | Sequential streaming | Non-seekable OK | Forward-only, memory efficient | +| **Writer** | Creating archives | Non-seekable OK | Forward-only writes | + +--- + +## Core Interface Hierarchies + +### Archive Interfaces + +```mermaid +classDiagram + direction TB + + class IDisposable { + <> + +Dispose() + } + + class IAsyncDisposable { + <> + +DisposeAsync() + } + + class IArchive { + <> + +Entries : IEnumerable~IArchiveEntry~ + +Volumes : IEnumerable~IVolume~ + +Type : ArchiveType + +ReaderOptions : ReaderOptions + +IsSolid : bool + +IsComplete : bool + +IsEncrypted : bool + +TotalSize : long + +TotalUncompressedSize : long + +ExtractAllEntries() IReader + } + + class IAsyncArchive { + <> + +EntriesAsync : IAsyncEnumerable~IArchiveEntry~ + +VolumesAsync : IAsyncEnumerable~IVolume~ + +Type : ArchiveType + +ExtractAllEntriesAsync() ValueTask~IAsyncReader~ + +IsSolidAsync() ValueTask~bool~ + +IsCompleteAsync() ValueTask~bool~ + +IsEncryptedAsync() ValueTask~bool~ + +TotalSizeAsync() ValueTask~long~ + +TotalUncompressedSizeAsync() ValueTask~long~ + } + + class IExtractableArchive { + <> + +Entries : IEnumerable~IExtractableArchiveEntry~ + } + + class IExtractableAsyncArchive { + <> + +EntriesAsync : IAsyncEnumerable~IExtractableArchiveEntry~ + } + + class IWritableArchiveCommon { + <> + +PauseEntryRebuilding() IDisposable + } + + class IWritableArchive { + <> + +AddEntry(key, source, closeStream, size, modified) IArchiveEntry + +AddDirectoryEntry(key, modified) IArchiveEntry + +RemoveEntry(entry) + } + + class IWritableArchive~TOptions~ { + <> + +SaveTo(stream, options) + } + + class IWritableAsyncArchive { + <> + +AddEntryAsync(...) ValueTask~IArchiveEntry~ + +AddDirectoryEntryAsync(...) ValueTask~IArchiveEntry~ + +RemoveEntryAsync(entry) ValueTask + } + + class IWritableAsyncArchive~TOptions~ { + <> + +SaveToAsync(stream, options, ct) ValueTask + } + + IDisposable <|-- IArchive + IAsyncDisposable <|-- IAsyncArchive + + IArchive <|-- IExtractableArchive + IAsyncArchive <|-- IExtractableAsyncArchive + + IArchive <|-- IWritableArchive + IWritableArchiveCommon <|-- IWritableArchive + IWritableArchive <|-- IWritableArchive~TOptions~ + + IAsyncArchive <|-- IWritableAsyncArchive + IWritableArchiveCommon <|-- IWritableAsyncArchive + IWritableAsyncArchive <|-- IWritableAsyncArchive~TOptions~ +``` + +### Reader Interfaces + +```mermaid +classDiagram + direction TB + + class IDisposable { + <> + +Dispose() + } + + class IAsyncDisposable { + <> + +DisposeAsync() + } + + class IReader { + <> + +ArchiveType : ArchiveType + +Entry : IEntry + +Cancelled : bool + +MoveToNextEntry() bool + +WriteEntryTo(stream) + +OpenEntryStream() EntryStream + +Cancel() + } + + class IAsyncReader { + <> + +ArchiveType : ArchiveType + +Entry : IEntry + +Cancelled : bool + +MoveToNextEntryAsync(ct) ValueTask~bool~ + +WriteEntryToAsync(stream, ct) ValueTask + +OpenEntryStreamAsync(ct) ValueTask~EntryStream~ + +Cancel() + } + + IDisposable <|-- IReader + IAsyncDisposable <|-- IAsyncReader +``` + +### Writer Interfaces + +```mermaid +classDiagram + direction TB + + class IDisposable { + <> + +Dispose() + } + + class IAsyncDisposable { + <> + +DisposeAsync() + } + + class IWriter { + <> + +WriterType : ArchiveType + +Write(filename, source, modificationTime) + +WriteDirectory(directoryName, modificationTime) + } + + class IAsyncWriter { + <> + +WriterType : ArchiveType + +WriteAsync(filename, source, modificationTime, ct) ValueTask + +WriteDirectoryAsync(directoryName, modificationTime, ct) ValueTask + } + + IDisposable <|-- IWriter + IDisposable <|-- IAsyncWriter + IAsyncDisposable <|-- IAsyncWriter +``` + +--- + +## Entry Interfaces + +```mermaid +classDiagram + direction TB + + class IEntry { + <> + +Key : string? + +Size : long + +CompressedSize : long + +CompressionType : CompressionType + +Crc : long + +IsDirectory : bool + +IsEncrypted : bool + +IsSolid : bool + +IsSplitAfter : bool + +LinkTarget : string? + +Attrib : int? + +ArchivedTime : DateTime? + +CreatedTime : DateTime? + +LastAccessedTime : DateTime? + +LastModifiedTime : DateTime? + +VolumeIndexFirst : int + +VolumeIndexLast : int + +Options : IReaderOptions + } + + class IArchiveEntry { + <> + +IsComplete : bool + +Archive : IArchive + } + + class IExtractableArchiveEntry { + <> + +OpenEntryStream() Stream + +OpenEntryStreamAsync(ct) ValueTask~Stream~ + } + + IEntry <|-- IArchiveEntry + IArchiveEntry <|-- IExtractableArchiveEntry +``` + +--- + +## Factory Interfaces + +```mermaid +classDiagram + direction TB + + class IFactory { + <> + +Name : string + +KnownArchiveType : ArchiveType? + +GetSupportedExtensions() IEnumerable~string~ + +IsArchive(stream, password) bool + +IsArchiveAsync(stream, password, ct) ValueTask~bool~ + +GetFilePart(index, part1) FileInfo? + } + + class IArchiveFactory { + <> + +OpenArchive(stream, options) IArchive + +OpenArchive(fileInfo, options) IArchive + +OpenAsyncArchive(stream, options, ct) ValueTask~IAsyncArchive~ + +OpenAsyncArchive(fileInfo, options, ct) ValueTask~IAsyncArchive~ + } + + class IMultiArchiveFactory { + <> + +OpenArchive(streams, options) IArchive + +OpenArchive(fileInfos, options) IArchive + +OpenAsyncArchive(streams, options) IAsyncArchive + +OpenAsyncArchive(fileInfos, options) IAsyncArchive + } + + class IReaderFactory { + <> + +OpenReader(stream, options) IReader + +OpenAsyncReader(stream, options, ct) ValueTask~IAsyncReader~ + } + + class IWriterFactory { + <> + +OpenWriter(stream, options) IWriter + +OpenAsyncWriter(stream, options, ct) IAsyncWriter + } + + IFactory <|-- IArchiveFactory + IFactory <|-- IMultiArchiveFactory + IFactory <|-- IReaderFactory + IFactory <|-- IWriterFactory +``` + +--- + +## Format-Specific Interfaces + +Each archive format has marker interfaces that inherit from the core interfaces: + +### Archive Formats + +```mermaid +classDiagram + direction LR + + class IArchive { + <> + } + class IAsyncArchive { + <> + } + class IExtractableArchive { + <> + } + class IExtractableAsyncArchive { + <> + } + + class IZipArchive { + <> + } + class IZipAsyncArchive { + <> + } + class ITarArchive { + <> + } + class ITarAsyncArchive { + <> + } + class IGZipArchive { + <> + } + class IGZipAsyncArchive { + <> + } + class ISevenZipArchive { + <> + } + class ISevenZipAsyncArchive { + <> + } + + IExtractableArchive <|-- IZipArchive + IExtractableAsyncArchive <|-- IZipAsyncArchive + + IExtractableArchive <|-- ITarArchive + IExtractableAsyncArchive <|-- ITarAsyncArchive + + IExtractableArchive <|-- IGZipArchive + IExtractableAsyncArchive <|-- IGZipAsyncArchive + + IArchive <|-- ISevenZipArchive : "No random extraction" + IAsyncArchive <|-- ISevenZipAsyncArchive : "No random extraction" +``` + +> **Note:** 7Zip archives implement `IArchive` directly (not `IExtractableArchive`) because the format requires sequential decompression - you cannot extract individual entries randomly. + +### Reader Formats + +```mermaid +classDiagram + direction LR + + class IReader { + <> + } + class IAsyncReader { + <> + } + + class IZipReader { + <> + } + class IZipAsyncReader { + <> + } + class ITarReader { + <> + } + class ITarAsyncReader { + <> + } + class IGZipReader { + <> + } + class IRarReader { + <> + } + class IAceReader { + <> + } + class IArcReader { + <> + } + class IArjReader { + <> + } + class ILzwReader { + <> + } + + IReader <|-- IZipReader + IAsyncReader <|-- IZipAsyncReader + IReader <|-- ITarReader + IAsyncReader <|-- ITarAsyncReader + IReader <|-- IGZipReader + IReader <|-- IRarReader + IReader <|-- IAceReader + IReader <|-- IArcReader + IReader <|-- IArjReader + IReader <|-- ILzwReader +``` + +--- + +## Relationship Overview + +```mermaid +flowchart TB + subgraph "Entry Points" + AF[ArchiveFactory] + RF[ReaderFactory] + WF[WriterFactory] + end + + subgraph "Archives (Random Access)" + IA[IArchive] + IAA[IAsyncArchive] + IEA[IExtractableArchive] + IWA[IWritableArchive] + end + + subgraph "Readers (Sequential)" + IR[IReader] + IAR[IAsyncReader] + end + + subgraph "Writers (Sequential)" + IW[IWriter] + IAW[IAsyncWriter] + end + + subgraph "Entries" + IE[IEntry] + IAE[IArchiveEntry] + IEAE[IExtractableArchiveEntry] + end + + AF -->|OpenArchive| IA + AF -->|OpenAsyncArchive| IAA + RF -->|OpenReader| IR + RF -->|OpenAsyncReader| IAR + WF -->|OpenWriter| IW + WF -->|OpenAsyncWriter| IAW + + IA -->|ExtractAllEntries| IR + IAA -->|ExtractAllEntriesAsync| IAR + + IA -->|Entries| IAE + IEA -->|Entries| IEAE + IR -->|Entry| IE + + IEAE -->|OpenEntryStream| Stream + IR -->|OpenEntryStream| Stream +``` + +--- + +## API Selection Guide + +### When to use Archive API + +- You have a **seekable stream** (file or memory with full access) +- Need **random access** to specific entries +- Want to **list all entries** before extracting +- Need to **modify** the archive (add/remove entries) + +```csharp +// Sync +using var archive = ArchiveFactory.OpenArchive(stream); +foreach (var entry in archive.Entries.Where(e => !e.IsDirectory)) +{ + entry.OpenEntryStream(); // Random access +} + +// Async +await using var archive = await ArchiveFactory.OpenAsyncArchive(stream); +await foreach (var entry in archive.EntriesAsync) +{ + await entry.OpenEntryStreamAsync(); +} +``` + +### When to use Reader API + +- Working with **non-seekable streams** (network, pipes) +- Processing **large archives** where memory is a concern +- Only need **sequential forward** access +- Processing entries **as they arrive** + +```csharp +// Sync +using var reader = ReaderFactory.OpenReader(stream); +while (reader.MoveToNextEntry()) +{ + reader.WriteEntryTo(outputStream); +} + +// Async +await using var reader = await ReaderFactory.OpenAsyncReader(stream); +while (await reader.MoveToNextEntryAsync()) +{ + await reader.WriteEntryToAsync(outputStream); +} +``` + +### When to use Writer API + +- **Creating** new archives +- **Streaming** content into archives +- Writing to **non-seekable** output streams + +```csharp +// Sync +using var writer = WriterFactory.OpenWriter(outputStream, options); +writer.Write("file.txt", contentStream, DateTime.Now); + +// Async +await using var writer = WriterFactory.OpenAsyncWriter(outputStream, options); +await writer.WriteAsync("file.txt", contentStream, DateTime.Now); +``` + +--- + +## Interface Capabilities by Format + +| Format | IArchive | IExtractableArchive | IReader | IWriter | +|--------|----------|---------------------|---------|---------| +| **Zip** | ✅ | ✅ | ✅ | ✅ | +| **Tar** | ✅ | ✅ | ✅ | ✅ | +| **GZip** | ✅ | ✅ | ✅ | ✅ | +| **7Zip** | ✅ | ❌ (sequential only) | ❌ | ❌ | +| **Rar** | ✅ | ✅ | ✅ (read-only) | ❌ | +| **Ace** | ❌ | ❌ | ✅ (read-only) | ❌ | +| **Arc** | ❌ | ❌ | ✅ (read-only) | ❌ | +| **Arj** | ❌ | ❌ | ✅ (read-only) | ❌ | +| **Lzw** | ❌ | ❌ | ✅ (read-only) | ❌ | + +--- + +## Key Design Decisions + +1. **Sync/Async Split**: Each major interface has both sync (`IArchive`) and async (`IAsyncArchive`) variants for different use cases. + +2. **IExtractableArchive**: Separates archives that support random entry extraction from those that don't (7Zip requires sequential processing). + +3. **Marker Interfaces**: Format-specific interfaces (`IZipArchive`, `ITarReader`) are markers that allow type-safe casting when format-specific features are needed. + +4. **Factory Pattern**: All instances are created through factories (`ArchiveFactory`, `ReaderFactory`, `WriterFactory`) which handle format detection and instantiation. + +5. **Entry Hierarchy**: `IEntry` → `IArchiveEntry` → `IExtractableArchiveEntry` provides progressive capabilities.