move static methods on SCStream

This commit is contained in:
Adam Hathcock
2026-02-04 17:03:56 +00:00
parent 73fd2d70ba
commit 7231b7b35c
7 changed files with 75 additions and 67 deletions

View File

@@ -72,9 +72,9 @@ internal sealed partial class StreamingZipHeaderFactory
)
{
_headerFactory = headerFactory;
// Use EnsureSeekable to avoid double-wrapping if stream is already a SharpCompressStream,
// Use Create to avoid double-wrapping if stream is already a SharpCompressStream,
// and to preserve seekability for DataDescriptorStream which needs to seek backward
_sharpCompressStream = SharpCompressStream.EnsureSeekable(stream);
_sharpCompressStream = SharpCompressStream.Create(stream);
_reader = new AsyncBinaryReader(_sharpCompressStream, leaveOpen: true);
_cancellationToken = cancellationToken;
}

View File

@@ -20,9 +20,9 @@ internal partial class StreamingZipHeaderFactory : ZipHeaderFactory
internal IEnumerable<ZipHeader> ReadStreamHeader(Stream stream)
{
// Use EnsureSeekable to avoid double-wrapping if stream is already a SharpCompressStream,
// Use Create to avoid double-wrapping if stream is already a SharpCompressStream,
// and to preserve seekability for DataDescriptorStream which needs to seek backward
var sharpCompressStream = SharpCompressStream.EnsureSeekable(stream);
var sharpCompressStream = SharpCompressStream.Create(stream);
var reader = new BinaryReader(
sharpCompressStream,
System.Text.Encoding.Default,

View File

@@ -0,0 +1,65 @@
using System;
using System.IO;
using SharpCompress.Common;
namespace SharpCompress.IO;
internal partial class SharpCompressStream
{
/// <summary>
/// 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.
/// </summary>
public static SharpCompressStream CreateNonDisposing(Stream stream) =>
new(stream, leaveStreamOpen: true, passthrough: true);
public static SharpCompressStream Create(
Stream stream,
int? rewindableBufferSize = null
)
{
int bufferSize = rewindableBufferSize ?? Constants.RewindableBufferSize;
// If it's a passthrough SharpCompressStream, unwrap it and create proper seekable wrapper
if (stream is SharpCompressStream sharpCompressStream)
{
if (sharpCompressStream._isPassthrough)
{
// Unwrap the passthrough and create appropriate wrapper
var underlying = sharpCompressStream.stream;
if (underlying.CanSeek)
{
// Create SeekableSharpCompressStream that preserves LeaveStreamOpen
return new SeekableSharpCompressStream(underlying)
{
LeaveStreamOpen = true, // Preserve non-disposing behavior
};
}
// Non-seekable underlying stream - wrap with rolling buffer
return new SharpCompressStream(underlying, bufferSize) { LeaveStreamOpen = true };
}
// Not passthrough - return as-is
return sharpCompressStream;
}
// Check if stream is wrapping a SharpCompressStream (e.g., via IStreamStack)
if (stream is IStreamStack streamStack)
{
var underlying = streamStack.GetStream<SharpCompressStream>();
if (underlying is not null)
{
return underlying;
}
}
if (stream.CanSeek)
{
return new SeekableSharpCompressStream(stream);
}
// 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, bufferSize);
}
}

View File

@@ -76,14 +76,6 @@ internal partial class SharpCompressStream : Stream, IStreamStack
_logicalPosition = 0;
}
/// <summary>
/// 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.
/// </summary>
public static SharpCompressStream CreateNonDisposing(Stream stream) =>
new(stream, leaveStreamOpen: true, passthrough: true);
/// <summary>
/// Gets whether the stream is actively recording reads to the ring buffer.
/// </summary>
@@ -121,7 +113,7 @@ internal partial class SharpCompressStream : Stream, IStreamStack
if (_isPassthrough)
{
throw new InvalidOperationException(
"Rewind cannot be called on a passthrough stream. Use EnsureSeekable() first."
"Rewind cannot be called on a passthrough stream. Use Create() first."
);
}
@@ -160,7 +152,7 @@ internal partial class SharpCompressStream : Stream, IStreamStack
if (_isPassthrough)
{
throw new InvalidOperationException(
"StopRecording cannot be called on a passthrough stream. Use EnsureSeekable() first."
"StopRecording cannot be called on a passthrough stream. Use Create() first."
);
}
if (!IsRecording)
@@ -180,61 +172,12 @@ internal partial class SharpCompressStream : Stream, IStreamStack
// (frozen recording mode) until Rewind(stopRecording: true) is called
}
public static SharpCompressStream EnsureSeekable(
Stream stream,
int? rewindableBufferSize = null
)
{
int bufferSize = rewindableBufferSize ?? Constants.RewindableBufferSize;
// If it's a passthrough SharpCompressStream, unwrap it and create proper seekable wrapper
if (stream is SharpCompressStream sharpCompressStream)
{
if (sharpCompressStream._isPassthrough)
{
// Unwrap the passthrough and create appropriate wrapper
var underlying = sharpCompressStream.stream;
if (underlying.CanSeek)
{
// Create SeekableSharpCompressStream that preserves LeaveStreamOpen
return new SeekableSharpCompressStream(underlying)
{
LeaveStreamOpen = true, // Preserve non-disposing behavior
};
}
// Non-seekable underlying stream - wrap with rolling buffer
return new SharpCompressStream(underlying, bufferSize) { LeaveStreamOpen = true };
}
// Not passthrough - return as-is
return sharpCompressStream;
}
// Check if stream is wrapping a SharpCompressStream (e.g., via IStreamStack)
if (stream is IStreamStack streamStack)
{
var underlying = streamStack.GetStream<SharpCompressStream>();
if (underlying is not null)
{
return underlying;
}
}
if (stream.CanSeek)
{
return new SeekableSharpCompressStream(stream);
}
// 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, bufferSize);
}
public virtual void StartRecording()
{
if (_isPassthrough)
{
throw new InvalidOperationException(
"StartRecording cannot be called on a passthrough stream. Use EnsureSeekable() first."
"StartRecording cannot be called on a passthrough stream. Use Create() first."
);
}
if (IsRecording)

View File

@@ -54,7 +54,7 @@ public static partial class ReaderFactory
stream.NotNull(nameof(stream));
options ??= new ReaderOptions() { LeaveStreamOpen = false };
var sharpCompressStream = SharpCompressStream.EnsureSeekable(
var sharpCompressStream = SharpCompressStream.Create(
stream,
options.RewindableBufferSize
);

View File

@@ -34,7 +34,7 @@ public static partial class ReaderFactory
stream.NotNull(nameof(stream));
options ??= new ReaderOptions() { LeaveStreamOpen = false };
var sharpCompressStream = SharpCompressStream.EnsureSeekable(
var sharpCompressStream = SharpCompressStream.Create(
stream,
options.RewindableBufferSize
);

View File

@@ -57,7 +57,7 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
{
stream.NotNull(nameof(stream));
options = options ?? new ReaderOptions();
var sharpCompressStream = SharpCompressStream.EnsureSeekable(
var sharpCompressStream = SharpCompressStream.Create(
stream,
options.RewindableBufferSize
);