Skip to content

Commit 775a9bf

Browse files
committed
update agents md
1 parent 57cd2f1 commit 775a9bf

File tree

1 file changed

+34
-65
lines changed

1 file changed

+34
-65
lines changed

AGENTS.md

Lines changed: 34 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@ applyTo: '**/*.cs'
66
# SharpCompress Development
77

88
## About SharpCompress
9-
SharpCompress is a pure C# compression library supporting multiple archive formats (Zip, Tar, GZip, BZip2, 7Zip, Rar, LZip, XZ, ZStandard) for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0, and .NET 8.0. The library provides both seekable Archive APIs and forward-only Reader/Writer APIs for streaming scenarios.
9+
SharpCompress is a pure C# compression library supporting multiple archive formats (Zip, Tar, GZip, BZip2, 7Zip, Rar, LZip, XZ, ZStandard). The project currently targets .NET Framework 4.8, .NET Standard 2.0, .NET 8.0, and .NET 10.0. The library provides both seekable Archive APIs and forward-only Reader/Writer APIs for streaming scenarios.
1010

1111
## C# Instructions
12-
- Always use the latest version C#, currently C# 13 features.
13-
- Write clear and concise comments for each function.
12+
- Use language features supported by the current project toolchain (`LangVersion=latest`) and existing codebase patterns.
13+
- Add comments for non-obvious logic and important design decisions; avoid redundant comments.
1414
- Follow the existing code style and patterns in the codebase.
1515

1616
## General Instructions
17-
- **Agents should NEVER commit to git** - Agents should stage files and leave committing to the user. Only create commits when the user explicitly requests them.
17+
- **Do not commit or stage changes unless the user explicitly asks for it.**
1818
- Make only high confidence suggestions when reviewing code changes.
1919
- Write code with good maintainability practices, including comments on why certain design decisions were made.
2020
- Handle edge cases and write clear exception handling.
2121
- For libraries or external dependencies, mention their usage and purpose in comments.
2222
- Preserve backward compatibility when making changes to public APIs.
2323

24+
### Workspace Hygiene
25+
- Do not edit generated or machine-local files unless required for the task (for example: `bin/`, `obj/`, `*.csproj.user`).
26+
- Avoid broad formatting-only diffs in unrelated files.
27+
2428
## Naming Conventions
2529

2630
- Follow PascalCase for component names, method names, and public members.
@@ -64,7 +68,7 @@ SharpCompress is a pure C# compression library supporting multiple archive forma
6468

6569
## Project Setup and Structure
6670

