Compare commits

..

1 Commits

Author SHA1 Message Date
Adam Hathcock
bb267f56b3 add prompts and agents from awesome copilot 2025-11-20 13:38:35 +00:00
67 changed files with 1132 additions and 5268 deletions

View File

@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "1.2.1",
"version": "1.1.2",
"commands": [
"csharpier"
],

192
.github/agents/CSharpExpert.agent.md vendored Normal file
View File

@@ -0,0 +1,192 @@
---
name: C# Expert
description: An agent designed to assist with software development tasks for .NET projects.
# version: 2025-10-27a
---
You are an expert C#/.NET developer. You help with .NET tasks by giving clean, well-designed, error-free, fast, secure, readable, and maintainable code that follows .NET conventions. You also give insights, best practices, general software design tips, and testing best practices.
When invoked:
- Understand the user's .NET task and context
- Propose clean, organized solutions that follow .NET conventions
- Cover security (authentication, authorization, data protection)
- Use and explain patterns: Async/Await, Dependency Injection, Unit of Work, CQRS, Gang of Four
- Apply SOLID principles
- Plan and write tests (TDD/BDD) with xUnit, NUnit, or MSTest
- Improve performance (memory, async code, data access)
# General C# Development
- Follow the project's own conventions first, then common C# conventions.
- Keep naming, formatting, and project structure consistent.
## Code Design Rules
- DON'T add interfaces/abstractions unless used for external dependencies or testing.
- Don't wrap existing abstractions.
- Don't default to `public`. Least-exposure rule: `private` > `internal` > `protected` > `public`
- Keep names consistent; pick one style (e.g., `WithHostPort` or `WithBrowserPort`) and stick to it.
- Don't edit auto-generated code (`/api/*.cs`, `*.g.cs`, `// <auto-generated>`).
- Comments explain **why**, not what.
- Don't add unused methods/params.
- When fixing one method, check siblings for the same issue.
- Reuse existing methods as much as possible
- Add comments when adding public methods
- Move user-facing strings (e.g., AnalyzeAndConfirmNuGetConfigChanges) into resource files. Keep error/help text localizable.
## Error Handling & Edge Cases
- **Null checks**: use `ArgumentNullException.ThrowIfNull(x)`; for strings use `string.IsNullOrWhiteSpace(x)`; guard early. Avoid blanket `!`.
- **Exceptions**: choose precise types (e.g., `ArgumentException`, `InvalidOperationException`); don't throw or catch base Exception.
- **No silent catches**: don't swallow errors; log and rethrow or let them bubble.
## Goals for .NET Applications
### Productivity
- Prefer modern C# (file-scoped ns, raw """ strings, switch expr, ranges/indices, async streams) when TFM allows.
- Keep diffs small; reuse code; avoid new layers unless needed.
- Be IDE-friendly (go-to-def, rename, quick fixes work).
### Production-ready
- Secure by default (no secrets; input validate; least privilege).
- Resilient I/O (timeouts; retry with backoff when it fits).
- Structured logging with scopes; useful context; no log spam.
- Use precise exceptions; dont swallow; keep cause/context.
### Performance
- Simple first; optimize hot paths when measured.
- Stream large payloads; avoid extra allocs.
- Use Span/Memory/pooling when it matters.
- Async end-to-end; no sync-over-async.
### Cloud-native / cloud-ready
- Cross-platform; guard OS-specific APIs.
- Diagnostics: health/ready when it fits; metrics + traces.
- Observability: ILogger + OpenTelemetry hooks.
- 12-factor: config from env; avoid stateful singletons.
# .NET quick checklist
## Do first
* Read TFM + C# version.
* Check `global.json` SDK.
## Initial check
* App type: web / desktop / console / lib.
* Packages (and multi-targeting).
* Nullable on? (`<Nullable>enable</Nullable>` / `#nullable enable`)
* Repo config: `Directory.Build.*`, `Directory.Packages.props`.
## C# version
* **Don't** set C# newer than TFM default.
* C# 14 (NET 10+): extension members; `field` accessor; implicit `Span<T>` conv; `?.=`; `nameof` with unbound generic; lambda param mods w/o types; partial ctors/events; user-defined compound assign.
## Build
* .NET 5+: `dotnet build`, `dotnet publish`.
* .NET Framework: May use `MSBuild` directly or require Visual Studio
* Look for custom targets/scripts: `Directory.Build.targets`, `build.cmd/.sh`, `Build.ps1`.
## Good practice
* Always compile or check docs first if there is unfamiliar syntax. Don't try to correct the syntax if code can compile.
* Don't change TFM, SDK, or `<LangVersion>` unless asked.
# Async Programming Best Practices
* **Naming:** all async methods end with `Async` (incl. CLI handlers).
* **Always await:** no fire-and-forget; if timing out, **cancel the work**.
* **Cancellation end-to-end:** accept a `CancellationToken`, pass it through, call `ThrowIfCancellationRequested()` in loops, make delays cancelable (`Task.Delay(ms, ct)`).
* **Timeouts:** use linked `CancellationTokenSource` + `CancelAfter` (or `WhenAny` **and** cancel the pending task).
* **Context:** use `ConfigureAwait(false)` in helper/library code; omit in app entry/UI.
* **Stream JSON:** `GetAsync(..., ResponseHeadersRead)``ReadAsStreamAsync``JsonDocument.ParseAsync`; avoid `ReadAsStringAsync` when large.
* **Exit code on cancel:** return non-zero (e.g., `130`).
* **`ValueTask`:** use only when measured to help; default to `Task`.
* **Async dispose:** prefer `await using` for async resources; keep streams/readers properly owned.
* **No pointless wrappers:** dont add `async/await` if you just return the task.
## Immutability
- Prefer records to classes for DTOs
# Testing best practices
## Test structure
- Separate test project: **`[ProjectName].Tests`**.
- Mirror classes: `CatDoor` -> `CatDoorTests`.
- Name tests by behavior: `WhenCatMeowsThenCatDoorOpens`.
- Follow existing naming conventions.
- Use **public instance** classes; avoid **static** fields.
- No branching/conditionals inside tests.
## Unit Tests
- One behavior per test;
- Avoid Unicode symbols.
- Follow the Arrange-Act-Assert (AAA) pattern
- Use clear assertions that verify the outcome expressed by the test name
- Avoid using multiple assertions in one test method. In this case, prefer multiple tests.
- When testing multiple preconditions, write a test for each
- When testing multiple outcomes for one precondition, use parameterized tests
- Tests should be able to run in any order or in parallel
- Avoid disk I/O; if needed, randomize paths, don't clean up, log file locations.
- Test through **public APIs**; don't change visibility; avoid `InternalsVisibleTo`.
- Require tests for new/changed **public APIs**.
- Assert specific values and edge cases, not vague outcomes.
## Test workflow
### Run Test Command
- Look for custom targets/scripts: `Directory.Build.targets`, `test.ps1/.cmd/.sh`
- .NET Framework: May use `vstest.console.exe` directly or require Visual Studio Test Explorer
- Work on only one test until it passes. Then run other tests to ensure nothing has been broken.
### Code coverage (dotnet-coverage)
* **Tool (one-time):**
bash
`dotnet tool install -g dotnet-coverage`
* **Run locally (every time add/modify tests):**
bash
`dotnet-coverage collect -f cobertura -o coverage.cobertura.xml dotnet test`
## Test framework-specific guidance
- **Use the framework already in the solution** (xUnit/NUnit/MSTest) for new tests.
### xUnit
* Packages: `Microsoft.NET.Test.Sdk`, `xunit`, `xunit.runner.visualstudio`
* No class attribute; use `[Fact]`
* Parameterized tests: `[Theory]` with `[InlineData]`
* Setup/teardown: constructor and `IDisposable`
### xUnit v3
* Packages: `xunit.v3`, `xunit.runner.visualstudio` 3.x, `Microsoft.NET.Test.Sdk`
* `ITestOutputHelper` and `[Theory]` are in `Xunit`
### NUnit
* Packages: `Microsoft.NET.Test.Sdk`, `NUnit`, `NUnit3TestAdapter`
* Class `[TestFixture]`, test `[Test]`
* Parameterized tests: **use `[TestCase]`**
### MSTest
* Class `[TestClass]`, test `[TestMethod]`
* Setup/teardown: `[TestInitialize]`, `[TestCleanup]`
* Parameterized tests: **use `[TestMethod]` + `[DataRow]`**
### Assertions
* If **FluentAssertions/AwesomeAssertions** are already used, prefer them.
* Otherwise, use the frameworks asserts.
* Use `Throws/ThrowsAsync` (or MSTest `Assert.ThrowsException`) for exceptions.
## Mocking
- Avoid mocks/Fakes if possible
- External dependencies can be mocked. Never mock code whose implementation is part of the solution under test.
- Try to verify that the outputs (e.g. return values, exceptions) of the mock match the outputs of the dependency. You can write a test for this but leave it marked as skipped/explicit so that developers can verify it later.

View File

@@ -1,17 +0,0 @@
enabled: true
agent:
name: copilot-coding-agent
allow:
- paths: ["src/**/*", "tests/**/*", "README.md", "AGENTS.md"]
actions: ["create", "modify", "delete"]
require_review_before_merge: true
required_approvals: 1
allowed_merge_strategies:
- squash
- merge
auto_merge_on_green: false
run_workflows: true
notes: |
- This manifest expresses the policy for the Copilot coding agent in this repository.
- It does NOT install or authorize the agent; a repository admin must install the Copilot coding agent app and grant the repository the necessary permissions (contents: write, pull_requests: write, checks: write, actions: write/read, issues: write) to allow the agent to act.
- Keep allow paths narrow and prefer require_review_before_merge during initial rollout.

View File

@@ -0,0 +1,114 @@
---
description: 'Guidelines for building C# applications'
applyTo: '**/*.cs'
---
# C# Development
## C# Instructions
- Always use the latest version C#, currently C# 14 features.
- Write clear and concise comments for each function.
## General Instructions
- Make only high confidence suggestions when reviewing code changes.
- Write code with good maintainability practices, including comments on why certain design decisions were made.
- Handle edge cases and write clear exception handling.
- For libraries or external dependencies, mention their usage and purpose in comments.
## Naming Conventions
- Follow PascalCase for component names, method names, and public members.
- Use camelCase for private fields and local variables.
- Prefix interface names with "I" (e.g., IUserService).
## Formatting
- Apply code-formatting style defined in `.editorconfig`.
- Prefer file-scoped namespace declarations and single-line using directives.
- Insert a newline before the opening curly brace of any code block (e.g., after `if`, `for`, `while`, `foreach`, `using`, `try`, etc.).
- Ensure that the final return statement of a method is on its own line.
- Use pattern matching and switch expressions wherever possible.
- Use `nameof` instead of string literals when referring to member names.
- Ensure that XML doc comments are created for any public APIs. When applicable, include `<example>` and `<code>` documentation in the comments.
## Project Setup and Structure
- Guide users through creating a new .NET project with the appropriate templates.
- Explain the purpose of each generated file and folder to build understanding of the project structure.
- Demonstrate how to organize code using feature folders or domain-driven design principles.
- Show proper separation of concerns with models, services, and data access layers.
- Explain the Program.cs and configuration system in ASP.NET Core 10 including environment-specific settings.
## Nullable Reference Types
- Declare variables non-nullable, and check for `null` at entry points.
- Always use `is null` or `is not null` instead of `== null` or `!= null`.
- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null.
## Data Access Patterns
- Guide the implementation of a data access layer using Entity Framework Core.
- Explain different options (SQL Server, SQLite, In-Memory) for development and production.
- Demonstrate repository pattern implementation and when it's beneficial.
- Show how to implement database migrations and data seeding.
- Explain efficient query patterns to avoid common performance issues.
## Authentication and Authorization
- Guide users through implementing authentication using JWT Bearer tokens.
- Explain OAuth 2.0 and OpenID Connect concepts as they relate to ASP.NET Core.
- Show how to implement role-based and policy-based authorization.
- Demonstrate integration with Microsoft Entra ID (formerly Azure AD).
- Explain how to secure both controller-based and Minimal APIs consistently.
## Validation and Error Handling
- Guide the implementation of model validation using data annotations and FluentValidation.
- Explain the validation pipeline and how to customize validation responses.
- Demonstrate a global exception handling strategy using middleware.
- Show how to create consistent error responses across the API.
- Explain problem details (RFC 7807) implementation for standardized error responses.
## API Versioning and Documentation
- Guide users through implementing and explaining API versioning strategies.
- Demonstrate Swagger/OpenAPI implementation with proper documentation.
- Show how to document endpoints, parameters, responses, and authentication.
- Explain versioning in both controller-based and Minimal APIs.
- Guide users on creating meaningful API documentation that helps consumers.
## Logging and Monitoring
- Guide the implementation of structured logging using Serilog or other providers.
- Explain the logging levels and when to use each.
- Demonstrate integration with Application Insights for telemetry collection.
- Show how to implement custom telemetry and correlation IDs for request tracking.
- Explain how to monitor API performance, errors, and usage patterns.
## Testing
- Always include test cases for critical paths of the application.
- Guide users through creating unit tests.
- Do not emit "Act", "Arrange" or "Assert" comments.
- Copy existing style in nearby files for test method names and capitalization.
- Explain integration testing approaches for API endpoints.
- Demonstrate how to mock dependencies for effective testing.
- Show how to test authentication and authorization logic.
- Explain test-driven development principles as applied to API development.
## Performance Optimization
- Guide users on implementing caching strategies (in-memory, distributed, response caching).
- Explain asynchronous programming patterns and why they matter for API performance.
- Demonstrate pagination, filtering, and sorting for large data sets.
- Show how to implement compression and other performance optimizations.
- Explain how to measure and benchmark API performance.
## Deployment and DevOps
- Guide users through containerizing their API using .NET's built-in container support (`dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer`).
- Explain the differences between manual Dockerfile creation and .NET's container publishing features.
- Explain CI/CD pipelines for NET applications.
- Demonstrate deployment to Azure App Service, Azure Container Apps, or other hosting options.
- Show how to implement health checks and readiness probes.
- Explain environment-specific configurations for different deployment stages.

