mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 06:54:40 +00:00
first pass of sync generator
This commit is contained in:
29
AGENTS.md
29
AGENTS.md
@@ -154,6 +154,35 @@ SharpCompress supports multiple archive and compression formats:
|
||||
- `OpenEntryStreamAsync` - Open entry stream asynchronously
|
||||
- Always provide `CancellationToken` parameter in async methods
|
||||
|
||||
### Generating sync methods from async ones
|
||||
Most of the library still keeps a hand-written sync twin beside each async method, usually split as
|
||||
`Foo.cs` + `Foo.Async.cs`. Where the two bodies are the same logic, the sync one is generated
|
||||
instead, using [Zomp.SyncMethodGenerator](https://github.com/zompinc/sync-method-generator) (already
|
||||
referenced for every project via `Directory.Packages.props`).
|
||||
|
||||
- Put `[Zomp.SyncMethodGenerator.CreateSyncVersion]` on the **async** method — that body is then the
|
||||
single source of truth — and delete the hand-written sync twin. The generator drops the `Async`
|
||||
suffix, `CancellationToken` and `IProgress<T>` (unless `PreserveCancellationToken` /
|
||||
`PreserveProgress`), maps `Task`/`ValueTask` to `void`/`T` and `Memory<T>` to `Span<T>`, and
|
||||
rewrites `FooAsync(...)` calls to `Foo(...)`. Modifiers, including `override`/`virtual`, are kept.
|
||||
- **Attribute individual methods, never the whole type.** A type-level attribute also generates the
|
||||
members that must not exist (see below), so you would need more `[SkipSyncVersion]` than
|
||||
`[CreateSyncVersion]`.
|
||||
- Attribute a method **only when the generated signature already exists by hand.** Generating a
|
||||
member that did not exist before is a behaviour change, not a deduplication — in particular, a
|
||||
generated `Read(Span<byte>)` replaces `Stream`'s default rent-and-copy shim.
|
||||
- Deliberately **not** generated: `Dispose`/`DisposeAsync` (on a `Stream` the generated `Dispose()`
|
||||
cannot override the non-virtual `Stream.Dispose()`), `Flush`/`FlushAsync`, `CopyTo`/`CopyToAsync`,
|
||||
`ReadByte`/`WriteByte` (generated without `override`, so they would hide `Stream`'s), and the
|
||||
`Memory`/`ReadOnlyMemory` overloads that have no sync twin.
|
||||
- Keep one XML doc comment, on the async method, phrased tense-neutrally ("Extract entry to the
|
||||
specified stream.") — it is emitted onto both copies.
|
||||
- Use `#if SYNC_ONLY` / `#if !SYNC_ONLY` only for a localised I/O idiom that genuinely differs; if it
|
||||
would cover more than a small part of the method, keep two hand-written methods instead.
|
||||
- To see what was generated, build with `-p:EmitCompilerGeneratedFiles=true` and look in
|
||||
`obj/<config>/<tfm>/generated/Zomp.SyncMethodGenerator/`. A sync stack frame may therefore name a
|
||||
method that has no file in the repo — debug it via the async source.
|
||||
|
||||
### Archive APIs vs Reader/Writer APIs
|
||||
- **Archive API**: Use for random access with seekable streams (e.g., `ZipArchive`, `TarArchive`)
|
||||
- **Reader API**: Use for forward-only reading on non-seekable streams (e.g., `ZipReader`, `TarReader`)
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace SharpCompress.Compressors.Filters;
|
||||
|
||||
internal abstract partial class Filter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
@@ -200,6 +201,7 @@ internal abstract partial class Filter
|
||||
}
|
||||
#endif
|
||||
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task WriteAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -50,99 +50,9 @@ internal abstract partial class Filter : Stream
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var size = 0;
|
||||
|
||||
if (_transformed > 0)
|
||||
{
|
||||
var copySize = _transformed;
|
||||
if (copySize > count)
|
||||
{
|
||||
copySize = count;
|
||||
}
|
||||
Buffer.BlockCopy(_tail, 0, buffer, offset, copySize);
|
||||
_transformed -= copySize;
|
||||
_read -= copySize;
|
||||
offset += copySize;
|
||||
count -= copySize;
|
||||
size += copySize;
|
||||
Buffer.BlockCopy(_tail, copySize, _tail, 0, _read);
|
||||
}
|
||||
if (count == 0)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
var inSize = _read;
|
||||
if (inSize > count)
|
||||
{
|
||||
inSize = count;
|
||||
}
|
||||
Buffer.BlockCopy(_tail, 0, buffer, offset, inSize);
|
||||
_read -= inSize;
|
||||
Buffer.BlockCopy(_tail, inSize, _tail, 0, _read);
|
||||
while (!_endReached && inSize < count)
|
||||
{
|
||||
var baseRead = _baseStream.Read(buffer, offset + inSize, count - inSize);
|
||||
inSize += baseRead;
|
||||
if (baseRead == 0)
|
||||
{
|
||||
_endReached = true;
|
||||
}
|
||||
}
|
||||
while (!_endReached && _read < _tail.Length)
|
||||
{
|
||||
var baseRead = _baseStream.Read(_tail, _read, _tail.Length - _read);
|
||||
_read += baseRead;
|
||||
if (baseRead == 0)
|
||||
{
|
||||
_endReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inSize > _tail.Length)
|
||||
{
|
||||
_transformed = Transform(buffer, offset, inSize);
|
||||
offset += _transformed;
|
||||
count -= _transformed;
|
||||
size += _transformed;
|
||||
inSize -= _transformed;
|
||||
_transformed = 0;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(buffer, offset, _window, 0, inSize);
|
||||
Buffer.BlockCopy(_tail, 0, _window, inSize, _read);
|
||||
if (inSize + _read > _tail.Length)
|
||||
{
|
||||
_transformed = Transform(_window, 0, inSize + _read);
|
||||
}
|
||||
else
|
||||
{
|
||||
_transformed = inSize + _read;
|
||||
}
|
||||
Buffer.BlockCopy(_window, 0, buffer, offset, inSize);
|
||||
Buffer.BlockCopy(_window, inSize, _tail, 0, _read);
|
||||
size += inSize;
|
||||
_transformed -= inSize;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||
|
||||
public override void SetLength(long value) => throw new NotSupportedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
Transform(buffer, offset, count);
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
protected abstract int Transform(byte[] buffer, int offset, int count);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class ArmFilter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
@@ -45,13 +44,5 @@ public partial class ArmFilter : BlockFilter
|
||||
|
||||
public override void ValidateFilter() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
BranchExecFilter.ARMConverter(buffer, _ip);
|
||||
_ip += (uint)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream) => BaseStream = stream;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class ArmThumbFilter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
@@ -45,13 +44,5 @@ public partial class ArmThumbFilter : BlockFilter
|
||||
|
||||
public override void ValidateFilter() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
BranchExecFilter.ARMTConverter(buffer, _ip);
|
||||
_ip += (uint)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream) => BaseStream = stream;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class IA64Filter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
@@ -45,13 +44,5 @@ public partial class IA64Filter : BlockFilter
|
||||
|
||||
public override void ValidateFilter() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
BranchExecFilter.IA64Converter(buffer, _ip);
|
||||
_ip += (uint)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream) => BaseStream = stream;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class Lzma2Filter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -52,8 +52,5 @@ public partial class Lzma2Filter : BlockFilter
|
||||
public override void SetBaseStream(Stream stream) =>
|
||||
BaseStream = LzmaStream.Create(new[] { _dictionarySize }, stream);
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) =>
|
||||
BaseStream.Read(buffer, offset, count);
|
||||
|
||||
public override int ReadByte() => BaseStream.ReadByte();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class PowerPCFilter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
@@ -45,13 +44,5 @@ public partial class PowerPCFilter : BlockFilter
|
||||
|
||||
public override void ValidateFilter() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
BranchExecFilter.PowerPCConverter(buffer, _ip);
|
||||
_ip += (uint)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream) => BaseStream = stream;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class SparcFilter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
@@ -45,13 +44,5 @@ public partial class SparcFilter : BlockFilter
|
||||
|
||||
public override void ValidateFilter() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
BranchExecFilter.SPARCConverter(buffer, _ip);
|
||||
_ip += (uint)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream) => BaseStream = stream;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
public partial class X86Filter
|
||||
{
|
||||
[Zomp.SyncMethodGenerator.CreateSyncVersion]
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz.Filters;
|
||||
|
||||
@@ -47,13 +46,5 @@ public partial class X86Filter : BlockFilter
|
||||
|
||||
public override void ValidateFilter() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = BaseStream.Read(buffer, offset, count);
|
||||
BranchExecFilter.X86Converter(buffer, _ip, ref _state);
|
||||
_ip += (uint)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void SetBaseStream(Stream stream) => BaseStream = stream;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user