Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
6732e3bc39 Fix XZStream to support non-seekable streams
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-13 15:54:24 +00:00
copilot-swe-agent[bot]
ee98b5565b Fix XZStream to support non-seekable streams
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-13 15:49:14 +00:00
copilot-swe-agent[bot]
88e23d95ad Initial plan 2026-02-13 15:43:26 +00:00
3 changed files with 56 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Xz;
@@ -12,9 +13,21 @@ namespace SharpCompress.Compressors.Xz;
public sealed partial class XZStream : XZReadOnlyStream
{
public XZStream(Stream baseStream)
: base(baseStream)
: base(WrapIfNeeded(baseStream))
{
_baseStream = baseStream;
_baseStream = BaseStream;
}
private static Stream WrapIfNeeded(Stream stream)
{
// XZ format requires position tracking for padding calculations.
// For non-seekable streams, wrap with SharpCompressStream which provides
// limited backward seeking via a ring buffer.
if (!stream.CanSeek)
{
return SharpCompressStream.Create(stream);
}
return stream;
}
protected override void Dispose(bool disposing)

View File

@@ -34,4 +34,24 @@ public class XzStreamAsyncTests : XzTestsBase
var uncompressed = await sr.ReadToEndAsync().ConfigureAwait(false);
Assert.Equal(OriginalIndexed, uncompressed);
}
[Fact]
public async ValueTask CanReadNonSeekableStreamAsync()
{
var nonSeekable = new ForwardOnlyStream(new MemoryStream(Compressed));
var xz = new XZStream(nonSeekable);
using var sr = new StreamReader(new AsyncOnlyStream(xz));
var uncompressed = await sr.ReadToEndAsync().ConfigureAwait(false);
Assert.Equal(Original, uncompressed);
}
[Fact]
public async ValueTask CanReadNonSeekableEmptyStreamAsync()
{
var nonSeekable = new ForwardOnlyStream(new MemoryStream(CompressedEmpty));
var xz = new XZStream(nonSeekable);
using var sr = new StreamReader(new AsyncOnlyStream(xz));
var uncompressed = await sr.ReadToEndAsync().ConfigureAwait(false);
Assert.Equal(OriginalEmpty, uncompressed);
}
}

View File

@@ -1,5 +1,6 @@
using System.IO;
using SharpCompress.Compressors.Xz;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test.Xz;
@@ -32,4 +33,24 @@ public class XzStreamTests : XzTestsBase
var uncompressed = sr.ReadToEnd();
Assert.Equal(OriginalIndexed, uncompressed);
}
[Fact]
public void CanReadNonSeekableStream()
{
var nonSeekable = new ForwardOnlyStream(new MemoryStream(Compressed));
var xz = new XZStream(nonSeekable);
using var sr = new StreamReader(xz);
var uncompressed = sr.ReadToEnd();
Assert.Equal(Original, uncompressed);
}
[Fact]
public void CanReadNonSeekableEmptyStream()
{
var nonSeekable = new ForwardOnlyStream(new MemoryStream(CompressedEmpty));
var xz = new XZStream(nonSeekable);
using var sr = new StreamReader(xz);
var uncompressed = sr.ReadToEnd();
Assert.Equal(OriginalEmpty, uncompressed);
}
}