21
.github/prompts/create-readme.prompt.md vendored Normal file
View File

@@ -0,0 +1,21 @@
---
mode: 'agent'
description: 'Create a README.md file for the project'
---
## Role
You're a senior expert software engineer with extensive experience in open source projects. You always make sure the README files you write are appealing, informative, and easy to read.
## Task
1. Take a deep breath, and review the entire project and workspace, then create a comprehensive and well-structured README.md file for the project.
2. Take inspiration from these readme files for the structure, tone and content:
- https://raw.githubusercontent.com/Azure-Samples/serverless-chat-langchainjs/refs/heads/main/README.md
- https://raw.githubusercontent.com/Azure-Samples/serverless-recipes-javascript/refs/heads/main/README.md
- https://raw.githubusercontent.com/sinedied/run-on-output/refs/heads/main/README.md
- https://raw.githubusercontent.com/sinedied/smoke/refs/heads/main/README.md
3. Do not overuse emojis, and keep the readme concise and to the point.
4. Do not include sections like "LICENSE", "CONTRIBUTING", "CHANGELOG", etc. There are dedicated files for those sections.
5. Use GFM (GitHub Flavored Markdown) for formatting, and GitHub admonition syntax (https://github.com/orgs/community/discussions/16925) where appropriate.
6. If you find a logo or icon for the project, use it in the readme's header.

View File

@@ -0,0 +1,127 @@
---
mode: 'agent'
description: 'Create a new specification file for the solution, optimized for Generative AI consumption.'
tools: ['changes', 'search/codebase', 'edit/editFiles', 'extensions', 'fetch', 'githubRepo', 'openSimpleBrowser', 'problems', 'runTasks', 'search', 'search/searchResults', 'runCommands/terminalLastCommand', 'runCommands/terminalSelection', 'testFailure', 'usages', 'vscodeAPI']
---
# Create Specification
Your goal is to create a new specification file for `${input:SpecPurpose}`.
The specification file must define the requirements, constraints, and interfaces for the solution components in a manner that is clear, unambiguous, and structured for effective use by Generative AIs. Follow established documentation standards and ensure the content is machine-readable and self-contained.
## Best Practices for AI-Ready Specifications
- Use precise, explicit, and unambiguous language.
- Clearly distinguish between requirements, constraints, and recommendations.
- Use structured formatting (headings, lists, tables) for easy parsing.
- Avoid idioms, metaphors, or context-dependent references.
- Define all acronyms and domain-specific terms.
- Include examples and edge cases where applicable.
- Ensure the document is self-contained and does not rely on external context.
The specification should be saved in the [/spec/](/spec/) directory and named according to the following convention: `spec-[a-z0-9-]+.md`, where the name should be descriptive of the specification's content and starting with the highlevel purpose, which is one of [schema, tool, data, infrastructure, process, architecture, or design].
The specification file must be formatted in well formed Markdown.
Specification files must follow the template below, ensuring that all sections are filled out appropriately. The front matter for the markdown should be structured correctly as per the example following:
```md
---
title: [Concise Title Describing the Specification's Focus]
version: [Optional: e.g., 1.0, Date]
date_created: [YYYY-MM-DD]
last_updated: [Optional: YYYY-MM-DD]
owner: [Optional: Team/Individual responsible for this spec]
tags: [Optional: List of relevant tags or categories, e.g., `infrastructure`, `process`, `design`, `app` etc]
---
# Introduction
[A short concise introduction to the specification and the goal it is intended to achieve.]
## 1. Purpose & Scope
[Provide a clear, concise description of the specification's purpose and the scope of its application. State the intended audience and any assumptions.]
## 2. Definitions
[List and define all acronyms, abbreviations, and domain-specific terms used in this specification.]
## 3. Requirements, Constraints & Guidelines
[Explicitly list all requirements, constraints, rules, and guidelines. Use bullet points or tables for clarity.]
- **REQ-001**: Requirement 1
- **SEC-001**: Security Requirement 1
- **[3 LETTERS]-001**: Other Requirement 1
- **CON-001**: Constraint 1
- **GUD-001**: Guideline 1
- **PAT-001**: Pattern to follow 1
## 4. Interfaces & Data Contracts
[Describe the interfaces, APIs, data contracts, or integration points. Use tables or code blocks for schemas and examples.]
## 5. Acceptance Criteria
[Define clear, testable acceptance criteria for each requirement using Given-When-Then format where appropriate.]
- **AC-001**: Given [context], When [action], Then [expected outcome]
- **AC-002**: The system shall [specific behavior] when [condition]
- **AC-003**: [Additional acceptance criteria as needed]
## 6. Test Automation Strategy
[Define the testing approach, frameworks, and automation requirements.]
- **Test Levels**: Unit, Integration, End-to-End
- **Frameworks**: MSTest, FluentAssertions, Moq (for .NET applications)
- **Test Data Management**: [approach for test data creation and cleanup]
- **CI/CD Integration**: [automated testing in GitHub Actions pipelines]
- **Coverage Requirements**: [minimum code coverage thresholds]
- **Performance Testing**: [approach for load and performance testing]
## 7. Rationale & Context
[Explain the reasoning behind the requirements, constraints, and guidelines. Provide context for design decisions.]
## 8. Dependencies & External Integrations
[Define the external systems, services, and architectural dependencies required for this specification. Focus on **what** is needed rather than **how** it's implemented. Avoid specific package or library versions unless they represent architectural constraints.]
### External Systems
- **EXT-001**: [External system name] - [Purpose and integration type]
### Third-Party Services
- **SVC-001**: [Service name] - [Required capabilities and SLA requirements]
### Infrastructure Dependencies
- **INF-001**: [Infrastructure component] - [Requirements and constraints]
### Data Dependencies
- **DAT-001**: [External data source] - [Format, frequency, and access requirements]
### Technology Platform Dependencies
- **PLT-001**: [Platform/runtime requirement] - [Version constraints and rationale]
### Compliance Dependencies
- **COM-001**: [Regulatory or compliance requirement] - [Impact on implementation]
**Note**: This section should focus on architectural and business dependencies, not specific package implementations. For example, specify "OAuth 2.0 authentication library" rather than "Microsoft.AspNetCore.Authentication.JwtBearer v6.0.1".
## 9. Examples & Edge Cases
```code
// Code snippet or data example demonstrating the correct application of the guidelines, including edge cases
```
## 10. Validation Criteria
[List the criteria or tests that must be satisfied for compliance with this specification.]
## 11. Related Specifications / Further Reading
[Link to related spec 1]
[Link to relevant external documentation]
```

50
.github/prompts/csharp-async.prompt.md vendored Normal file
View File

@@ -0,0 +1,50 @@
---
mode: 'agent'
tools: ['changes', 'search/codebase', 'edit/editFiles', 'problems']
description: 'Get best practices for C# async programming'
---
# C# Async Programming Best Practices
Your goal is to help me follow best practices for asynchronous programming in C#.
## Naming Conventions
- Use the 'Async' suffix for all async methods
- Match method names with their synchronous counterparts when applicable (e.g., `GetDataAsync()` for `GetData()`)
## Return Types
- Return `Task<T>` when the method returns a value
- Return `Task` when the method doesn't return a value
- Consider `ValueTask<T>` for high-performance scenarios to reduce allocations
- Avoid returning `void` for async methods except for event handlers
## Exception Handling
- Use try/catch blocks around await expressions
- Avoid swallowing exceptions in async methods
- Use `ConfigureAwait(false)` when appropriate to prevent deadlocks in library code
- Propagate exceptions with `Task.FromException()` instead of throwing in async Task returning methods
## Performance
- Use `Task.WhenAll()` for parallel execution of multiple tasks
- Use `Task.WhenAny()` for implementing timeouts or taking the first completed task
- Avoid unnecessary async/await when simply passing through task results
- Consider cancellation tokens for long-running operations
## Common Pitfalls
- Never use `.Wait()`, `.Result`, or `.GetAwaiter().GetResult()` in async code
- Avoid mixing blocking and async code
- Don't create async void methods (except for event handlers)
- Always await Task-returning methods
## Implementation Patterns
- Implement the async command pattern for long-running operations
- Use async streams (IAsyncEnumerable<T>) for processing sequences asynchronously
- Consider the task-based asynchronous pattern (TAP) for public APIs
When reviewing my C# code, identify these issues and suggest improvements that follow these best practices.

69
.github/prompts/csharp-xunit.prompt.md vendored Normal file
View File

@@ -0,0 +1,69 @@
---
mode: 'agent'
tools: ['changes', 'search/codebase', 'edit/editFiles', 'problems', 'search']
description: 'Get best practices for XUnit unit testing, including data-driven tests'
---
# XUnit Best Practices
Your goal is to help me write effective unit tests with XUnit, covering both standard and data-driven testing approaches.
## Project Setup
- Use a separate test project with naming convention `[ProjectName].Tests`
- Reference Microsoft.NET.Test.Sdk, xunit, and xunit.runner.visualstudio packages
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
- Use .NET SDK test commands: `dotnet test` for running tests
## Test Structure
- No test class attributes required (unlike MSTest/NUnit)
- Use fact-based tests with `[Fact]` attribute for simple tests
- Follow the Arrange-Act-Assert (AAA) pattern
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
- Use constructor for setup and `IDisposable.Dispose()` for teardown
- Use `IClassFixture<T>` for shared context between tests in a class
- Use `ICollectionFixture<T>` for shared context between multiple test classes
## Standard Tests
- Keep tests focused on a single behavior
- Avoid testing multiple behaviors in one test method
- Use clear assertions that express intent
- Include only the assertions needed to verify the test case
- Make tests independent and idempotent (can run in any order)
- Avoid test interdependencies
## Data-Driven Tests
- Use `[Theory]` combined with data source attributes
- Use `[InlineData]` for inline test data
- Use `[MemberData]` for method-based test data
- Use `[ClassData]` for class-based test data
- Create custom data attributes by implementing `DataAttribute`
- Use meaningful parameter names in data-driven tests
## Assertions
- Use `Assert.Equal` for value equality
- Use `Assert.Same` for reference equality
- Use `Assert.True`/`Assert.False` for boolean conditions
- Use `Assert.Contains`/`Assert.DoesNotContain` for collections
- Use `Assert.Matches`/`Assert.DoesNotMatch` for regex pattern matching
- Use `Assert.Throws<T>` or `await Assert.ThrowsAsync<T>` to test exceptions
- Use fluent assertions library for more readable assertions
## Mocking and Isolation
- Consider using Moq or NSubstitute alongside XUnit
- Mock dependencies to isolate units under test
- Use interfaces to facilitate mocking
- Consider using a DI container for complex test setups
## Test Organization
- Group tests by feature or component
- Use `[Trait("Category", "CategoryName")]` for categorization
- Use collection fixtures to group tests with shared dependencies
- Consider output helpers (`ITestOutputHelper`) for test diagnostics
- Skip tests conditionally with `Skip = "reason"` in fact/theory attributes

View File

@@ -0,0 +1,84 @@
---
mode: 'agent'
description: 'Ensure .NET/C# code meets best practices for the solution/project.'
---
# .NET/C# Best Practices
Your task is to ensure .NET/C# code in ${selection} meets the best practices specific to this solution/project. This includes:
## Documentation & Structure
- Create comprehensive XML documentation comments for all public classes, interfaces, methods, and properties
- Include parameter descriptions and return value descriptions in XML comments
- Follow the established namespace structure: {Core|Console|App|Service}.{Feature}
## Design Patterns & Architecture
- Use primary constructor syntax for dependency injection (e.g., `public class MyClass(IDependency dependency)`)
- Implement the Command Handler pattern with generic base classes (e.g., `CommandHandler<TOptions>`)
- Use interface segregation with clear naming conventions (prefix interfaces with 'I')
- Follow the Factory pattern for complex object creation.
## Dependency Injection & Services
- Use constructor dependency injection with null checks via ArgumentNullException
- Register services with appropriate lifetimes (Singleton, Scoped, Transient)
- Use Microsoft.Extensions.DependencyInjection patterns
- Implement service interfaces for testability
## Resource Management & Localization
- Use ResourceManager for localized messages and error strings
- Separate LogMessages and ErrorMessages resource files
- Access resources via `_resourceManager.GetString("MessageKey")`
## Async/Await Patterns
- Use async/await for all I/O operations and long-running tasks
- Return Task or Task<T> from async methods
- Use ConfigureAwait(false) where appropriate
- Handle async exceptions properly
## Testing Standards
- Use MSTest framework with FluentAssertions for assertions
- Follow AAA pattern (Arrange, Act, Assert)
- Use Moq for mocking dependencies
- Test both success and failure scenarios
- Include null parameter validation tests
## Configuration & Settings
- Use strongly-typed configuration classes with data annotations
- Implement validation attributes (Required, NotEmptyOrWhitespace)
- Use IConfiguration binding for settings
- Support appsettings.json configuration files
## Semantic Kernel & AI Integration
- Use Microsoft.SemanticKernel for AI operations
- Implement proper kernel configuration and service registration
- Handle AI model settings (ChatCompletion, Embedding, etc.)
- Use structured output patterns for reliable AI responses
## Error Handling & Logging
- Use structured logging with Microsoft.Extensions.Logging
- Include scoped logging with meaningful context
- Throw specific exceptions with descriptive messages
- Use try-catch blocks for expected failure scenarios
## Performance & Security
- Use C# 12+ features and .NET 8 optimizations where applicable
- Implement proper input validation and sanitization
- Use parameterized queries for database operations
- Follow secure coding practices for AI/ML operations
## Code Quality
- Ensure SOLID principles compliance
- Avoid code duplication through base classes and utilities
- Use meaningful names that reflect domain concepts
- Keep methods focused and cohesive
- Implement proper disposal patterns for resources

View File

@@ -0,0 +1,41 @@
---
mode: 'agent'
description: 'Review the C#/.NET code for design pattern implementation and suggest improvements.'
---
# .NET/C# Design Pattern Review
Review the C#/.NET code in ${selection} for design pattern implementation and suggest improvements for the solution/project. Do not make any changes to the code, just provide a review.
## Required Design Patterns
- **Command Pattern**: Generic base classes (`CommandHandler<TOptions>`), `ICommandHandler<TOptions>` interface, `CommandHandlerOptions` inheritance, static `SetupCommand(IHost host)` methods
- **Factory Pattern**: Complex object creation service provider integration
- **Dependency Injection**: Primary constructor syntax, `ArgumentNullException` null checks, interface abstractions, proper service lifetimes
- **Repository Pattern**: Async data access interfaces provider abstractions for connections
- **Provider Pattern**: External service abstractions (database, AI), clear contracts, configuration handling
- **Resource Pattern**: ResourceManager for localized messages, separate .resx files (LogMessages, ErrorMessages)
## Review Checklist
- **Design Patterns**: Identify patterns used. Are Command Handler, Factory, Provider, and Repository patterns correctly implemented? Missing beneficial patterns?
- **Architecture**: Follow namespace conventions (`{Core|Console|App|Service}.{Feature}`)? Proper separation between Core/Console projects? Modular and readable?
- **.NET Best Practices**: Primary constructors, async/await with Task returns, ResourceManager usage, structured logging, strongly-typed configuration?
- **GoF Patterns**: Command, Factory, Template Method, Strategy patterns correctly implemented?
- **SOLID Principles**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion violations?
- **Performance**: Proper async/await, resource disposal, ConfigureAwait(false), parallel processing opportunities?
- **Maintainability**: Clear separation of concerns, consistent error handling, proper configuration usage?
- **Testability**: Dependencies abstracted via interfaces, mockable components, async testability, AAA pattern compatibility?
- **Security**: Input validation, secure credential handling, parameterized queries, safe exception handling?
- **Documentation**: XML docs for public APIs, parameter/return descriptions, resource file organization?
- **Code Clarity**: Meaningful names reflecting domain concepts, clear intent through patterns, self-explanatory structure?
- **Clean Code**: Consistent style, appropriate method/class size, minimal complexity, eliminated duplication?
## Improvement Focus Areas
- **Command Handlers**: Validation in base class, consistent error handling, proper resource management
- **Factories**: Dependency configuration, service provider integration, disposal patterns
- **Providers**: Connection management, async patterns, exception handling and logging
- **Configuration**: Data annotations, validation attributes, secure sensitive value handling
- **AI/ML Integration**: Semantic Kernel patterns, structured output handling, model configuration
Provide specific, actionable recommendations for improvements aligned with the project's architecture and .NET best practices.

View File

@@ -14,10 +14,10 @@ jobs:
os: [windows-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
dotnet-version: 8.0.x
- run: dotnet run --project build/build.csproj
- uses: actions/upload-artifact@v5
with:

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ tests/TestArchives/*/Scratch
tests/TestArchives/*/Scratch2
.vs
tools
.vscode
.idea/
.DS_Store

View File

@@ -1,9 +0,0 @@
{
"recommendations": [
"ms-dotnettools.csdevkit",
"ms-dotnettools.csharp",
"ms-dotnettools.vscode-dotnet-runtime",
"csharpier.csharpier-vscode",
"formulahendry.dotnet-test-explorer"
]
}

97
.vscode/launch.json vendored
View File

@@ -1,97 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Tests (net10.0)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "dotnet",
"args": [
"test",
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
"-f",
"net10.0",
"--no-build",
"--verbosity=normal"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Debug Specific Test (net10.0)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "dotnet",
"args": [
"test",
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
"-f",
"net10.0",
"--no-build",
"--filter",
"FullyQualifiedName~${input:testName}"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Debug Performance Tests",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "dotnet",
"args": [
"run",
"--project",
"${workspaceFolder}/tests/SharpCompress.Performance/SharpCompress.Performance.csproj",
"--no-build"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Debug Build Script",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"run",
"--project",
"${workspaceFolder}/build/build.csproj",
"--",
"${input:buildTarget}"
],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"stopAtEntry": false
}
],
"inputs": [
{
"id": "testName",
"type": "promptString",
"description": "Enter test name or pattern (e.g., TestMethodName or ClassName)",
"default": ""
},
{
"id": "buildTarget",
"type": "pickString",
"description": "Select build target",
"options": [
"clean",
"restore",
"build",
"test",
"format",
"publish",
"default"
],
"default": "build"
}
]
}

29
.vscode/settings.json vendored
View File

@@ -1,29 +0,0 @@
{
"dotnet.defaultSolution": "SharpCompress.sln",
"files.exclude": {
"**/bin": true,
"**/obj": true
},
"files.watcherExclude": {
"**/bin/**": true,
"**/obj/**": true,
"**/artifacts/**": true
},
"search.exclude": {
"**/bin": true,
"**/obj": true,
"**/artifacts": true
},
"editor.formatOnSave": false,
"[csharp]": {
"editor.defaultFormatter": "csharpier.csharpier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
}
},
"csharpier.enableDebugLogs": false,
"omnisharp.enableRoslynAnalyzers": true,
"omnisharp.enableEditorConfigSupport": true,
"dotnet-test-explorer.testProjectPath": "tests/**/*.csproj"
}

