diff --git a/.github/workflows/nuget-release.yml b/.github/workflows/nuget-release.yml
index de4a366a0..ccc626cc6 100644
--- a/.github/workflows/nuget-release.yml
+++ b/.github/workflows/nuget-release.yml
@@ -45,6 +45,12 @@ jobs:
# Build and test
- name: Build and Test
run: dotnet run --project build/build.csproj
+
+ - name: Validate AOT Smoke Test
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ dotnet publish tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj --configuration Release --runtime linux-x64 --self-contained true --output artifacts/aot-smoke
+ ./artifacts/aot-smoke/SharpCompress.AotSmoke
# Upload artifacts for verification
- name: Upload NuGet Package
diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json
index 738df5991..e1e7afa51 100644
--- a/src/SharpCompress/packages.lock.json
+++ b/src/SharpCompress/packages.lock.json
@@ -286,9 +286,9 @@
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
- "requested": "[10.0.6, )",
- "resolved": "10.0.6",
- "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA=="
+ "requested": "[10.0.8, )",
+ "resolved": "10.0.8",
+ "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -388,9 +388,9 @@
"net8.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
- "requested": "[8.0.26, )",
- "resolved": "8.0.26",
- "contentHash": "o7/yVssM2r9Wyln2s9edBd5ANZXqdSdBI+g7JqXkyJmXrhs2WsJp25K5yPnYrTgdKBCjKB8bg+O2oew4sgzFaA=="
+ "requested": "[8.0.27, )",
+ "resolved": "8.0.27",
+ "contentHash": "rQi9TxifHRnXP7lVRZH05DxD2/XGbJp12q0ozcbrlBlBnyyzssFTH/2vLhtKWUp2CT1qVscTrcYTFiwTyKPKRg=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
diff --git a/tests/SharpCompress.AotSmoke/Program.cs b/tests/SharpCompress.AotSmoke/Program.cs
new file mode 100644
index 000000000..2bdbabe6c
--- /dev/null
+++ b/tests/SharpCompress.AotSmoke/Program.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+using System.Text;
+using SharpCompress.Archives;
+using SharpCompress.Common;
+using SharpCompress.Readers;
+using SharpCompress.Writers;
+
+var original = "SharpCompress AOT smoke test";
+using var archiveStream = new MemoryStream();
+
+using (
+ var writer = WriterFactory.OpenWriter(
+ archiveStream,
+ ArchiveType.Zip,
+ new WriterOptions(CompressionType.Deflate) { LeaveStreamOpen = true }
+ )
+)
+{
+ using var entryStream = new MemoryStream(Encoding.UTF8.GetBytes(original));
+ writer.Write("payload.txt", entryStream, DateTime.UtcNow);
+}
+
+archiveStream.Position = 0;
+using (var reader = ReaderFactory.OpenReader(archiveStream, ReaderOptions.ForExternalStream))
+{
+ if (!reader.MoveToNextEntry() || reader.Entry.IsDirectory)
+ {
+ throw new InvalidOperationException("Expected a file entry.");
+ }
+
+ using var extracted = new MemoryStream();
+ reader.WriteEntryTo(extracted);
+ var actual = Encoding.UTF8.GetString(extracted.ToArray());
+ if (!string.Equals(original, actual, StringComparison.Ordinal))
+ {
+ throw new InvalidOperationException("ReaderFactory round-trip content mismatch.");
+ }
+}
+
+archiveStream.Position = 0;
+using (var archive = ArchiveFactory.OpenArchive(archiveStream, ReaderOptions.ForExternalStream))
+{
+ var entryCount = 0;
+ foreach (var entry in archive.Entries)
+ {
+ if (!entry.IsDirectory)
+ {
+ entryCount++;
+ }
+ }
+
+ if (entryCount != 1)
+ {
+ throw new InvalidOperationException("ArchiveFactory did not see the expected entry.");
+ }
+}
+
+Console.WriteLine("SharpCompress AOT smoke test passed.");
diff --git a/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj b/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj
new file mode 100644
index 000000000..c6f6314b4
--- /dev/null
+++ b/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj
@@ -0,0 +1,12 @@
+
+
+ Exe
+ net10.0
+ true
+ true
+ full
+
+
+
+
+
diff --git a/tests/SharpCompress.AotSmoke/packages.lock.json b/tests/SharpCompress.AotSmoke/packages.lock.json
new file mode 100644
index 000000000..74a5d8021
--- /dev/null
+++ b/tests/SharpCompress.AotSmoke/packages.lock.json
@@ -0,0 +1,84 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net10.0": {
+ "Microsoft.DotNet.ILCompiler": {
+ "type": "Direct",
+ "requested": "[10.0.6, )",
+ "resolved": "10.0.6",
+ "contentHash": "nBOzxOys8OeyJ+Nsi/uYlI/5TSsvwjaM/p5m4dTL6khCLx9UuP3b2ec3HeuBw/+F7hHCAZG1yFx8VBeoRAX+EQ=="
+ },
+ "Microsoft.NET.ILLink.Tasks": {
+ "type": "Direct",
+ "requested": "[10.0.6, )",
+ "resolved": "10.0.6",
+ "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA=="
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies": {
+ "type": "Direct",
+ "requested": "[1.0.3, )",
+ "resolved": "1.0.3",
+ "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+ "dependencies": {
+ "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
+ }
+ },
+ "Microsoft.SourceLink.GitHub": {
+ "type": "Direct",
+ "requested": "[10.0.102, )",
+ "resolved": "10.0.102",
+ "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==",
+ "dependencies": {
+ "Microsoft.Build.Tasks.Git": "10.0.102",
+ "Microsoft.SourceLink.Common": "10.0.102"
+ }
+ },
+ "Microsoft.VisualStudio.Threading.Analyzers": {
+ "type": "Direct",
+ "requested": "[17.14.15, )",
+ "resolved": "17.14.15",
+ "contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
+ },
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
+ "Microsoft.Build.Tasks.Git": {
+ "type": "Transitive",
+ "resolved": "10.0.102",
+ "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg=="
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies.net461": {
+ "type": "Transitive",
+ "resolved": "1.0.3",
+ "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
+ },
+ "Microsoft.SourceLink.Common": {
+ "type": "Transitive",
+ "resolved": "10.0.102",
+ "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A=="
+ },
+ "sharpcompress": {
+ "type": "Project"
+ }
+ },
+ "net10.0/osx-arm64": {
+ "Microsoft.DotNet.ILCompiler": {
+ "type": "Direct",
+ "requested": "[10.0.6, )",
+ "resolved": "10.0.6",
+ "contentHash": "nBOzxOys8OeyJ+Nsi/uYlI/5TSsvwjaM/p5m4dTL6khCLx9UuP3b2ec3HeuBw/+F7hHCAZG1yFx8VBeoRAX+EQ==",
+ "dependencies": {
+ "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": "10.0.6"
+ }
+ },
+ "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": {
+ "type": "Transitive",
+ "resolved": "10.0.6",
+ "contentHash": "+yovwOAlIpfIcH+ZWmLYXWTSWYJ93wcQxF/RVk+X4MXgLASeosCJYVLqP20g0cufKjoRqvCmnklR6y9Su3ORtA=="
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/SharpCompress.Performance/Benchmarks/TarBenchmarks.cs b/tests/SharpCompress.Performance/Benchmarks/TarBenchmarks.cs
index 634f39403..7a7cbadd0 100644
--- a/tests/SharpCompress.Performance/Benchmarks/TarBenchmarks.cs
+++ b/tests/SharpCompress.Performance/Benchmarks/TarBenchmarks.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
+using SharpCompress.Archives;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Compressors;
@@ -67,11 +68,11 @@ public void TarExtractReaderApi()
[Benchmark(Description = "Tar: Extract all entries (Archive API) - SystemGzip")]
public void SystemTarExtractArchiveApi()
{
- using var stream = new MemoryStream(_tarBytes);
- using var archive = TarArchive.OpenArchive(
+ using var stream = new MemoryStream(_tarGzBytes);
+ using var archive = ArchiveFactory.OpenArchive(
stream,
ReaderOptions.ForExternalStream.WithProviders(
- CompressionProviderRegistry.Empty.With(new SystemGZipCompressionProvider())
+ CompressionProviderRegistry.Default.With(new SystemGZipCompressionProvider())
)
);
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
@@ -84,11 +85,11 @@ public void SystemTarExtractArchiveApi()
[Benchmark(Description = "Tar: Extract all entries (Reader API) - SystemGzip")]
public void SystemTarExtractReaderApi()
{
- using var stream = new MemoryStream(_tarBytes);
+ using var stream = new MemoryStream(_tarGzBytes);
using var reader = ReaderFactory.OpenReader(
stream,
ReaderOptions.ForExternalStream.WithProviders(
- CompressionProviderRegistry.Empty.With(new SystemGZipCompressionProvider())
+ CompressionProviderRegistry.Default.With(new SystemGZipCompressionProvider())
)
);
while (reader.MoveToNextEntry())
@@ -104,7 +105,7 @@ public void SystemTarExtractReaderApi()
public void TarGzipExtract()
{
using var stream = new MemoryStream(_tarGzBytes);
- using var archive = TarArchive.OpenArchive(stream);
+ using var archive = ArchiveFactory.OpenArchive(stream);
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();
@@ -116,7 +117,9 @@ public void TarGzipExtract()
public async Task TarGzipExtractAsync()
{
using var stream = new MemoryStream(_tarGzBytes);
- await using var archive = await TarArchive.OpenAsyncArchive(stream).ConfigureAwait(false);
+ await using var archive = await ArchiveFactory
+ .OpenAsyncArchive(stream)
+ .ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
diff --git a/tests/SharpCompress.Test/AsyncParityAndCancellationTests.cs b/tests/SharpCompress.Test/AsyncParityAndCancellationTests.cs
new file mode 100644
index 000000000..49beacfab
--- /dev/null
+++ b/tests/SharpCompress.Test/AsyncParityAndCancellationTests.cs
@@ -0,0 +1,341 @@
+#if NET8_0_OR_GREATER
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using SharpCompress.Archives;
+using SharpCompress.Common;
+using SharpCompress.Readers;
+using SharpCompress.Test.Mocks;
+using SharpCompress.Writers;
+using Xunit;
+
+namespace SharpCompress.Test;
+
+public class AsyncParityAndCancellationTests : TestBase
+{
+ [Theory]
+ [InlineData("Zip.deflate.zip")]
+ [InlineData("Tar.tar")]
+ [InlineData("Tar.tar.gz")]
+ [InlineData("Rar.rar")]
+ [InlineData("7Zip.nonsolid.7z")]
+ public async Task ArchiveAsyncEntries_ShouldMatchSyncEntries(string archiveName)
+ {
+ var archivePath = Path.Combine(TEST_ARCHIVES_PATH, archiveName);
+
+ var syncEntries = ReadArchiveEntries(archivePath);
+ var asyncEntries = await ReadArchiveEntriesAsync(archivePath);
+
+ Assert.Equal(syncEntries, asyncEntries);
+ }
+
+ [Theory]
+ [InlineData("Zip.deflate.zip")]
+ [InlineData("Tar.tar")]
+ [InlineData("Tar.tar.gz")]
+ [InlineData("Rar.rar")]
+ public async Task ReaderAsyncEntries_ShouldMatchSyncEntries(string archiveName)
+ {
+ var archivePath = Path.Combine(TEST_ARCHIVES_PATH, archiveName);
+
+ var syncEntries = ReadReaderEntries(archivePath);
+ var asyncEntries = await ReadReaderEntriesAsync(archivePath);
+
+ Assert.Equal(syncEntries, asyncEntries);
+ }
+
+ [Fact]
+ public async Task AsyncReaderExtraction_ShouldRespectCancellationBeforeStart()
+ {
+ var archivePath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip");
+ await using var stream = File.OpenRead(archivePath);
+ await using var reader = await ReaderFactory.OpenAsyncReader(stream);
+ using var cts = new CancellationTokenSource();
+ cts.Cancel();
+
+ await Assert.ThrowsAnyAsync(async () =>
+ await reader.WriteAllToDirectoryAsync(SCRATCH_FILES_PATH, cancellationToken: cts.Token)
+ );
+ }
+
+ [Fact]
+ public async Task AsyncArchiveExtraction_ShouldRespectCancellationBeforeStart()
+ {
+ var archivePath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip");
+ await using var archive = await ArchiveFactory.OpenAsyncArchive(archivePath);
+ using var cts = new CancellationTokenSource();
+ cts.Cancel();
+
+ await Assert.ThrowsAnyAsync(async () =>
+ await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH, cancellationToken: cts.Token)
+ );
+ }
+
+ [Fact]
+ public async Task AsyncReaderExtraction_ShouldRespectCancellationDuringRead()
+ {
+ var archiveBytes = CreateLargeTarArchive();
+ using var cts = new CancellationTokenSource();
+ await using var stream = new CancelAfterBytesReadStream(
+ new MemoryStream(archiveBytes),
+ cts,
+ cancelAfterBytes: 2048
+ );
+ await Assert.ThrowsAnyAsync(async () =>
+ {
+ await using var reader = await ReaderFactory.OpenAsyncReader(
+ stream,
+ cancellationToken: cts.Token
+ );
+ await reader.WriteAllToDirectoryAsync(SCRATCH_FILES_PATH, cancellationToken: cts.Token);
+ });
+ }
+
+ [Fact]
+ public async Task OpenAsyncReader_CallerProvidedStream_ShouldRemainOpenByDefault()
+ {
+ var archiveBytes = await File.ReadAllBytesAsync(
+ Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip")
+ );
+ var stream = new TestStream(new MemoryStream(archiveBytes));
+
+ try
+ {
+ await using (var reader = await ReaderFactory.OpenAsyncReader(stream))
+ {
+ Assert.True(await reader.MoveToNextEntryAsync());
+ }
+
+ Assert.False(stream.IsDisposed);
+ }
+ finally
+ {
+ stream.Dispose();
+ }
+ }
+
+ [Fact]
+ public async Task OpenAsyncArchive_CallerProvidedStream_ShouldRemainOpenByDefault()
+ {
+ var archiveBytes = await File.ReadAllBytesAsync(
+ Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip")
+ );
+ var stream = new TestStream(new MemoryStream(archiveBytes));
+
+ try
+ {
+ await using (var archive = await ArchiveFactory.OpenAsyncArchive(stream))
+ {
+ await foreach (var _ in archive.EntriesAsync)
+ {
+ break;
+ }
+ }
+
+ Assert.False(stream.IsDisposed);
+ }
+ finally
+ {
+ await stream.DisposeAsync();
+ }
+ }
+
+ private static List ReadArchiveEntries(string archivePath)
+ {
+ using var archive = ArchiveFactory.OpenArchive(archivePath);
+ return archive
+ .Entries.Where(entry => !entry.IsDirectory)
+ .Select(entry =>
+ {
+ using var stream = entry.OpenEntryStream();
+ using var memory = new MemoryStream();
+ stream.CopyTo(memory);
+ return new EntrySnapshot(
+ entry.Key ?? string.Empty,
+ entry.Size,
+ entry.CompressionType,
+ Convert.ToBase64String(memory.ToArray())
+ );
+ })
+ .OrderBy(entry => entry.Key, StringComparer.Ordinal)
+ .ToList();
+ }
+
+ private static async Task> ReadArchiveEntriesAsync(string archivePath)
+ {
+ await using var archive = await ArchiveFactory.OpenAsyncArchive(archivePath);
+ var entries = new List();
+ await foreach (var entry in archive.EntriesAsync)
+ {
+ if (entry.IsDirectory)
+ {
+ continue;
+ }
+
+ await using var stream = await entry.OpenEntryStreamAsync();
+ using var memory = new MemoryStream();
+ await stream.CopyToAsync(memory);
+ entries.Add(
+ new EntrySnapshot(
+ entry.Key ?? string.Empty,
+ entry.Size,
+ entry.CompressionType,
+ Convert.ToBase64String(memory.ToArray())
+ )
+ );
+ }
+
+ return entries.OrderBy(entry => entry.Key, StringComparer.Ordinal).ToList();
+ }
+
+ private static List ReadReaderEntries(string archivePath)
+ {
+ using var stream = File.OpenRead(archivePath);
+ using var reader = ReaderFactory.OpenReader(stream);
+ var entries = new List();
+ while (reader.MoveToNextEntry())
+ {
+ if (reader.Entry.IsDirectory)
+ {
+ continue;
+ }
+
+ using var memory = new MemoryStream();
+ reader.WriteEntryTo(memory);
+ entries.Add(
+ new EntrySnapshot(
+ reader.Entry.Key ?? string.Empty,
+ reader.Entry.Size,
+ reader.Entry.CompressionType,
+ Convert.ToBase64String(memory.ToArray())
+ )
+ );
+ }
+
+ return entries.OrderBy(entry => entry.Key, StringComparer.Ordinal).ToList();
+ }
+
+ private static async Task> ReadReaderEntriesAsync(string archivePath)
+ {
+ await using var stream = File.OpenRead(archivePath);
+ await using var reader = await ReaderFactory.OpenAsyncReader(stream);
+ var entries = new List();
+ while (await reader.MoveToNextEntryAsync())
+ {
+ if (reader.Entry.IsDirectory)
+ {
+ continue;
+ }
+
+ using var memory = new MemoryStream();
+ await reader.WriteEntryToAsync(memory);
+ entries.Add(
+ new EntrySnapshot(
+ reader.Entry.Key ?? string.Empty,
+ reader.Entry.Size,
+ reader.Entry.CompressionType,
+ Convert.ToBase64String(memory.ToArray())
+ )
+ );
+ }
+
+ return entries.OrderBy(entry => entry.Key, StringComparer.Ordinal).ToList();
+ }
+
+ private static byte[] CreateLargeTarArchive()
+ {
+ using var stream = new MemoryStream();
+ using (
+ var writer = WriterFactory.OpenWriter(
+ stream,
+ ArchiveType.Tar,
+ new WriterOptions(CompressionType.None)
+ )
+ )
+ {
+ writer.Write("large.bin", new MemoryStream(new byte[64 * 1024]));
+ }
+ return stream.ToArray();
+ }
+
+ private sealed record EntrySnapshot(
+ string Key,
+ long Size,
+ CompressionType CompressionType,
+ string Content
+ );
+
+ private sealed class CancelAfterBytesReadStream(
+ Stream stream,
+ CancellationTokenSource cancellationTokenSource,
+ long cancelAfterBytes
+ ) : Stream
+ {
+ private long _bytesRead;
+
+ public override bool CanRead => stream.CanRead;
+ public override bool CanSeek => stream.CanSeek;
+ public override bool CanWrite => false;
+ public override long Length => stream.Length;
+ public override long Position
+ {
+ get => stream.Position;
+ set => stream.Position = value;
+ }
+
+ public override void Flush() => stream.Flush();
+
+ public override int Read(byte[] buffer, int offset, int count) =>
+ throw new NotSupportedException("Use async reads for this test stream.");
+
+ public override async ValueTask ReadAsync(
+ Memory buffer,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var read = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
+ _bytesRead = read;
+ if (_bytesRead > cancelAfterBytes)
+ {
+ cancellationTokenSource.Cancel();
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+
+ return read;
+ }
+
+ public override Task ReadAsync(
+ byte[] buffer,
+ int offset,
+ int count,
+ CancellationToken cancellationToken
+ ) => ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ stream.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ public override async ValueTask DisposeAsync()
+ {
+ await stream.DisposeAsync().ConfigureAwait(false);
+ await base.DisposeAsync().ConfigureAwait(false);
+ }
+
+ public override long Seek(long offset, SeekOrigin origin) => stream.Seek(offset, origin);
+
+ public override void SetLength(long value) => throw new NotSupportedException();
+
+ public override void Write(byte[] buffer, int offset, int count) =>
+ throw new NotSupportedException();
+ }
+}
+#endif