67-
- The project targets multiple frameworks: .NET Framework 4.62, .NET Standard 2.1, .NET 6.0, and .NET 8.0
71+
- The project targets multiple frameworks: .NET Framework 4.8, .NET Standard 2.0, .NET 8.0, and .NET 10.0
6872
- Main library is in `src/SharpCompress/`
6973
- Tests are in `tests/SharpCompress.Test/`
7074
- Performance tests are in `tests/SharpCompress.Performance/`
@@ -89,13 +93,18 @@ src/SharpCompress/
8993
tests/SharpCompress.Test/
9094
├── Zip/, Tar/, Rar/, SevenZip/, GZip/, BZip2/ # Format-specific tests
9195
├── TestBase.cs # Base test class with helper methods
92-
└── TestArchives/ # Test data (not checked into main test project)
96+
97+
tests/
98+
├── SharpCompress.Test/ # Unit/integration tests
99+
├── SharpCompress.Performance/ # Benchmark tests
100+
└── TestArchives/ # Test data archives
93101
```
94102

95103
### Factory Pattern
96-
All format types implement factory interfaces (`IArchiveFactory`, `IReaderFactory`, `IWriterFactory`) for auto-detection:
97-
- `ReaderFactory.Open()` - Auto-detects format by probing stream
98-
- `WriterFactory.Open()` - Creates writer for specified `ArchiveType`
104+
Factory implementations can implement one or more interfaces (`IArchiveFactory`, `IReaderFactory`, `IWriterFactory`) depending on format capabilities:
105+
- `ArchiveFactory.OpenArchive()` - Opens archive API objects from seekable streams/files
106+
- `ReaderFactory.OpenReader()` - Auto-detects and opens forward-only readers
107+
- `WriterFactory.OpenWriter()` - Creates a writer for a specified `ArchiveType`
99108
- Factories located in: `src/SharpCompress/Factories/`
100109

101110
## Nullable Reference Types
@@ -166,71 +175,31 @@ SharpCompress supports multiple archive and compression formats:
166175
- Test stream disposal and `LeaveStreamOpen` behavior.
167176
- Test edge cases: empty archives, large files, corrupted archives, encrypted archives.
168177

178+
### Validation Expectations
179+
- Run targeted tests for the changed area first.
180+
- Run `dotnet csharpier format .` after code edits.
181+
- Run `dotnet csharpier check .` before handing off changes.
182+
169183
### Test Organization
170184
- Base class: `TestBase` - Provides `TEST_ARCHIVES_PATH`, `SCRATCH_FILES_PATH`, temp directory management
171185
- Framework: xUnit with AwesomeAssertions
172186
- Test archives: `tests/TestArchives/` - Use existing archives, don't create new ones unnecessarily
173187
- Match naming style of nearby test files
174188

189+
### Public API Change Checklist
190+
- Preserve existing public method signatures and behavior when possible.
191+
- If a breaking change is unavoidable, document it and provide a migration path.
192+
- Add or update tests that cover backward compatibility expectations.
193+
194+
### Stream Ownership and Position Checklist
195+
- Verify `LeaveStreamOpen` behavior for externally owned streams.
196+
- Validate behavior for both seekable and non-seekable streams.
197+
- Ensure stream position assumptions are explicit and tested.
198+
175199
## Common Pitfalls
176200

177201
1. **Don't mix Archive and Reader APIs** - Archive needs seekable stream, Reader doesn't
178202
2. **Solid archives (Rar, 7Zip)** - Use `ExtractAllEntries()` for best performance, not individual entry extraction
179203
3. **Stream disposal** - Always set `LeaveStreamOpen` explicitly when needed (default is to close)
180204
4. **Tar + non-seekable stream** - Must provide file size or it will throw
181-
6. **Format detection** - Use `ReaderFactory.Open()` for auto-detection, test with actual archive files
182-
183-
### Async Struct-Copy Bug in LZMA RangeCoder
184-
185-
When implementing async methods on mutable `struct` types (like `BitEncoder` and `BitDecoder` in the LZMA RangeCoder), be aware that the async state machine copies the struct when `await` is encountered. This means mutations to struct fields after the `await` point may not persist back to the original struct stored in arrays or fields.
186-
187-
**The Bug:**
188-
```csharp
189-
// BAD: async method on mutable struct
190-
public async ValueTask<uint> DecodeAsync(Decoder decoder, CancellationToken cancellationToken = default)
191-
{
192-
var newBound = (decoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob;
193-
if (decoder._code < newBound)
194-
{
195-
decoder._range = newBound;
196-
_prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; // Mutates _prob
197-
await decoder.Normalize2Async(cancellationToken).ConfigureAwait(false); // Struct gets copied here
198-
return 0; // Original _prob update may be lost
199-
}
200-
// ...
201-
}
202-
```
203-
204-
**The Fix:**
205-
Refactor async methods on mutable structs to perform all struct mutations synchronously before any `await`, or use a helper method to separate the await from the struct mutation:
206-
207-
```csharp
208-
// GOOD: struct mutations happen synchronously, await is conditional
209-
public ValueTask<uint> DecodeAsync(Decoder decoder, CancellationToken cancellationToken = default)
210-
{
211-
var newBound = (decoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob;
212-
if (decoder._code < newBound)
213-
{
214-
decoder._range = newBound;
215-
_prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; // All mutations complete
216-
return DecodeAsyncHelper(decoder.Normalize2Async(cancellationToken), 0); // Await in helper
217-
}
218-
decoder._range -= newBound;
219-
decoder._code -= newBound;
220-
_prob -= (_prob) >> K_NUM_MOVE_BITS; // All mutations complete
221-
return DecodeAsyncHelper(decoder.Normalize2Async(cancellationToken), 1); // Await in helper
222-
}
223-
224-
private static async ValueTask<uint> DecodeAsyncHelper(ValueTask normalizeTask, uint result)
225-
{
226-
await normalizeTask.ConfigureAwait(false);
227-
return result;
228-
}
229-
```
230-
231-
**Why This Matters:**
232-
In LZMA, the `BitEncoder` and `BitDecoder` structs maintain adaptive probability models in their `_prob` field. When these structs are stored in arrays (e.g., `_models[m]`), the async state machine copy breaks the adaptive model, causing incorrect bit decoding and eventually `DataErrorException` exceptions.
233-
234-
**Related Files:**
235-
- `src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.Async.cs` - Fixed
236-
- `src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.Async.cs` - Uses readonly structs, so this pattern doesn't apply
205+
5. **Format detection** - Use `ReaderFactory.OpenReader()` for auto-detection, test with actual archive files

0 commit comments

Comments
 (0)