178
.vscode/tasks.json vendored
View File

@@ -1,178 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/SharpCompress.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "build-release",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/SharpCompress.sln",
"-c",
"Release",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile",
"group": "build"
},
{
"label": "build-library",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/SharpCompress/SharpCompress.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile",
"group": "build"
},
{
"label": "restore",
"command": "dotnet",
"type": "process",
"args": [
"restore",
"${workspaceFolder}/SharpCompress.sln"
],
"problemMatcher": "$msCompile"
},
{
"label": "clean",
"command": "dotnet",
"type": "process",
"args": [
"clean",
"${workspaceFolder}/SharpCompress.sln"
],
"problemMatcher": "$msCompile"
},
{
"label": "test",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
"--no-build",
"--verbosity=normal"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "test",
"isDefault": true
},
"dependsOn": "build"
},
{
"label": "test-net10",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
"-f",
"net10.0",
"--no-build",
"--verbosity=normal"
],
"problemMatcher": "$msCompile",
"group": "test",
"dependsOn": "build"
},
{
"label": "test-net48",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
"-f",
"net48",
"--no-build",
"--verbosity=normal"
],
"problemMatcher": "$msCompile",
"group": "test",
"dependsOn": "build"
},
{
"label": "format",
"command": "dotnet",
"type": "process",
"args": [
"csharpier",
"."
],
"problemMatcher": []
},
{
"label": "format-check",
"command": "dotnet",
"type": "process",
"args": [
"csharpier",
"check",
"."
],
"problemMatcher": []
},
{
"label": "run-build-script",
"command": "dotnet",
"type": "process",
"args": [
"run",
"--project",
"${workspaceFolder}/build/build.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "pack",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/src/SharpCompress/SharpCompress.csproj",
"-c",
"Release",
"-o",
"${workspaceFolder}/artifacts/"
],
"problemMatcher": "$msCompile",
"dependsOn": "build-release"
},
{
"label": "performance-tests",
"command": "dotnet",
"type": "process",
"args": [
"run",
"--project",
"${workspaceFolder}/tests/SharpCompress.Performance/SharpCompress.Performance.csproj",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -49,30 +49,6 @@ SharpCompress is a pure C# compression library supporting multiple archive forma
- Use `dotnet test` to run tests
- Solution file: `SharpCompress.sln`
### Directory Structure
```
src/SharpCompress/
├── Archives/ # IArchive implementations (Zip, Tar, Rar, 7Zip, GZip)
├── Readers/ # IReader implementations (forward-only)
├── Writers/ # IWriter implementations (forward-only)
├── Compressors/ # Low-level compression streams (BZip2, Deflate, LZMA, etc.)
├── Factories/ # Format detection and factory pattern
├── Common/ # Shared types (ArchiveType, Entry, Options)
├── Crypto/ # Encryption implementations
└── IO/ # Stream utilities and wrappers
tests/SharpCompress.Test/
├── Zip/, Tar/, Rar/, SevenZip/, GZip/, BZip2/ # Format-specific tests
├── TestBase.cs # Base test class with helper methods
└── TestArchives/ # Test data (not checked into main test project)
```
### Factory Pattern
All format types implement factory interfaces (`IArchiveFactory`, `IReaderFactory`, `IWriterFactory`) for auto-detection:
- `ReaderFactory.Open()` - Auto-detects format by probing stream
- `WriterFactory.Open()` - Creates writer for specified `ArchiveType`
- Factories located in: `src/SharpCompress/Factories/`
## Nullable Reference Types
- Declare variables non-nullable, and check for `null` at entry points.
@@ -140,18 +116,3 @@ SharpCompress supports multiple archive and compression formats:
- Use test archives from `tests/TestArchives` directory for consistency.
- Test stream disposal and `LeaveStreamOpen` behavior.
- Test edge cases: empty archives, large files, corrupted archives, encrypted archives.
### Test Organization
- Base class: `TestBase` - Provides `TEST_ARCHIVES_PATH`, `SCRATCH_FILES_PATH`, temp directory management
- Framework: xUnit with AwesomeAssertions
- Test archives: `tests/TestArchives/` - Use existing archives, don't create new ones unnecessarily
- Match naming style of nearby test files
## Common Pitfalls
1. **Don't mix Archive and Reader APIs** - Archive needs seekable stream, Reader doesn't
2. **Solid archives (Rar, 7Zip)** - Use `ExtractAllEntries()` for best performance, not individual entry extraction
3. **Stream disposal** - Always set `LeaveStreamOpen` explicitly when needed (default is to close)
4. **Tar + non-seekable stream** - Must provide file size or it will throw
5. **Multi-framework differences** - Some features differ between .NET Framework and modern .NET (e.g., Mono.Posix)
6. **Format detection** - Use `ReaderFactory.Open()` for auto-detection, test with actual archive files

View File

@@ -1,20 +1,20 @@
<Project>
<ItemGroup>
<PackageVersion Include="Bullseye" Version="6.0.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.2.1" />
<PackageVersion Include="Glob" Version="1.1.9" />
<PackageVersion Include="JetBrains.Profiler.SelfApi" Version="2.5.14" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageVersion Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageVersion Include="SimpleExec" Version="12.0.0" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="ZstdSharp.Port" Version="0.8.6" />
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="10.0.0" />
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="8.0.21" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
</ItemGroup>

View File

@@ -22,16 +22,11 @@
| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A |
1. SOLID Rars are only supported in the RarReader API.
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading. See [Zip Format Notes](#zip-format-notes) for details on multi-volume archives and streaming behavior.
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading.
3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown.
4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API
5. LZip has no support for extra data like the file name or timestamp. There is a default filename used when looking at the entry Key on the archive.
### Zip Format Notes
- Multi-volume/split ZIP archives require ZipArchive (seekable streams) as ZipReader cannot seek across volume files.
- ZipReader processes entries from LocalEntry headers (which include directory entries ending with `/`) and intentionally skips DirectoryEntry headers from the central directory, as they are redundant in streaming mode - all entry data comes from LocalEntry headers which ZipReader has already processed.
## Compression Streams
For those who want to directly compress/decompress bits. The single file formats are represented here as well. However, BZip2, LZip and XZ have no metadata (GZip has a little) so using them without something like a Tar file makes little sense.

View File

@@ -1,6 +1,6 @@
# SharpCompress
SharpCompress is a compression library in pure C# for .NET Framework 4.8, .NET 8.0 and .NET 10.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip, unzstd, unarc and unarj with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented.
SharpCompress is a compression library in pure C# for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0 and NET 8.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip, unzstd, unarc and unarj with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented.
The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream).

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bullseye" />

View File

@@ -1,7 +1,7 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"net8.0": {
"Bullseye": {
"type": "Direct",
"requested": "[6.0.0, )",

View File

@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.100",
"version": "8.0.100",
"rollForward": "latestFeature"
}
}

View File

@@ -161,11 +161,6 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
/// </summary>
public virtual bool IsSolid => false;
/// <summary>
/// Archive is ENCRYPTED (this means the Archive has password-protected files).
/// </summary>
public virtual bool IsEncrypted => false;
/// <summary>
/// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
/// </summary>

View File

@@ -3,10 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Factories;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -135,10 +131,10 @@ public static class ArchiveFactory
{
finfo.NotNull(nameof(finfo));
using Stream stream = finfo.OpenRead();
return FindFactory<T>(stream, finfo.Name);
return FindFactory<T>(stream);
}
private static T FindFactory<T>(Stream stream, string? fileName = null)
private static T FindFactory<T>(Stream stream)
where T : IFactory
{
stream.NotNull(nameof(stream));
@@ -163,16 +159,6 @@ public static class ArchiveFactory
}
}
stream.Seek(startPosition, SeekOrigin.Begin);
// Check if this is a compressed tar file (tar.bz2, tar.lz, etc.)
// These formats are supported by ReaderFactory but not by ArchiveFactory
var compressedTarMessage = TryGetCompressedTarMessage(stream, fileName);
if (compressedTarMessage != null)
{
throw new InvalidOperationException(compressedTarMessage);
}
var extensions = string.Join(", ", factories.Select(item => item.Name));
throw new InvalidOperationException(
@@ -262,111 +248,4 @@ public static class ArchiveFactory
}
public static IArchiveFactory AutoFactory { get; } = new AutoArchiveFactory();
/// <summary>
/// Checks if the stream is a compressed tar file (tar.bz2, tar.lz, etc.) that should use ReaderFactory instead.
/// Returns an error message if detected, null otherwise.
/// </summary>
private static string? TryGetCompressedTarMessage(Stream stream, string? fileName)
{
var startPosition = stream.Position;
try
{
// Check if it's a BZip2 file
if (BZip2Stream.IsBZip2(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin);
// Try to decompress and check if it contains a tar archive
using var decompressed = new BZip2Stream(stream, CompressionMode.Decompress, true);
if (IsTarStream(decompressed))
{
return "This appears to be a tar.bz2 archive. The Archive API requires seekable streams, but decompression streams are not seekable. "
+ "Please use ReaderFactory.Open() instead for forward-only extraction, "
+ "or decompress the file first and then open the resulting tar file with ArchiveFactory.Open().";
}
return null;
}
stream.Seek(startPosition, SeekOrigin.Begin);
// Check if it's an LZip file
if (LZipStream.IsLZipFile(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin);
// Try to decompress and check if it contains a tar archive
using var decompressed = new LZipStream(stream, CompressionMode.Decompress);
if (IsTarStream(decompressed))
{
return "This appears to be a tar.lz archive. The Archive API requires seekable streams, but decompression streams are not seekable. "
+ "Please use ReaderFactory.Open() instead for forward-only extraction, "
+ "or decompress the file first and then open the resulting tar file with ArchiveFactory.Open().";
}
return null;
}
// Check file extension as a fallback for other compressed tar formats
if (fileName != null)
{
var lowerFileName = fileName.ToLowerInvariant();
if (
lowerFileName.EndsWith(".tar.bz2")
|| lowerFileName.EndsWith(".tbz")
|| lowerFileName.EndsWith(".tbz2")
|| lowerFileName.EndsWith(".tb2")
|| lowerFileName.EndsWith(".tz2")
|| lowerFileName.EndsWith(".tar.lz")
|| lowerFileName.EndsWith(".tar.xz")
|| lowerFileName.EndsWith(".txz")
|| lowerFileName.EndsWith(".tar.zst")
|| lowerFileName.EndsWith(".tar.zstd")
|| lowerFileName.EndsWith(".tzst")
|| lowerFileName.EndsWith(".tzstd")
|| lowerFileName.EndsWith(".tar.z")
|| lowerFileName.EndsWith(".tz")
|| lowerFileName.EndsWith(".taz")
)
{
return $"The file '{fileName}' appears to be a compressed tar archive. The Archive API requires seekable streams, but decompression streams are not seekable. "
+ "Please use ReaderFactory.Open() instead for forward-only extraction, "
+ "or decompress the file first and then open the resulting tar file with ArchiveFactory.Open().";
}
}
return null;
}
catch
{
// If we can't determine, just return null and let the normal error handling proceed
return null;
}
finally
{
try
{
stream.Seek(startPosition, SeekOrigin.Begin);
}
catch
{
// Ignore seek failures
}
}
}
/// <summary>
/// Checks if a stream contains a tar archive by trying to read a tar header.
/// </summary>
private static bool IsTarStream(Stream stream)
{
try
{
var tarHeader = new TarHeader(new ArchiveEncoding());
return tarHeader.Read(new BinaryReader(stream));
}
catch
{
return false;
}
}
}

