diff --git a/src/SharpCompress/Common/AsyncBinaryReader.cs b/src/SharpCompress/Common/AsyncBinaryReader.cs index f2cdb4a6..77cc6f64 100644 --- a/src/SharpCompress/Common/AsyncBinaryReader.cs +++ b/src/SharpCompress/Common/AsyncBinaryReader.cs @@ -18,10 +18,6 @@ namespace SharpCompress.Common { _originalStream = stream ?? throw new ArgumentNullException(nameof(stream)); _leaveOpen = leaveOpen; - - // Use the stream directly without wrapping in BufferedStream - // BufferedStream uses synchronous Read internally which doesn't work with async-only streams - // SharpCompress uses SharpCompressStream for buffering which supports true async reads _stream = stream; } diff --git a/src/SharpCompress/IO/BufferedSubStream.cs b/src/SharpCompress/IO/BufferedSubStream.cs index 7a51bb0f..6d9e6b42 100755 --- a/src/SharpCompress/IO/BufferedSubStream.cs +++ b/src/SharpCompress/IO/BufferedSubStream.cs @@ -1,47 +1,18 @@ using System; using System.Buffers; using System.IO; -using System.Threading; -using System.Threading.Tasks; namespace SharpCompress.IO; -internal class BufferedSubStream : SharpCompressStream, IStreamStack +internal class BufferedSubStream(Stream stream, long origin, long bytesToRead) + : SharpCompressStream(stream, throwOnDispose: false) { -#if DEBUG_STREAMS - long IStreamStack.InstanceId { get; set; } -#endif - - Stream IStreamStack.BaseStream() => base.Stream; - - public BufferedSubStream(Stream stream, long origin, long bytesToRead) - : base(stream, leaveOpen: true, throwOnDispose: false) - { -#if DEBUG_STREAMS - this.DebugConstruct(typeof(BufferedSubStream)); -#endif - this.origin = origin; - this.BytesLeftToRead = bytesToRead; - } - - protected override void Dispose(bool disposing) - { -#if DEBUG_STREAMS - this.DebugDispose(typeof(BufferedSubStream)); -#endif - if (disposing) - { - ArrayPool.Shared.Return(_cache); - } - base.Dispose(disposing); - } - private int _cacheOffset; private int _cacheLength; - private readonly byte[] _cache = ArrayPool.Shared.Rent(32 << 10); - private long origin; - private long BytesLeftToRead { get; set; } + private readonly byte[] _cache = ArrayPool.Shared.Rent(32 << 10); + + private long BytesLeftToRead { get; set; } = bytesToRead; public override bool CanRead => true; @@ -51,7 +22,7 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack public override void Flush() { } - public override long Length => BytesLeftToRead + _cacheLength - _cacheOffset; + public override long Length => BytesLeftToRead; public override long Position { @@ -59,55 +30,32 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack set => throw new NotSupportedException(); } - private void RefillCache() - { - var count = (int)Math.Min(BytesLeftToRead, _cache.Length); - _cacheOffset = 0; - if (count == 0) - { - _cacheLength = 0; - return; - } - Stream.Position = origin; - _cacheLength = Stream.Read(_cache, 0, count); - origin += _cacheLength; - BytesLeftToRead -= _cacheLength; - } - - private async ValueTask RefillCacheAsync(CancellationToken cancellationToken) - { - var count = (int)Math.Min(BytesLeftToRead, _cache.Length); - _cacheOffset = 0; - if (count == 0) - { - _cacheLength = 0; - return; - } - Stream.Position = origin; - _cacheLength = await Stream - .ReadAsync(_cache, 0, count, cancellationToken) - .ConfigureAwait(false); - origin += _cacheLength; - BytesLeftToRead -= _cacheLength; - } - public override int Read(byte[] buffer, int offset, int count) { - if (count > Length) + if (count > BytesLeftToRead) { - count = (int)Length; + count = (int)BytesLeftToRead; } if (count > 0) { - if (_cacheOffset == _cacheLength) + if (_cacheLength == 0) { - RefillCache(); + _cacheOffset = 0; + Stream.Position = origin; + _cacheLength = Stream.Read(_cache, 0, _cache.Length); + origin += _cacheLength; + } + + if (count > _cacheLength) + { + count = _cacheLength; } - count = Math.Min(count, _cacheLength - _cacheOffset); Buffer.BlockCopy(_cache, _cacheOffset, buffer, offset, count); _cacheOffset += count; + _cacheLength -= count; + BytesLeftToRead -= count; } return count; @@ -115,77 +63,41 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack public override int ReadByte() { - if (_cacheOffset == _cacheLength) + if (BytesLeftToRead == 0) { - RefillCache(); - if (_cacheLength == 0) - { - return -1; - } + return -1; } - return _cache[_cacheOffset++]; + if (_cacheLength == 0) + { + _cacheOffset = 0; + Stream.Position = origin; + _cacheLength = Stream.Read(_cache, 0, _cache.Length); + origin += _cacheLength; + } + + int value = _cache[_cacheOffset]; + + _cacheOffset++; + _cacheLength--; + BytesLeftToRead--; + + return value; } - public override async Task ReadAsync( - byte[] buffer, - int offset, - int count, - CancellationToken cancellationToken - ) - { - if (count > Length) - { - count = (int)Length; - } - - if (count > 0) - { - if (_cacheOffset == _cacheLength) - { - await RefillCacheAsync(cancellationToken).ConfigureAwait(false); - } - - count = Math.Min(count, _cacheLength - _cacheOffset); - Buffer.BlockCopy(_cache, _cacheOffset, buffer, offset, count); - _cacheOffset += count; - } - - return count; - } - -#if !LEGACY_DOTNET - public override async ValueTask ReadAsync( - Memory buffer, - CancellationToken cancellationToken = default - ) - { - var count = buffer.Length; - if (count > Length) - { - count = (int)Length; - } - - if (count > 0) - { - if (_cacheOffset == _cacheLength) - { - await RefillCacheAsync(cancellationToken).ConfigureAwait(false); - } - - count = Math.Min(count, _cacheLength - _cacheOffset); - _cache.AsSpan(_cacheOffset, count).CopyTo(buffer.Span); - _cacheOffset += count; - } - - return count; - } -#endif - 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) => throw new NotSupportedException(); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + ArrayPool.Shared.Return(_cache); + } + base.Dispose(disposing); + } } diff --git a/tests/SharpCompress.Performance/Benchmarks.cs b/tests/SharpCompress.Performance/Benchmarks.cs index fa01a15e..44794cab 100644 --- a/tests/SharpCompress.Performance/Benchmarks.cs +++ b/tests/SharpCompress.Performance/Benchmarks.cs @@ -7,8 +7,8 @@ public class Benchmarks string filename = $"/Users/adam/Downloads/original.7z"; [Benchmark] - public async Task SharpCompress_0_44_Original() + public void SharpCompress_0_44_Original() { - await Extractor.GetFiles(filename); + Extractor.GetFiles(filename); } } diff --git a/tests/SharpCompress.Performance/Extractor.cs b/tests/SharpCompress.Performance/Extractor.cs index ca9eea09..cf19427f 100644 --- a/tests/SharpCompress.Performance/Extractor.cs +++ b/tests/SharpCompress.Performance/Extractor.cs @@ -9,7 +9,7 @@ public static class Extractor { private const char Delimiter = '/'; - public static async Task> GetFiles(string filename) + public static IEnumerable GetFiles(string filename) { IArchive? archive = null; FileStream? fileStream = null; @@ -37,9 +37,9 @@ public static class Extractor if (entryKey is null) continue; - await using EntryStream source = reader.OpenEntryStream(); - await using MemoryStream memoryStream = new(); - await source.CopyToAsync(memoryStream); + using EntryStream source = reader.OpenEntryStream(); + using MemoryStream memoryStream = new(); + source.CopyTo(memoryStream); var data = memoryStream.ToArray(); var nameParts = entryKey.Split(Delimiter); @@ -51,7 +51,7 @@ public static class Extractor { if (fileStream is not null) { - await fileStream.DisposeAsync(); + fileStream.Dispose(); } archive?.Dispose(); } diff --git a/tests/SharpCompress.Performance/Program.cs b/tests/SharpCompress.Performance/Program.cs index ae9c5b75..ca5927a8 100644 --- a/tests/SharpCompress.Performance/Program.cs +++ b/tests/SharpCompress.Performance/Program.cs @@ -7,7 +7,7 @@ using SharpCompress.Archives; using SharpCompress.Performance; using SharpCompress.Readers; using SharpCompress.Test; - +/* var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf( "SharpCompress.Performance", StringComparison.OrdinalIgnoreCase @@ -23,12 +23,12 @@ using (var __ = JetbrainsProfiler.Cpu($"/Users/adam/git/temp")) string filename = $"/Users/adam/Downloads/original.7z"; for (int i = 0; i < 10; i++) { - await Extractor.GetFiles(filename); + Extractor.GetFiles(filename); Console.WriteLine(i); } Console.WriteLine("Still running..."); } -await Task.Delay(500); +await Task.Delay(500);*/ -//BenchmarkRunner.Run(); +BenchmarkRunner.Run();