mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-04-05 21:51:09 +00:00
Merge remote-tracking branch 'origin/release'
# Conflicts: # src/SharpCompress/Common/Zip/ZipFilePart.cs # src/SharpCompress/IO/SharpCompressStream.cs
This commit is contained in:
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,19 @@ internal abstract partial class ZipFilePart
|
||||
{
|
||||
throw new NotSupportedException("LZMA with pkware encryption.");
|
||||
}
|
||||
// When the uncompressed size is known to be zero, skip remaining compressed
|
||||
// bytes (required for streaming reads) and return an empty stream.
|
||||
// Bit1 (EOS marker flag) means the output size is not stored in the header
|
||||
// (the LZMA stream itself contains an end-of-stream marker instead), so we
|
||||
// only short-circuit when the size is explicitly known to be zero.
|
||||
if (
|
||||
!FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
||||
&& Header.UncompressedSize == 0
|
||||
)
|
||||
{
|
||||
await stream.SkipAsync(cancellationToken).ConfigureAwait(false);
|
||||
return Stream.Null;
|
||||
}
|
||||
var buffer = new byte[4];
|
||||
await stream.ReadFullyAsync(buffer, 0, 4, cancellationToken).ConfigureAwait(false);
|
||||
var propsSize = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(2, 2));
|
||||
|
||||
@@ -17,12 +17,6 @@ internal sealed partial class SeekableSharpCompressStream : SharpCompressStream
|
||||
/// </summary>
|
||||
public override bool LeaveStreamOpen { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to throw an exception when Dispose is called.
|
||||
/// Useful for testing to ensure streams are not disposed prematurely.
|
||||
/// </summary>
|
||||
public override bool ThrowOnDispose { get; set; }
|
||||
|
||||
public SeekableSharpCompressStream(Stream stream, bool leaveStreamOpen = false)
|
||||
: base(Null, true, false, null)
|
||||
{
|
||||
|
||||
@@ -7,13 +7,66 @@ namespace SharpCompress.IO;
|
||||
public 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.
|
||||
/// Creates a <see cref="SharpCompressStream"/> that acts as a zero-overhead passthrough wrapper
|
||||
/// around <paramref name="stream"/> without taking ownership of it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is a thin wrapper: all reads, writes, and seeks are forwarded directly to the underlying
|
||||
/// stream with no ring-buffer overhead. <see cref="Stream.CanSeek"/> delegates to the underlying
|
||||
/// stream's own value.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The resulting stream does <b>not</b> support <see cref="StartRecording"/>, <see cref="Rewind()"/>,
|
||||
/// or <see cref="StopRecording"/>. Call <see cref="Create"/> on the passthrough stream to obtain
|
||||
/// a recording-capable wrapper when needed.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Because the stream does not take ownership, the underlying stream is <b>never</b> disposed when
|
||||
/// this wrapper is disposed. Use this when you need to satisfy an API that expects a
|
||||
/// <see cref="SharpCompressStream"/> without transferring lifetime responsibility.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream to wrap. Must not be <see langword="null"/>.</param>
|
||||
/// <returns>
|
||||
/// A passthrough <see cref="SharpCompressStream"/> that does not dispose <paramref name="stream"/>.
|
||||
/// </returns>
|
||||
public static SharpCompressStream CreateNonDisposing(Stream stream) =>
|
||||
new(stream, leaveStreamOpen: true, passthrough: true, bufferSize: null);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SharpCompressStream"/> that supports recording and rewinding over
|
||||
/// <paramref name="stream"/>, choosing the most efficient strategy based on the stream's
|
||||
/// capabilities.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Seekable streams</b> — wraps in a thin delegate that calls the underlying
|
||||
/// stream's native <see cref="Stream.Seek"/> directly. No ring buffer is allocated.
|
||||
/// <see cref="StartRecording"/> stores the current position; <see cref="Rewind()"/> seeks
|
||||
/// back to it.</para>
|
||||
/// <para><b>Non-seekable streams</b> (network streams, compressed streams, pipes) — allocates
|
||||
/// a ring buffer of <paramref name="bufferSize"/> bytes. All bytes read from the underlying
|
||||
/// stream are kept in the ring buffer so that <see cref="Rewind()"/> 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 <see cref="InvalidOperationException"/>; increase
|
||||
/// <paramref name="bufferSize"/> or <see cref="Common.Constants.RewindableBufferSize"/> to
|
||||
/// avoid this.</para>
|
||||
/// <para><b>Already-wrapped streams</b> — if <paramref name="stream"/> is already a
|
||||
/// <see cref="SharpCompressStream"/> (or a stack that contains one), it is returned as-is to
|
||||
/// prevent double-wrapping and double-buffering.</para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream to wrap. Must not be <see langword="null"/>.</param>
|
||||
/// <param name="bufferSize">
|
||||
/// Size in bytes of the ring buffer allocated for non-seekable streams.
|
||||
/// Defaults to <see cref="Common.Constants.RewindableBufferSize"/> (81 920 bytes) when
|
||||
/// <see langword="null"/>. Has no effect when <paramref name="stream"/> is seekable, because
|
||||
/// no ring buffer is needed in that case.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="SharpCompressStream"/> wrapping <paramref name="stream"/>. The returned instance
|
||||
/// owns the stream and will dispose it unless the original source was a non-disposing passthrough
|
||||
/// wrapper.
|
||||
/// </returns>
|
||||
public static SharpCompressStream Create(Stream stream, int? bufferSize = null)
|
||||
{
|
||||
var rewindableBufferSize = bufferSize ?? Constants.RewindableBufferSize;
|
||||
@@ -54,6 +107,6 @@ public 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public 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.
|
||||
/// </summary>
|
||||
public virtual bool ThrowOnDispose { get; set; }
|
||||
internal bool ThrowOnDispose { get; set; }
|
||||
|
||||
public SharpCompressStream(Stream stream)
|
||||
{
|
||||
@@ -193,7 +193,7 @@ public 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
|
||||
|
||||
Reference in New Issue
Block a user