View File

@@ -128,7 +128,6 @@ public static class IArchiveEntryExtensions
{
using var fs = File.Open(destinationFileName, fm);
await entry.WriteToAsync(fs, cancellationToken).ConfigureAwait(false);
},
cancellationToken
}
);
}

View File

@@ -84,8 +84,6 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
public override bool IsSolid => Volumes.First().IsSolidArchive;
public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted;
public virtual int MinVersion => Volumes.First().MinVersion;
public virtual int MaxVersion => Volumes.First().MaxVersion;

View File

@@ -205,8 +205,6 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
.GroupBy(x => x.FilePart.Folder)
.Any(folder => folder.Count() > 1);
public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted;
public override long TotalSize =>
_database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0;

View File

@@ -57,7 +57,7 @@ namespace SharpCompress.Common.Arc
return value switch
{
1 or 2 => CompressionType.None,
3 => CompressionType.Packed,
3 => CompressionType.RLE90,
4 => CompressionType.Squeezed,
5 or 6 or 7 or 8 => CompressionType.Crunched,
9 => CompressionType.Squashed,

View File

@@ -44,7 +44,7 @@ namespace SharpCompress.Common.Arc
Header.CompressedSize
);
break;
case CompressionType.Packed:
case CompressionType.RLE90:
compressedStream = new RunLength90Stream(
_stream,
(int)Header.CompressedSize
@@ -54,14 +54,6 @@ namespace SharpCompress.Common.Arc
compressedStream = new SqueezeStream(_stream, (int)Header.CompressedSize);
break;
case CompressionType.Crunched:
if (Header.OriginalSize > 128 * 1024)
{
throw new NotSupportedException(
"CompressionMethod: "
+ Header.CompressionMethod
+ " with size > 128KB"
);
}
compressedStream = new ArcLzwStream(
_stream,
(int)Header.CompressedSize,

View File

@@ -41,7 +41,7 @@ namespace SharpCompress.Common.Arj
case CompressionMethod.CompressedMost:
case CompressionMethod.Compressed:
case CompressionMethod.CompressedFaster:
if (Header.OriginalSize > 128 * 1024)
if (Header.CompressedSize > 128 * 1024)
{
throw new NotSupportedException(
"CompressionMethod: "

View File

@@ -23,7 +23,7 @@ public enum CompressionType
Reduce4,
Explode,
Squeezed,
Packed,
RLE90,
Crunched,
Squashed,
Crushed,

View File

@@ -1,7 +1,6 @@
using System;
using System.Buffers.Binary;
using System.Security.Cryptography;
using System.Text;
namespace SharpCompress.Common.Zip;
@@ -20,24 +19,8 @@ internal class WinzipAesEncryptionData
{
_keySize = keySize;
#if NETFRAMEWORK
#if NETFRAMEWORK || NETSTANDARD2_0
var rfc2898 = new Rfc2898DeriveBytes(password, salt, RFC2898_ITERATIONS);
KeyBytes = rfc2898.GetBytes(KeySizeInBytes);
IvBytes = rfc2898.GetBytes(KeySizeInBytes);
var generatedVerifyValue = rfc2898.GetBytes(2);
#elif NET10_0_OR_GREATER
var derivedKeySize = (KeySizeInBytes * 2) + 2;
var passwordBytes = Encoding.UTF8.GetBytes(password);
var derivedKey = Rfc2898DeriveBytes.Pbkdf2(
passwordBytes,
salt,
RFC2898_ITERATIONS,
HashAlgorithmName.SHA1,
derivedKeySize
);
KeyBytes = derivedKey.AsSpan(0, KeySizeInBytes).ToArray();
IvBytes = derivedKey.AsSpan(KeySizeInBytes, KeySizeInBytes).ToArray();
var generatedVerifyValue = derivedKey.AsSpan((KeySizeInBytes * 2), 2).ToArray();
#else
var rfc2898 = new Rfc2898DeriveBytes(
password,
@@ -45,10 +28,11 @@ internal class WinzipAesEncryptionData
RFC2898_ITERATIONS,
HashAlgorithmName.SHA1
);
KeyBytes = rfc2898.GetBytes(KeySizeInBytes);
#endif
KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ???
IvBytes = rfc2898.GetBytes(KeySizeInBytes);
var generatedVerifyValue = rfc2898.GetBytes(2);
#endif
var verify = BinaryPrimitives.ReadInt16LittleEndian(passwordVerifyValue);
var generated = BinaryPrimitives.ReadInt16LittleEndian(generatedVerifyValue);

View File

@@ -544,12 +544,6 @@ internal sealed class CBZip2OutputStream : Stream, IStreamStack
private void EndBlock()
{
// Skip block processing for empty input (no data written)
if (last < 0)
{
return;
}
blockCRC = mCrc.GetFinalCRC();
combinedCRC = (combinedCRC << 1) | (int)(((uint)combinedCRC) >> 31);
combinedCRC ^= blockCRC;

View File

@@ -57,238 +57,19 @@ public class TarFactory
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
)
{
if (!stream.CanSeek)
{
return TarArchive.IsTarFile(stream); // For non-seekable streams, just check if it's a tar file
}
var startPosition = stream.Position;
// First check if it's a regular tar file
if (TarArchive.IsTarFile(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin); // Seek back for consistency
return true;
}
// Seek back after the tar file check
stream.Seek(startPosition, SeekOrigin.Begin);
if (compressionOptions == null)
{
return false;
}
try
{
// Try each compression option to see if it contains a tar file
foreach (var testOption in compressionOptions)
{
if (testOption.Type == CompressionType.None)
{
continue; // Skip uncompressed
}
stream.Seek(startPosition, SeekOrigin.Begin);
try
{
if (testOption.CanHandle(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin);
// Try to decompress and check if it contains a tar archive
// For compression formats that don't support leaveOpen, we need to save/restore position
var positionBeforeDecompress = stream.Position;
Stream? decompressedStream = null;
bool streamWasClosed = false;
try
{
decompressedStream = testOption.Type switch
{
CompressionType.BZip2 => new BZip2Stream(stream, CompressionMode.Decompress, true),
_ => testOption.CreateStream(stream) // For other types, may close the stream
};
if (TarArchive.IsTarFile(decompressedStream))
{
return true;
}
}
catch (ObjectDisposedException)
{
streamWasClosed = true;
throw; // Stream was closed, can't continue
}
finally
{
decompressedStream?.Dispose();
if (!streamWasClosed && stream.CanSeek)
{
try
{
stream.Seek(positionBeforeDecompress, SeekOrigin.Begin);
}
catch
{
// If seek fails, the stream might have been closed
}
}
}
// Seek back to start after decompression attempt
stream.Seek(startPosition, SeekOrigin.Begin);
}
}
catch
{
// If decompression fails, it's not this format - continue to next option
try
{
stream.Seek(startPosition, SeekOrigin.Begin);
}
catch
{
// Ignore seek failures
}
}
}
return false;
}
finally
{
try
{
stream.Seek(startPosition, SeekOrigin.Begin);
}
catch
{
// Ignore seek failures
}
}
}
) => TarArchive.IsTarFile(stream);
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
readerOptions ??= new ReaderOptions();
// Try to detect and handle compressed tar formats
if (stream.CanSeek)
{
var startPosition = stream.Position;
// Try each compression option to see if we can decompress it
foreach (var testOption in compressionOptions)
{
if (testOption.Type == CompressionType.None)
{
continue; // Skip uncompressed
}
stream.Seek(startPosition, SeekOrigin.Begin);
if (testOption.CanHandle(stream))
{
stream.Seek(startPosition, SeekOrigin.Begin);
// Decompress the entire stream into a seekable MemoryStream
using var decompressedStream = testOption.CreateStream(stream);
var memoryStream = new MemoryStream();
decompressedStream.CopyTo(memoryStream);
memoryStream.Position = 0;
// Verify it's actually a tar file
if (TarArchive.IsTarFile(memoryStream))
{
memoryStream.Position = 0;
// Return a TarArchive from the decompressed memory stream
// The TarArchive will own the MemoryStream and dispose it when disposed
var options = new ReaderOptions
{
LeaveStreamOpen = false, // Ensure the MemoryStream is disposed with the archive
ArchiveEncoding = readerOptions?.ArchiveEncoding ?? new ArchiveEncoding()
};
return TarArchive.Open(memoryStream, options);
}
memoryStream.Dispose();
}
}
stream.Seek(startPosition, SeekOrigin.Begin);
}
// Fall back to normal tar archive opening
return TarArchive.Open(stream, readerOptions);
}
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
TarArchive.Open(stream, readerOptions);
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
readerOptions ??= new ReaderOptions();
// Try to detect and handle compressed tar formats by file extension and content
using var fileStream = fileInfo.OpenRead();
// Try each compression option
foreach (var testOption in compressionOptions)
{
if (testOption.Type == CompressionType.None)
{
continue; // Skip uncompressed
}
// Check if file extension matches
var fileName = fileInfo.Name.ToLowerInvariant();
if (testOption.KnownExtensions.Any(ext => fileName.EndsWith(ext)))
{
fileStream.Position = 0;
// Verify it's the right compression format
if (testOption.CanHandle(fileStream))
{
fileStream.Position = 0;
// Decompress the entire file into a seekable MemoryStream
using var decompressedStream = testOption.CreateStream(fileStream);
var memoryStream = new MemoryStream();
decompressedStream.CopyTo(memoryStream);
memoryStream.Position = 0;
// Verify it's actually a tar file
if (TarArchive.IsTarFile(memoryStream))
{
memoryStream.Position = 0;
// Return a TarArchive from the decompressed memory stream
// The TarArchive will own the MemoryStream and dispose it when disposed
var options = new ReaderOptions
{
LeaveStreamOpen = false, // Ensure the MemoryStream is disposed with the archive
ArchiveEncoding = readerOptions?.ArchiveEncoding ?? new ArchiveEncoding()
};
return TarArchive.Open(memoryStream, options);
}
memoryStream.Dispose();
}
}
}
// fileStream will be closed by the using statement
// Fall back to normal tar archive opening
return TarArchive.Open(fileInfo, readerOptions);
}
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
TarArchive.Open(fileInfo, readerOptions);
#endregion

View File

@@ -75,14 +75,6 @@ public class ZipReader : AbstractReader<ZipEntry, ZipVolume>
);
}
break;
case ZipHeaderType.DirectoryEntry:
// DirectoryEntry headers in the central directory are intentionally skipped.
// In streaming mode, we can only read forward, and DirectoryEntry headers
// reference LocalEntry headers that have already been processed. The file
// data comes from LocalEntry headers, not DirectoryEntry headers.
// For multi-volume ZIPs where file data spans multiple files, use ZipArchive
// instead, which requires seekable streams.
break;
case ZipHeaderType.DirectoryEnd:
{
yield break;

View File

@@ -2,11 +2,11 @@
<PropertyGroup>
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.42.0</VersionPrefix>
<AssemblyVersion>0.42.0</AssemblyVersion>
<FileVersion>0.42.0</FileVersion>
<VersionPrefix>0.41.0</VersionPrefix>
<AssemblyVersion>0.41.0</AssemblyVersion>
<FileVersion>0.41.0</FileVersion>
<Authors>Adam Hathcock</Authors>
<TargetFrameworks>net48;net8.0;net10.0</TargetFrameworks>
<TargetFrameworks>net48;net481;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<AssemblyName>SharpCompress</AssemblyName>
<AssemblyOriginatorKeyFile>../../SharpCompress.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
@@ -17,7 +17,7 @@
<Copyright>Copyright (c) 2025 Adam Hathcock</Copyright>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Description>SharpCompress is a compression library for NET 4.8/NET 8.0/NET 10.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.</Description>
<Description>SharpCompress is a compression library for NET Standard 2.0/NET 4.8/NET 4.8.1/NET 6.0/NET 8.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.</Description>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<DebugType>embedded</DebugType>
@@ -28,29 +28,31 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net10.0' ">
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ZstdSharp.Port" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net10.0' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.NET.ILLink.Tasks" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' Or '$(TargetFramework)' == 'net481' ">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="System.Text.Encoding.CodePages" />
<PackageReference Include="System.Buffers" />
<PackageReference Include="System.Memory" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="System.Text.Encoding.CodePages" />
<PackageReference Include="System.Memory" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

View File

@@ -48,29 +48,7 @@ internal class ZipCentralDirectoryEntry
var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed;
var headeroffsetvalue = zip64 ? uint.MaxValue : (uint)HeaderOffset;
var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0;
// Determine version needed to extract:
// - Version 63 for LZMA, PPMd, BZip2, ZStandard (advanced compression methods)
// - Version 45 for Zip64 extensions (when Zip64HeaderOffset != 0 or actual sizes require it)
// - Version 20 for standard Deflate/None compression
byte version;
if (
compression == ZipCompressionMethod.LZMA
|| compression == ZipCompressionMethod.PPMd
|| compression == ZipCompressionMethod.BZip2
|| compression == ZipCompressionMethod.ZStandard
)
{
version = 63;
}
else if (zip64 || Zip64HeaderOffset != 0)
{
version = 45;
}
else
{
version = 20;
}
var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption
var flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8)
? HeaderFlags.Efs

