diff --git a/.vscode/settings.json b/.vscode/settings.json
index 07998539..1d13071f 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -25,5 +25,8 @@
"csharpier.enableDebugLogs": false,
"omnisharp.enableRoslynAnalyzers": true,
"omnisharp.enableEditorConfigSupport": true,
- "dotnet-test-explorer.testProjectPath": "tests/**/*.csproj"
+ "dotnet-test-explorer.testProjectPath": "tests/**/*.csproj",
+ "chat.tools.terminal.autoApprove": {
+ "dotnet csharpier": true
+ }
}
diff --git a/src/SharpCompress/IO/SeekableSharpCompressStream.cs b/src/SharpCompress/IO/SeekableSharpCompressStream.cs
index 28b67c4e..c49e5be5 100644
--- a/src/SharpCompress/IO/SeekableSharpCompressStream.cs
+++ b/src/SharpCompress/IO/SeekableSharpCompressStream.cs
@@ -16,12 +16,6 @@ internal sealed partial class SeekableSharpCompressStream : SharpCompressStream
///
public override bool LeaveStreamOpen { get; }
- ///
- /// Gets or sets whether to throw an exception when Dispose is called.
- /// Useful for testing to ensure streams are not disposed prematurely.
- ///
- public override bool ThrowOnDispose { get; set; }
-
public SeekableSharpCompressStream(Stream stream, bool leaveStreamOpen = false)
: base(Null, true, false, null)
{
diff --git a/src/SharpCompress/IO/SharpCompressStream.Async.cs b/src/SharpCompress/IO/SharpCompressStream.Async.cs
index e18a47a1..a36a9862 100644
--- a/src/SharpCompress/IO/SharpCompressStream.Async.cs
+++ b/src/SharpCompress/IO/SharpCompressStream.Async.cs
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace SharpCompress.IO;
-internal partial class SharpCompressStream
+public partial class SharpCompressStream
{
public override Task ReadAsync(
byte[] buffer,
diff --git a/src/SharpCompress/IO/SharpCompressStream.Create.cs b/src/SharpCompress/IO/SharpCompressStream.Create.cs
index 3a886a80..37d91ebf 100644
--- a/src/SharpCompress/IO/SharpCompressStream.Create.cs
+++ b/src/SharpCompress/IO/SharpCompressStream.Create.cs
@@ -4,16 +4,69 @@ using SharpCompress.Common;
namespace SharpCompress.IO;
-internal partial class SharpCompressStream
+public partial class SharpCompressStream
{
///
- /// Creates a SharpCompressStream that acts as a passthrough wrapper.
- /// No buffering is performed; CanSeek delegates to the underlying stream.
- /// The underlying stream will not be disposed when this stream is disposed.
+ /// Creates a that acts as a zero-overhead passthrough wrapper
+ /// around without taking ownership of it.
///
+ ///
+ ///
+ /// This is a thin wrapper: all reads, writes, and seeks are forwarded directly to the underlying
+ /// stream with no ring-buffer overhead. delegates to the underlying
+ /// stream's own value.
+ ///
+ ///
+ /// The resulting stream does not support , ,
+ /// or . Call on the passthrough stream to obtain
+ /// a recording-capable wrapper when needed.
+ ///
+ ///
+ /// Because the stream does not take ownership, the underlying stream is never disposed when
+ /// this wrapper is disposed. Use this when you need to satisfy an API that expects a
+ /// without transferring lifetime responsibility.
+ ///
+ ///
+ /// The underlying stream to wrap. Must not be .
+ ///
+ /// A passthrough that does not dispose .
+ ///
public static SharpCompressStream CreateNonDisposing(Stream stream) =>
new(stream, leaveStreamOpen: true, passthrough: true, bufferSize: null);
+ ///
+ /// Creates a that supports recording and rewinding over
+ /// , choosing the most efficient strategy based on the stream's
+ /// capabilities.
+ ///
+ ///
+ /// Seekable streams — wraps in a thin delegate that calls the underlying
+ /// stream's native directly. No ring buffer is allocated.
+ /// stores the current position; seeks
+ /// back to it.
+ /// Non-seekable streams (network streams, compressed streams, pipes) — allocates
+ /// a ring buffer of bytes. All bytes read from the underlying
+ /// stream are kept in the ring buffer so that can replay them without
+ /// re-reading the underlying stream. If more bytes have been read than the ring buffer can hold,
+ /// a subsequent rewind will throw ; increase
+ /// or to
+ /// avoid this.
+ /// Already-wrapped streams — if is already a
+ /// (or a stack that contains one), it is returned as-is to
+ /// prevent double-wrapping and double-buffering.
+ ///
+ /// The underlying stream to wrap. Must not be .
+ ///
+ /// Size in bytes of the ring buffer allocated for non-seekable streams.
+ /// Defaults to (81 920 bytes) when
+ /// . Has no effect when is seekable, because
+ /// no ring buffer is needed in that case.
+ ///
+ ///
+ /// A wrapping . The returned instance
+ /// owns the stream and will dispose it unless the original source was a non-disposing passthrough
+ /// wrapper.
+ ///
public static SharpCompressStream Create(Stream stream, int? bufferSize = null)
{
var rewindableBufferSize = bufferSize ?? Constants.RewindableBufferSize;
@@ -54,6 +107,6 @@ internal partial class SharpCompressStream
// For non-seekable streams, create a SharpCompressStream with rolling buffer
// to allow limited backward seeking (required by decompressors that over-read)
- return new SharpCompressStream(stream, false, false, bufferSize);
+ return new SharpCompressStream(stream, false, false, rewindableBufferSize);
}
}
diff --git a/src/SharpCompress/IO/SharpCompressStream.cs b/src/SharpCompress/IO/SharpCompressStream.cs
index a7c09072..aef14790 100644
--- a/src/SharpCompress/IO/SharpCompressStream.cs
+++ b/src/SharpCompress/IO/SharpCompressStream.cs
@@ -4,7 +4,7 @@ using SharpCompress.Common;
namespace SharpCompress.IO;
-internal partial class SharpCompressStream : Stream, IStreamStack
+public partial class SharpCompressStream : Stream, IStreamStack
{
public virtual Stream BaseStream() => stream;
@@ -38,7 +38,7 @@ internal partial class SharpCompressStream : Stream, IStreamStack
/// Gets or sets whether to throw an exception when Dispose is called.
/// Useful for testing to ensure streams are not disposed prematurely.
///
- public virtual bool ThrowOnDispose { get; set; }
+ internal bool ThrowOnDispose { get; set; }
public SharpCompressStream(Stream stream)
{
@@ -181,7 +181,7 @@ internal partial class SharpCompressStream : Stream, IStreamStack
// Ensure ring buffer exists
if (_ringBuffer is null)
{
- _ringBuffer = new RingBuffer(Constants.BufferSize);
+ _ringBuffer = new RingBuffer(Constants.RewindableBufferSize);
}
// Mark current position as recording anchor
diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json
index 1f290aa4..972b9be4 100644
--- a/tests/SharpCompress.Test/packages.lock.json
+++ b/tests/SharpCompress.Test/packages.lock.json
@@ -309,30 +309,6 @@
}
}
},
- ".NETFramework,Version=v4.8/win-x86": {
- "Microsoft.Win32.Registry": {
- "type": "Transitive",
- "resolved": "5.0.0",
- "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
- "dependencies": {
- "System.Security.AccessControl": "5.0.0",
- "System.Security.Principal.Windows": "5.0.0"
- }
- },
- "System.Security.AccessControl": {
- "type": "Transitive",
- "resolved": "5.0.0",
- "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
- "dependencies": {
- "System.Security.Principal.Windows": "5.0.0"
- }
- },
- "System.Security.Principal.Windows": {
- "type": "Transitive",
- "resolved": "5.0.0",
- "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
- }
- },
"net10.0": {
"AwesomeAssertions": {
"type": "Direct",
@@ -545,13 +521,6 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
}
- },
- "net10.0/win-x86": {
- "Microsoft.Win32.Registry": {
- "type": "Transitive",
- "resolved": "5.0.0",
- "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg=="
- }
}
}
}
\ No newline at end of file