From 7162b66ccb5344d56ddb0455cac0f0dcdad601fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Jul 2026 07:41:40 +0000 Subject: [PATCH 1/2] Initial plan From abcbdb4aa3c47d512221712a5021b46433c95f29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Jul 2026 07:57:09 +0000 Subject: [PATCH 2/2] Fix SeekableSharpCompressStream.StopRecording() to rewind to recorded position When SeekableSharpCompressStream.StopRecording() was called (e.g. after the tar probe in GZipFactory.TryOpenReader), it cleared _recordedPosition without seeking back to it. This left the underlying FileStream at the advanced position reached during the probe, so the subsequent GZipReader would start reading from the wrong offset, producing Key=null and 0 extracted bytes. Fix: seek back to _recordedPosition before clearing it, matching the behavior of the non-seekable SharpCompressStream.StopRecording() which rewinds _logicalPosition. Also enhance the existing GZip_ReaderFactory_FlatGZip test to actually verify the extracted content (not just MoveToNextEntry returns true), and add an async variant. Closes #1391 --- .../IO/SeekableSharpCompressStream.cs | 11 ++++++- src/SharpCompress/packages.lock.json | 12 ++++---- .../SharpCompress.AotSmoke/packages.lock.json | 24 +++++++-------- .../GZip/GZipReaderAsyncTests.cs | 30 +++++++++++++++++++ .../GZip/GZipReaderTests.cs | 6 ++++ 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/SharpCompress/IO/SeekableSharpCompressStream.cs b/src/SharpCompress/IO/SeekableSharpCompressStream.cs index 73d23aeed..15e95fd82 100644 --- a/src/SharpCompress/IO/SeekableSharpCompressStream.cs +++ b/src/SharpCompress/IO/SeekableSharpCompressStream.cs @@ -67,7 +67,16 @@ public override void Rewind(bool stopRecording = false) public override void StartRecording(int? minBufferSize = null) => _recordedPosition = _stream.Position; - public override void StopRecording() => _recordedPosition = null; + public override void StopRecording() + { + if (_recordedPosition.HasValue) + { + // Seek back to the recording anchor position, matching the behavior of the + // non-seekable SharpCompressStream.StopRecording() which rewinds _logicalPosition. + _stream.Seek(_recordedPosition.Value, SeekOrigin.Begin); + } + _recordedPosition = null; + } protected override void Dispose(bool disposing) { diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 78c97f48f..70b2818fe 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -339,9 +339,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "4Iw41e2h7I4t70SJcX2GCmbyKJIlA273Cfm9RJMM050/3VBejGAG1KcthP5Z2L6SQcbfbf6BhNWO26+ZG+GzMg==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -471,9 +471,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.28, )", - "resolved": "8.0.28", - "contentHash": "XMqgVjlLxLqWmEh3c49haXLQwsMNtvo6YscUaqfvEGfg1iA8hnYgkUVq3i9Zu9gKeNKMWiiZKVwZExc/qyEAsQ==" + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "HSBTfrkIZijz8z3ybLRKB7E8rHk4QQufFwpHa9fc5CMIgRhRzdn4mBGmlyXZqaueiMPtuJcnjresGvSTfaW8Mg==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", diff --git a/tests/SharpCompress.AotSmoke/packages.lock.json b/tests/SharpCompress.AotSmoke/packages.lock.json index 07db04d1a..28172cef7 100644 --- a/tests/SharpCompress.AotSmoke/packages.lock.json +++ b/tests/SharpCompress.AotSmoke/packages.lock.json @@ -4,15 +4,15 @@ "net10.0": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "4y+VsQOcs4EiTSINdCpCWi/aLRbIbGTxSezQXd8uGVhzbDRm1FNVTZDyCUQixE0+g9UFusvfxVcF68YYz7RzxA==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "tnG8ntt/Bk6odvHREnGLMo3PEiihy5iSlIFVp0JbIo00GKtNRt2k73eKZbPqR5yaJNIa3z8R86YLwbxfqpb17g==" }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "4Iw41e2h7I4t70SJcX2GCmbyKJIlA273Cfm9RJMM050/3VBejGAG1KcthP5Z2L6SQcbfbf6BhNWO26+ZG+GzMg==" + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -82,17 +82,17 @@ "net10.0/linux-x64": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.9, )", - "resolved": "10.0.9", - "contentHash": "4y+VsQOcs4EiTSINdCpCWi/aLRbIbGTxSezQXd8uGVhzbDRm1FNVTZDyCUQixE0+g9UFusvfxVcF68YYz7RzxA==", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "tnG8ntt/Bk6odvHREnGLMo3PEiihy5iSlIFVp0JbIo00GKtNRt2k73eKZbPqR5yaJNIa3z8R86YLwbxfqpb17g==", "dependencies": { - "runtime.linux-x64.Microsoft.DotNet.ILCompiler": "10.0.9" + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": "10.0.10" } }, "runtime.linux-x64.Microsoft.DotNet.ILCompiler": { "type": "Transitive", - "resolved": "10.0.9", - "contentHash": "45CVefG8S0eUKUJ4LBWOi8FOAgMJOP6exW9l5M9OjvQaGR7jvkokBK50XaZCsO66uLxABcuzvncV8A3YiJLUgw==" + "resolved": "10.0.10", + "contentHash": "WRjSRBfv6A6UjgjO8EQuLe9xqdICpkQx1hACUziCw4B2uGL+2jVhkFLq/G7rxRr3MGvqLo9B+nNdfIJ/5CYN7A==" } } } diff --git a/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs index 83d2b352b..a3da7e049 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs @@ -1,4 +1,5 @@ using System.IO; +using System.IO.Compression; using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.IO; @@ -37,4 +38,33 @@ public async ValueTask GZip_Reader_Generic2_Async() } } } + + [Fact] + public async ValueTask GZip_ReaderFactory_FlatGZip_Async() + { + var source = new byte[2048]; + for (var i = 0; i < source.Length; i++) + { + source[i] = 0xFF; + } + + var gzipPath = Path.Combine(SCRATCH_FILES_PATH, "Flat.bin.gz"); + using (var output = File.Create(gzipPath)) + await using (var gzip = new GZipStream(output, CompressionMode.Compress)) + { + await gzip.WriteAsync(source, 0, source.Length); + } + + await using Stream stream = File.OpenRead(gzipPath); + await using var reader = await ReaderFactory.OpenAsyncReader(stream); + Assert.IsType(reader); + Assert.True(await reader.MoveToNextEntryAsync()); + + using var ms = new MemoryStream(); + await reader.WriteEntryToAsync(ms); + Assert.Equal(source.Length, ms.Length); + Assert.Equal(source, ms.ToArray()); + + Assert.False(await reader.MoveToNextEntryAsync()); + } } diff --git a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs index fb3936a8f..ab870da2d 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs @@ -48,6 +48,12 @@ public void GZip_ReaderFactory_FlatGZip() using var reader = ReaderFactory.OpenReader(stream); Assert.IsType(reader); Assert.True(reader.MoveToNextEntry()); + + using var ms = new MemoryStream(); + reader.WriteEntryTo(ms); + Assert.Equal(source.Length, ms.Length); + Assert.Equal(source, ms.ToArray()); + Assert.False(reader.MoveToNextEntry()); } }