View File

@@ -4,11 +4,11 @@
".NETFramework,Version=v4.8": {
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Direct",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.6.3"
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
@@ -49,13 +49,12 @@
},
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "QLP54mIATaBpjGlsZIxga38VPk1G9js0Kw651B+bvrXi2kSgGZYrxJSpM3whhTZCBK4HEBHX3fzfDQMw7CXHGQ==",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
"dependencies": {
"System.Memory": "4.6.3",
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
"System.ValueTuple": "4.6.1"
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"ZstdSharp.Port": {
@@ -96,25 +95,216 @@
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.6.3",
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
}
},
".NETFramework,Version=v4.8.1": {
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.0.3, )",
"resolved": "1.0.3",
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
}
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"System.Buffers": {
"type": "Direct",
"requested": "[4.6.1, )",
"resolved": "4.6.1",
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
},
"System.Memory": {
"type": "Direct",
"requested": "[4.6.3, )",
"resolved": "4.6.3",
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
"dependencies": {
"System.Buffers": "4.6.1",
"System.Numerics.Vectors": "4.6.1",
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
}
},
"System.ValueTuple": {
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"ZstdSharp.Port": {
"type": "Direct",
"requested": "[0.8.6, )",
"resolved": "0.8.6",
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.NETFramework.ReferenceAssemblies.net481": {
"type": "Transitive",
"resolved": "1.0.3",
"contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.6.1",
"contentHash": "+RJT4qaekpZ7DDLhf+LTjq+E48jieKiY9ulJ+BoxKmZblIJfIJT8Ufcaa/clQqnYvWs8jugfGSMu8ylS0caG0w=="
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.1.2",
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
}
},
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
".NETStandard,Version=v2.0": {
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Direct",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"NETStandard.Library": {
"type": "Direct",
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"System.Memory": {
"type": "Direct",
"requested": "[4.6.3, )",
"resolved": "4.6.3",
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
"dependencies": {
"System.Buffers": "4.6.1",
"System.Numerics.Vectors": "4.6.1",
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
}
},
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"ZstdSharp.Port": {
"type": "Direct",
"requested": "[0.8.6, )",
"resolved": "0.8.6",
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.6.1",
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.1.2",
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
},
"System.Buffers": {
"type": "CentralTransitive",
"requested": "[4.6.1, )",
"resolved": "4.6.1",
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
}
},
"net6.0": {
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
@@ -145,9 +335,9 @@
"net8.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
"requested": "[8.0.21, )",
"resolved": "8.0.21",
"contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Profiler.SelfApi" />

View File

@@ -1,7 +1,7 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"net8.0": {
"JetBrains.Profiler.SelfApi": {
"type": "Direct",
"requested": "[2.5.14, )",

View File

@@ -27,22 +27,5 @@ namespace SharpCompress.Test.Arc
[Fact]
public void Arc_Crunched_Read() => Read("Arc.crunched.arc");
[Theory]
[InlineData("Arc.crunched.largefile.arc", CompressionType.Crunched)]
public void Arc_LargeFile_ShouldThrow(string fileName, CompressionType compressionType)
{
var exception = Assert.Throws<NotSupportedException>(() =>
ReadForBufferBoundaryCheck(fileName, compressionType)
);
}
[Theory]
[InlineData("Arc.uncompressed.largefile.arc", CompressionType.None)]
[InlineData("Arc.squeezed.largefile.arc", CompressionType.Squeezed)]
public void Arc_LargeFileTest_Read(string fileName, CompressionType compressionType)
{
ReadForBufferBoundaryCheck(fileName, compressionType);
}
}
}

View File

@@ -1,63 +0,0 @@
using System;
using System.IO;
using SharpCompress.Archives;
using Xunit;
namespace SharpCompress.Test;
public class ArchiveFactoryCompressedTarTests : TestBase
{
[Fact]
public void ArchiveFactory_Open_TarBz2_ThrowsHelpfulException()
{
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2");
var exception = Assert.Throws<InvalidOperationException>(() =>
{
using var archive = ArchiveFactory.Open(testFile);
});
Assert.Contains("tar.bz2", exception.Message);
Assert.Contains("ReaderFactory", exception.Message);
}
[Fact]
public void ArchiveFactory_Open_TarLz_ThrowsHelpfulException()
{
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.lz");
var exception = Assert.Throws<InvalidOperationException>(() =>
{
using var archive = ArchiveFactory.Open(testFile);
});
Assert.Contains("tar.lz", exception.Message);
Assert.Contains("ReaderFactory", exception.Message);
}
[Fact]
public void ArchiveFactory_Open_TarBz2Stream_ThrowsHelpfulException()
{
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2");
using var stream = File.OpenRead(testFile);
var exception = Assert.Throws<InvalidOperationException>(() =>
{
using var archive = ArchiveFactory.Open(stream);
});
Assert.Contains("tar.bz2", exception.Message);
Assert.Contains("ReaderFactory", exception.Message);
}
[Fact]
public void ArchiveFactory_Open_TarLzStream_ThrowsHelpfulException()
{
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.lz");
using var stream = File.OpenRead(testFile);
var exception = Assert.Throws<InvalidOperationException>(() =>
{
using var archive = ArchiveFactory.Open(stream);
});
Assert.Contains("tar.lz", exception.Message);
Assert.Contains("ReaderFactory", exception.Message);
}
}

View File

@@ -45,14 +45,16 @@ namespace SharpCompress.Test.Arj
public void Arj_Multi_Reader()
{
var exception = Assert.Throws<MultiVolumeExtractionException>(() =>
DoArj_Multi_Reader([
"Arj.store.split.arj",
"Arj.store.split.a01",
"Arj.store.split.a02",
"Arj.store.split.a03",
"Arj.store.split.a04",
"Arj.store.split.a05",
])
DoArj_Multi_Reader(
[
"Arj.store.split.arj",
"Arj.store.split.a01",
"Arj.store.split.a02",
"Arj.store.split.a03",
"Arj.store.split.a04",
"Arj.store.split.a05",
]
)
);
}
@@ -63,7 +65,7 @@ namespace SharpCompress.Test.Arj
public void Arj_LargeFile_ShouldThrow(string fileName, CompressionType compressionType)
{
var exception = Assert.Throws<NotSupportedException>(() =>
ReadForBufferBoundaryCheck(fileName, compressionType)
Arj_LargeFileTest_Read(fileName, compressionType)
);
}
@@ -72,7 +74,24 @@ namespace SharpCompress.Test.Arj
[InlineData("Arj.method4.largefile.arj", CompressionType.ArjLZ77)]
public void Arj_LargeFileTest_Read(string fileName, CompressionType compressionType)
{
ReadForBufferBoundaryCheck(fileName, compressionType);
using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, fileName)))
using (
var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true })
)
{
while (reader.MoveToNextEntry())
{
Assert.Equal(compressionType, reader.Entry.CompressionType);
reader.WriteEntryToDirectory(
SCRATCH_FILES_PATH,
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
);
}
}
CompareFilesByPath(
Path.Combine(SCRATCH_FILES_PATH, "news.txt"),
Path.Combine(MISC_TEST_FILES_PATH, "news.txt")
);
}
private void DoArj_Multi_Reader(string[] archives)

View File

@@ -633,13 +633,4 @@ public class RarArchiveTests : ArchiveTests
"Rar5.encrypted_filesOnly.rar",
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe"
);
[Fact]
public void Rar_TestEncryptedDetection()
{
using var passwordProtectedFilesArchive = RarArchive.Open(
Path.Combine(TEST_ARCHIVES_PATH, "Rar.encrypted_filesOnly.rar")
);
Assert.True(passwordProtectedFilesArchive.IsEncrypted);
}
}

View File

@@ -15,25 +15,29 @@ public class RarReaderAsyncTests : ReaderTests
{
[Fact]
public async Task Rar_Multi_Reader_Async() =>
await DoRar_Multi_Reader_Async([
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]);
await DoRar_Multi_Reader_Async(
[
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]
);
[Fact]
public async Task Rar5_Multi_Reader_Async() =>
await DoRar_Multi_Reader_Async([
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]);
await DoRar_Multi_Reader_Async(
[
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]
);
private async Task DoRar_Multi_Reader_Async(string[] archives)
{
@@ -91,25 +95,29 @@ public class RarReaderAsyncTests : ReaderTests
[Fact]
public async Task Rar_Multi_Reader_Delete_Files_Async() =>
await DoRar_Multi_Reader_Delete_Files_Async([
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]);
await DoRar_Multi_Reader_Delete_Files_Async(
[
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]
);
[Fact]
public async Task Rar5_Multi_Reader_Delete_Files_Async() =>
await DoRar_Multi_Reader_Delete_Files_Async([
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]);
await DoRar_Multi_Reader_Delete_Files_Async(
[
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]
);
private async Task DoRar_Multi_Reader_Delete_Files_Async(string[] archives)
{

View File

@@ -14,25 +14,29 @@ public class RarReaderTests : ReaderTests
{
[Fact]
public void Rar_Multi_Reader() =>
DoRar_Multi_Reader([
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]);
DoRar_Multi_Reader(
[
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]
);
[Fact]
public void Rar5_Multi_Reader() =>
DoRar_Multi_Reader([
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]);
DoRar_Multi_Reader(
[
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]
);
private void DoRar_Multi_Reader(string[] archives)
{
@@ -57,14 +61,16 @@ public class RarReaderTests : ReaderTests
[Fact]
public void Rar_Multi_Reader_Encrypted() =>
DoRar_Multi_Reader_Encrypted([
"Rar.EncryptedParts.part01.rar",
"Rar.EncryptedParts.part02.rar",
"Rar.EncryptedParts.part03.rar",
"Rar.EncryptedParts.part04.rar",
"Rar.EncryptedParts.part05.rar",
"Rar.EncryptedParts.part06.rar",
]);
DoRar_Multi_Reader_Encrypted(
[
"Rar.EncryptedParts.part01.rar",
"Rar.EncryptedParts.part02.rar",
"Rar.EncryptedParts.part03.rar",
"Rar.EncryptedParts.part04.rar",
"Rar.EncryptedParts.part05.rar",
"Rar.EncryptedParts.part06.rar",
]
);
private void DoRar_Multi_Reader_Encrypted(string[] archives) =>
Assert.Throws<InvalidFormatException>(() =>
@@ -91,25 +97,29 @@ public class RarReaderTests : ReaderTests
[Fact]
public void Rar_Multi_Reader_Delete_Files() =>
DoRar_Multi_Reader_Delete_Files([
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]);
DoRar_Multi_Reader_Delete_Files(
[
"Rar.multi.part01.rar",
"Rar.multi.part02.rar",
"Rar.multi.part03.rar",
"Rar.multi.part04.rar",
"Rar.multi.part05.rar",
"Rar.multi.part06.rar",
]
);
[Fact]
public void Rar5_Multi_Reader_Delete_Files() =>
DoRar_Multi_Reader_Delete_Files([
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]);
DoRar_Multi_Reader_Delete_Files(
[
"Rar5.multi.part01.rar",
"Rar5.multi.part02.rar",
"Rar5.multi.part03.rar",
"Rar5.multi.part04.rar",
"Rar5.multi.part05.rar",
"Rar5.multi.part06.rar",
]
);
private void DoRar_Multi_Reader_Delete_Files(string[] archives)
{
@@ -397,14 +407,16 @@ public class RarReaderTests : ReaderTests
Path.Combine("exe", "test.exe"),
}
);
using var reader = RarReader.Open([
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part01.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part02.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part03.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part04.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part05.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part06.rar"),
]);
using var reader = RarReader.Open(
[
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part01.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part02.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part03.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part04.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part05.rar"),
Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part06.rar"),
]
);
while (reader.MoveToNextEntry())
{
Assert.Equal(expectedOrder.Pop(), reader.Entry.Key);

View File

@@ -176,27 +176,6 @@ public abstract class ReaderTests : TestBase
}
}
protected void ReadForBufferBoundaryCheck(string fileName, CompressionType compressionType)
{
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, fileName));
using var reader = ReaderFactory.Open(stream, new ReaderOptions { LookForHeader = true });
while (reader.MoveToNextEntry())
{
Assert.Equal(compressionType, reader.Entry.CompressionType);
reader.WriteEntryToDirectory(
SCRATCH_FILES_PATH,
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
);
}
CompareFilesByPath(
Path.Combine(SCRATCH_FILES_PATH, "alice29.txt"),
Path.Combine(MISC_TEST_FILES_PATH, "alice29.txt")
);
}
protected void Iterate(
string testArchive,
string fileOrder,

View File

@@ -224,15 +224,6 @@ public class SevenZipArchiveTests : ArchiveTests
);
}
[Fact]
public void SevenZipArchive_TestEncryptedDetection()
{
using var passwordProtectedFilesArchive = SevenZipArchive.Open(
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.encryptedFiles.7z")
);
Assert.True(passwordProtectedFilesArchive.IsEncrypted);
}
[Fact]
public void SevenZipArchive_TestSolidDetection()
{

View File

@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net10.0;net48</TargetFrameworks>
<TargetFrameworks>net8.0;net48</TargetFrameworks>
<AssemblyName>SharpCompress.Test</AssemblyName>
<PackageId>SharpCompress.Test</PackageId>
<AssemblyOriginatorKeyFile>SharpCompress.Test.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))">

View File

@@ -74,21 +74,9 @@ public class XzIndexAsyncTests : XzTestsBase
public async Task SkipsPaddingAsync()
{
// Index with 3-byte padding.
using Stream badStream = new MemoryStream([
0x00,
0x01,
0x10,
0x80,
0x01,
0x00,
0x00,
0x00,
0xB1,
0x01,
0xD9,
0xC9,
0xFF,
]);
using Stream badStream = new MemoryStream(
[0x00, 0x01, 0x10, 0x80, 0x01, 0x00, 0x00, 0x00, 0xB1, 0x01, 0xD9, 0xC9, 0xFF]
);
var br = new BinaryReader(badStream);
var index = new XZIndex(br, false);
await index.ProcessAsync().ConfigureAwait(false);

View File

@@ -71,21 +71,9 @@ public class XzIndexTests : XzTestsBase
public void SkipsPadding()
{
// Index with 3-byte padding.
using Stream badStream = new MemoryStream([
0x00,
0x01,
0x10,
0x80,
0x01,
0x00,
0x00,
0x00,
0xB1,
0x01,
0xD9,
0xC9,
0xFF,
]);
using Stream badStream = new MemoryStream(
[0x00, 0x01, 0x10, 0x80, 0x01, 0x00, 0x00, 0x00, 0xB1, 0x01, 0xD9, 0xC9, 0xFF]
);
var br = new BinaryReader(badStream);
var index = new XZIndex(br, false);
index.Process();

View File

@@ -1,441 +0,0 @@
using System;
using System.Buffers.Binary;
using System.IO;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
using Xunit;
namespace SharpCompress.Test.Zip;
/// <summary>
/// Tests for verifying version consistency between Local File Header (LFH)
/// and Central Directory File Header (CDFH) when using Zip64.
/// </summary>
public class Zip64VersionConsistencyTests : WriterTests
{
public Zip64VersionConsistencyTests()
: base(ArchiveType.Zip) { }
[Fact]
public void Zip64_Small_File_With_UseZip64_Should_Have_Matching_Versions()
{
// Create a zip with UseZip64=true but with a small file
var filename = Path.Combine(SCRATCH2_FILES_PATH, "zip64_version_test.zip");
if (File.Exists(filename))
{
File.Delete(filename);
}
// Create archive with UseZip64=true
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = false,
UseZip64 = true,
};
ZipArchive zipArchive = ZipArchive.Create();
zipArchive.AddEntry("empty", new MemoryStream());
zipArchive.SaveTo(filename, writerOptions);
// Now read the raw bytes to verify version consistency
using var fs = File.OpenRead(filename);
using var br = new BinaryReader(fs);
// Read Local File Header
var lfhSignature = br.ReadUInt32();
Assert.Equal(0x04034b50u, lfhSignature); // Local file header signature
var lfhVersion = br.ReadUInt16();
// Skip to Central Directory
// Find Central Directory by searching from the end
fs.Seek(-22, SeekOrigin.End); // Min EOCD size
var eocdSignature = br.ReadUInt32();
if (eocdSignature != 0x06054b50u)
{
// Might have Zip64 EOCD, search backwards
fs.Seek(-100, SeekOrigin.End);
var buffer = new byte[100];
fs.Read(buffer, 0, 100);
// Find EOCD signature
for (int i = buffer.Length - 4; i >= 0; i--)
{
if (BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(i)) == 0x06054b50u)
{
fs.Seek(-100 + i, SeekOrigin.End);
break;
}
}
}
// Read EOCD
fs.Seek(-22, SeekOrigin.End);
br.ReadUInt32(); // EOCD signature
br.ReadUInt16(); // disk number
br.ReadUInt16(); // disk with central dir
br.ReadUInt16(); // entries on this disk
br.ReadUInt16(); // total entries
br.ReadUInt32(); // central directory size (unused)
var cdOffset = br.ReadUInt32();
// If Zip64, need to read from Zip64 EOCD
if (cdOffset == 0xFFFFFFFF)
{
// Find Zip64 EOCD Locator
fs.Seek(-22 - 20, SeekOrigin.End);
var z64eocdlSig = br.ReadUInt32();
if (z64eocdlSig == 0x07064b50u)
{
br.ReadUInt32(); // disk number
var z64eocdOffset = br.ReadUInt64();
br.ReadUInt32(); // total disks
// Read Zip64 EOCD
fs.Seek((long)z64eocdOffset, SeekOrigin.Begin);
br.ReadUInt32(); // signature
br.ReadUInt64(); // size of EOCD64
br.ReadUInt16(); // version made by
br.ReadUInt16(); // version needed
br.ReadUInt32(); // disk number
br.ReadUInt32(); // disk with CD
br.ReadUInt64(); // entries on disk
br.ReadUInt64(); // total entries
br.ReadUInt64(); // CD size
cdOffset = (uint)br.ReadUInt64(); // CD offset
}
}
// Read Central Directory Header
fs.Seek(cdOffset, SeekOrigin.Begin);
var cdhSignature = br.ReadUInt32();
Assert.Equal(0x02014b50u, cdhSignature); // Central directory header signature
br.ReadUInt16(); // version made by
var cdhVersionNeeded = br.ReadUInt16();
// The versions should match when UseZip64 is true
Assert.Equal(lfhVersion, cdhVersionNeeded);
}
[Fact]
public void Zip64_Small_File_Without_UseZip64_Should_Have_Version_20()
{
// Create a zip without UseZip64
var filename = Path.Combine(SCRATCH2_FILES_PATH, "no_zip64_version_test.zip");
if (File.Exists(filename))
{
File.Delete(filename);
}
// Create archive without UseZip64
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = false,
UseZip64 = false,
};
ZipArchive zipArchive = ZipArchive.Create();
zipArchive.AddEntry("empty", new MemoryStream());
zipArchive.SaveTo(filename, writerOptions);
// Read the raw bytes
using var fs = File.OpenRead(filename);
using var br = new BinaryReader(fs);
// Read Local File Header version
var lfhSignature = br.ReadUInt32();
Assert.Equal(0x04034b50u, lfhSignature);
var lfhVersion = br.ReadUInt16();
// Read Central Directory Header version
fs.Seek(-22, SeekOrigin.End);
br.ReadUInt32(); // EOCD signature
br.ReadUInt16(); // disk number
br.ReadUInt16(); // disk with central dir
br.ReadUInt16(); // entries on this disk
br.ReadUInt16(); // total entries
br.ReadUInt32(); // CD size
var cdOffset = br.ReadUInt32();
fs.Seek(cdOffset, SeekOrigin.Begin);
var cdhSignature = br.ReadUInt32();
Assert.Equal(0x02014b50u, cdhSignature);
br.ReadUInt16(); // version made by
var cdhVersionNeeded = br.ReadUInt16();
// Both should be version 20 (or less)
Assert.True(lfhVersion <= 20);
Assert.Equal(lfhVersion, cdhVersionNeeded);
}
[Fact]
public void LZMA_Compression_Should_Use_Version_63()
{
// Create a zip with LZMA compression
var filename = Path.Combine(SCRATCH2_FILES_PATH, "lzma_version_test.zip");
if (File.Exists(filename))
{
File.Delete(filename);
}
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.LZMA)
{
LeaveStreamOpen = false,
UseZip64 = false,
};
ZipArchive zipArchive = ZipArchive.Create();
var data = new byte[100];
new Random(42).NextBytes(data);
zipArchive.AddEntry("test.bin", new MemoryStream(data));
zipArchive.SaveTo(filename, writerOptions);
// Read the raw bytes
using var fs = File.OpenRead(filename);
using var br = new BinaryReader(fs);
// Read Local File Header version
var lfhSignature = br.ReadUInt32();
Assert.Equal(0x04034b50u, lfhSignature);
var lfhVersion = br.ReadUInt16();
// Read Central Directory Header version
fs.Seek(-22, SeekOrigin.End);
br.ReadUInt32(); // EOCD signature
br.ReadUInt16(); // disk number
br.ReadUInt16(); // disk with central dir
br.ReadUInt16(); // entries on this disk
br.ReadUInt16(); // total entries
br.ReadUInt32(); // CD size
var cdOffset = br.ReadUInt32();
fs.Seek(cdOffset, SeekOrigin.Begin);
var cdhSignature = br.ReadUInt32();
Assert.Equal(0x02014b50u, cdhSignature);
br.ReadUInt16(); // version made by
var cdhVersionNeeded = br.ReadUInt16();
// Both should be version 63 for LZMA
Assert.Equal(63, lfhVersion);
Assert.Equal(lfhVersion, cdhVersionNeeded);
}
[Fact]
public void PPMd_Compression_Should_Use_Version_63()
{
// Create a zip with PPMd compression
var filename = Path.Combine(SCRATCH2_FILES_PATH, "ppmd_version_test.zip");
if (File.Exists(filename))
{
File.Delete(filename);
}
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.PPMd)
{
LeaveStreamOpen = false,
UseZip64 = false,
};
ZipArchive zipArchive = ZipArchive.Create();
var data = new byte[100];
new Random(42).NextBytes(data);
zipArchive.AddEntry("test.bin", new MemoryStream(data));
zipArchive.SaveTo(filename, writerOptions);
// Read the raw bytes
using var fs = File.OpenRead(filename);
using var br = new BinaryReader(fs);
// Read Local File Header version
var lfhSignature = br.ReadUInt32();
Assert.Equal(0x04034b50u, lfhSignature);
var lfhVersion = br.ReadUInt16();
// Read Central Directory Header version
fs.Seek(-22, SeekOrigin.End);
br.ReadUInt32(); // EOCD signature
br.ReadUInt16(); // disk number
br.ReadUInt16(); // disk with central dir
br.ReadUInt16(); // entries on this disk
br.ReadUInt16(); // total entries
br.ReadUInt32(); // CD size
var cdOffset = br.ReadUInt32();
fs.Seek(cdOffset, SeekOrigin.Begin);
var cdhSignature = br.ReadUInt32();
Assert.Equal(0x02014b50u, cdhSignature);
br.ReadUInt16(); // version made by
var cdhVersionNeeded = br.ReadUInt16();
// Both should be version 63 for PPMd
Assert.Equal(63, lfhVersion);
Assert.Equal(lfhVersion, cdhVersionNeeded);
}
[Fact]
public void Zip64_Multiple_Small_Files_With_UseZip64_Should_Have_Matching_Versions()
{
// Create a zip with UseZip64=true but with multiple small files
var filename = Path.Combine(SCRATCH2_FILES_PATH, "zip64_version_multiple_test.zip");
if (File.Exists(filename))
{
File.Delete(filename);
}
WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = false,
UseZip64 = true,
};
ZipArchive zipArchive = ZipArchive.Create();
for (int i = 0; i < 5; i++)
{
var data = new byte[100];
new Random(i).NextBytes(data);
zipArchive.AddEntry($"file{i}.bin", new MemoryStream(data));
}
zipArchive.SaveTo(filename, writerOptions);
// Verify that all entries have matching versions
using var fs = File.OpenRead(filename);
using var br = new BinaryReader(fs);
// Read all LFH versions
var lfhVersions = new System.Collections.Generic.List<ushort>();
while (true)
{
var sig = br.ReadUInt32();
if (sig == 0x04034b50u) // LFH signature
{
var version = br.ReadUInt16();
lfhVersions.Add(version);
// Skip rest of LFH
br.ReadUInt16(); // flags
br.ReadUInt16(); // compression
br.ReadUInt32(); // mod time
br.ReadUInt32(); // crc
br.ReadUInt32(); // compressed size
br.ReadUInt32(); // uncompressed size
var fnLen = br.ReadUInt16();
var extraLen = br.ReadUInt16();
fs.Seek(fnLen + extraLen, SeekOrigin.Current);
// Skip compressed data by reading compressed size from extra field if zip64
// For simplicity in this test, we'll just find the next signature
var found = false;
while (fs.Position < fs.Length - 4)
{
var b = br.ReadByte();
if (b == 0x50)
{
var nextBytes = br.ReadBytes(3);
if (
(nextBytes[0] == 0x4b && nextBytes[1] == 0x03 && nextBytes[2] == 0x04)
|| // LFH
(nextBytes[0] == 0x4b && nextBytes[1] == 0x01 && nextBytes[2] == 0x02)
) // CDH
{
fs.Seek(-4, SeekOrigin.Current);
found = true;
break;
}
}
}
if (!found)
{
break;
}
}
else if (sig == 0x02014b50u) // CDH signature
{
break; // Reached central directory
}
else
{
break; // Unknown signature
}
}
// Find Central Directory
fs.Seek(-22, SeekOrigin.End);
br.ReadUInt32(); // EOCD signature
br.ReadUInt16(); // disk number
br.ReadUInt16(); // disk with central dir
br.ReadUInt16(); // entries on this disk
var totalEntries = br.ReadUInt16();
br.ReadUInt32(); // CD size
var cdOffset = br.ReadUInt32();
// Check if we need Zip64 EOCD
if (cdOffset == 0xFFFFFFFF)
{
fs.Seek(-22 - 20, SeekOrigin.End);
var z64eocdlSig = br.ReadUInt32();
if (z64eocdlSig == 0x07064b50u)
{
br.ReadUInt32(); // disk number
var z64eocdOffset = br.ReadUInt64();
fs.Seek((long)z64eocdOffset, SeekOrigin.Begin);
br.ReadUInt32(); // signature
br.ReadUInt64(); // size
br.ReadUInt16(); // version made by
br.ReadUInt16(); // version needed
br.ReadUInt32(); // disk number
br.ReadUInt32(); // disk with CD
br.ReadUInt64(); // entries on disk
totalEntries = (ushort)br.ReadUInt64(); // total entries
br.ReadUInt64(); // CD size
cdOffset = (uint)br.ReadUInt64(); // CD offset
}
}
// Read CDH versions
fs.Seek(cdOffset, SeekOrigin.Begin);
var cdhVersions = new System.Collections.Generic.List<ushort>();
for (int i = 0; i < totalEntries; i++)
{
var sig = br.ReadUInt32();
Assert.Equal(0x02014b50u, sig);
br.ReadUInt16(); // version made by
var version = br.ReadUInt16();
cdhVersions.Add(version);
// Skip rest of CDH
br.ReadUInt16(); // flags
br.ReadUInt16(); // compression
br.ReadUInt32(); // mod time
br.ReadUInt32(); // crc
br.ReadUInt32(); // compressed size
br.ReadUInt32(); // uncompressed size
var fnLen = br.ReadUInt16();
var extraLen = br.ReadUInt16();
var commentLen = br.ReadUInt16();
br.ReadUInt16(); // disk number start
br.ReadUInt16(); // internal attributes
br.ReadUInt32(); // external attributes
br.ReadUInt32(); // LFH offset
fs.Seek(fnLen + extraLen + commentLen, SeekOrigin.Current);
}
// Verify all versions match
Assert.Equal(lfhVersions.Count, cdhVersions.Count);
for (int i = 0; i < lfhVersions.Count; i++)
{
Assert.Equal(lfhVersions[i], cdhVersions[i]);
}
}
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -399,41 +397,4 @@ public class ZipReaderTests : ReaderTests
Assert.Equal("second.txt", reader.Entry.Key);
Assert.Equal(197, reader.Entry.Size);
}
[Fact]
public void ZipReader_Returns_Same_Entries_As_ZipArchive()
{
// Verifies that ZipReader and ZipArchive return the same entries
// for standard single-volume ZIP files. ZipReader processes LocalEntry
// headers sequentially, while ZipArchive uses DirectoryEntry headers
// from the central directory and seeks to LocalEntry headers for data.
var testFiles = new[] { "Zip.none.zip", "Zip.deflate.zip", "Zip.none.issue86.zip" };
foreach (var testFile in testFiles)
{
var path = Path.Combine(TEST_ARCHIVES_PATH, testFile);
var readerKeys = new List<string>();
using (var stream = File.OpenRead(path))
using (var reader = ZipReader.Open(stream))
{
while (reader.MoveToNextEntry())
{
readerKeys.Add(reader.Entry.Key!);
}
}
var archiveKeys = new List<string>();
using (var archive = Archives.Zip.ZipArchive.Open(path))
{
foreach (var entry in archive.Entries)
{
archiveKeys.Add(entry.Key!);
}
}
Assert.Equal(archiveKeys.Count, readerKeys.Count);
Assert.Equal(archiveKeys.OrderBy(k => k), readerKeys.OrderBy(k => k));
}
}
}

View File

@@ -1,7 +1,5 @@
using System.IO;
using System.Text;
using SharpCompress.Common;
using SharpCompress.Writers;
using Xunit;
namespace SharpCompress.Test.Zip;
@@ -11,42 +9,6 @@ public class ZipWriterTests : WriterTests
public ZipWriterTests()
: base(ArchiveType.Zip) { }
[Fact]
public void Zip_BZip2_Write_EmptyFile()
{
// Test that writing an empty file with BZip2 compression doesn't throw DivideByZeroException
using var memoryStream = new MemoryStream();
var options = new WriterOptions(CompressionType.BZip2)
{
ArchiveEncoding = new ArchiveEncoding { Default = new UTF8Encoding(false) },
};
using (var writer = WriterFactory.Open(memoryStream, ArchiveType.Zip, options))
{
writer.Write("test-folder/zero-byte-file.txt", Stream.Null);
}
Assert.True(memoryStream.Length > 0);
}
[Fact]
public void Zip_BZip2_Write_EmptyFolder()
{
// Test that writing an empty folder entry with BZip2 compression doesn't throw DivideByZeroException
using var memoryStream = new MemoryStream();
var options = new WriterOptions(CompressionType.BZip2)
{
ArchiveEncoding = new ArchiveEncoding { Default = new UTF8Encoding(false) },
};
using (var writer = WriterFactory.Open(memoryStream, ArchiveType.Zip, options))
{
writer.Write("test-empty-folder/", Stream.Null);
}
Assert.True(memoryStream.Length > 0);
}
[Fact]
public void Zip_Deflate_Write() =>
Write(

View File

@@ -4,20 +4,20 @@
".NETFramework,Version=v4.8": {
"AwesomeAssertions": {
"type": "Direct",
"requested": "[9.3.0, )",
"resolved": "9.3.0",
"contentHash": "8lGLYap2ec2gNLgjf2xKZaKLpQ7j36oJvrYzBVVpNAumqnxRdevqqhEF66qxE92f8y2+zsbQ061DeHG61ZhzaQ==",
"requested": "[9.2.1, )",
"resolved": "9.2.1",
"contentHash": "lbwhyQNXxxEGx4oCbFqNfFy2DsywsvNhN6qoOjY4wwhMgI2L9+YrxjyF/M0io99yrvWV1Cjj12LP2QGcC43Uhw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.NET.Test.Sdk": {
"type": "Direct",
"requested": "[18.0.1, )",
"resolved": "18.0.1",
"contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==",
"requested": "[18.0.0, )",
"resolved": "18.0.0",
"contentHash": "bvxj2Asb7nT+tqOFFerrhQeEjUYLwx0Poi0Rznu63WbqN+A4uDn1t5NWXfAOOQsF6lpmK6N2v+Vvgso7KWZS7g==",
"dependencies": {
"Microsoft.CodeCoverage": "18.0.1"
"Microsoft.CodeCoverage": "18.0.0"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
@@ -29,12 +29,6 @@
"Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
}
},
"Mono.Posix.NETStandard": {
"type": "Direct",
"requested": "[1.0.0, )",
"resolved": "1.0.0",
"contentHash": "vSN/L1uaVwKsiLa95bYu2SGkF0iY3xMblTfxc8alSziPuVfJpj3geVqHGAA75J7cZkMuKpFVikz82Lo6y6LLdA=="
},
"xunit": {
"type": "Direct",
"requested": "[2.9.3, )",
@@ -57,8 +51,8 @@
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "18.0.1",
"contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA=="
"resolved": "18.0.0",
"contentHash": "DFPhMrsIofgJ1DDU3ModqqRArDm15/bNl4ecmcuBspZkZ4ONYnCC0R8U27WzK7cYv6r8l6Q/fRmvg7cb+I/dJA=="
},
"Microsoft.NETFramework.ReferenceAssemblies.net48": {
"type": "Transitive",
@@ -98,17 +92,12 @@
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.6.3",
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
},
"System.ValueTuple": {
"type": "Transitive",
"resolved": "4.6.1",
"contentHash": "+RJT4qaekpZ7DDLhf+LTjq+E48jieKiY9ulJ+BoxKmZblIJfIJT8Ufcaa/clQqnYvWs8jugfGSMu8ylS0caG0w=="
},
"xunit.abstractions": {
"type": "Transitive",
"resolved": "2.0.3",
@@ -152,20 +141,20 @@
"sharpcompress": {
"type": "Project",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "[10.0.0, )",
"Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )",
"System.Buffers": "[4.6.1, )",
"System.Memory": "[4.6.3, )",
"System.Text.Encoding.CodePages": "[10.0.0, )",
"System.Text.Encoding.CodePages": "[8.0.0, )",
"ZstdSharp.Port": "[0.8.6, )"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "CentralTransitive",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.6.3"
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"System.Buffers": {
@@ -187,13 +176,12 @@
},
"System.Text.Encoding.CodePages": {
"type": "CentralTransitive",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "QLP54mIATaBpjGlsZIxga38VPk1G9js0Kw651B+bvrXi2kSgGZYrxJSpM3whhTZCBK4HEBHX3fzfDQMw7CXHGQ==",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
"dependencies": {
"System.Memory": "4.6.3",
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
"System.ValueTuple": "4.6.1"
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"ZstdSharp.Port": {
@@ -208,21 +196,21 @@
}
}
},
"net10.0": {
"net8.0": {
"AwesomeAssertions": {
"type": "Direct",
"requested": "[9.3.0, )",
"resolved": "9.3.0",
"contentHash": "8lGLYap2ec2gNLgjf2xKZaKLpQ7j36oJvrYzBVVpNAumqnxRdevqqhEF66qxE92f8y2+zsbQ061DeHG61ZhzaQ=="
"requested": "[9.2.1, )",
"resolved": "9.2.1",
"contentHash": "lbwhyQNXxxEGx4oCbFqNfFy2DsywsvNhN6qoOjY4wwhMgI2L9+YrxjyF/M0io99yrvWV1Cjj12LP2QGcC43Uhw=="
},
"Microsoft.NET.Test.Sdk": {
"type": "Direct",
"requested": "[18.0.1, )",
"resolved": "18.0.1",
"contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==",
"requested": "[18.0.0, )",
"resolved": "18.0.0",
"contentHash": "bvxj2Asb7nT+tqOFFerrhQeEjUYLwx0Poi0Rznu63WbqN+A4uDn1t5NWXfAOOQsF6lpmK6N2v+Vvgso7KWZS7g==",
"dependencies": {
"Microsoft.CodeCoverage": "18.0.1",
"Microsoft.TestPlatform.TestHost": "18.0.1"
"Microsoft.CodeCoverage": "18.0.0",
"Microsoft.TestPlatform.TestHost": "18.0.0"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
@@ -259,8 +247,8 @@
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "18.0.1",
"contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA=="
"resolved": "18.0.0",
"contentHash": "DFPhMrsIofgJ1DDU3ModqqRArDm15/bNl4ecmcuBspZkZ4ONYnCC0R8U27WzK7cYv6r8l6Q/fRmvg7cb+I/dJA=="
},
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
"type": "Transitive",
@@ -269,18 +257,18 @@
},
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "18.0.1",
"contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==",
"resolved": "18.0.0",
"contentHash": "Al/a99ymb8UdEEh6DKNiaoFn5i8fvX5PdM9LfU9Z/Q8NJrlyHHzF+LRHLbR+t89gRsJ2fFMpwYxgEn3eH1BQwA==",
"dependencies": {
"System.Reflection.Metadata": "8.0.0"
}
},
"Microsoft.TestPlatform.TestHost": {
"type": "Transitive",
"resolved": "18.0.1",
"contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==",
"resolved": "18.0.0",
"contentHash": "aAxE8Thr9ZHGrljOYaDeLJqitQi75iE4xeEFn6CEGFirlHSn1KwpKPniuEn6zCLZ90Z3XqNlrC3ZJTuvBov45w==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.0.1",
"Microsoft.TestPlatform.ObjectModel": "18.0.0",
"Newtonsoft.Json": "13.0.3"
}
},

File diff suppressed because it is too large Load Diff