// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#nullable enable
using System.Runtime.CompilerServices;
namespace System.IO
/// away from paths during normalization, but if we see such a path at this point it should be
/// normalized and has retained the final characters. (Typically from one of the *Info classes)
/// </summary>
- internal static string EnsureExtendedPrefixIfNeeded(string path)
+ /// TODO: add atribute [return: NotNullIfNotNull("path")]
+ internal static string? EnsureExtendedPrefixIfNeeded(string? path)
{
if (path != null && (path.Length >= MaxShortPath || EndsWithPeriodOrSpace(path)))
{
public override bool CanWrite { get { throw null; } }
public override long Length { get { throw null; } }
public override long Position { get { throw null; } set { } }
- public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) { throw null; }
- public override System.IAsyncResult BeginWrite(byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) { throw null; }
+ public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? asyncCallback, object? asyncState) { throw null; }
+ public override System.IAsyncResult BeginWrite(byte[] array, int offset, int count, System.AsyncCallback? asyncCallback, object? asyncState) { throw null; }
public override void CopyTo(System.IO.Stream destination, int bufferSize) { }
public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) { throw null; }
protected override void Dispose(bool disposing) { }
public override bool CanWrite { get { throw null; } }
public override long Length { get { throw null; } }
public override long Position { get { throw null; } set { } }
- public override System.IAsyncResult BeginRead(byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) { throw null; }
- public override System.IAsyncResult BeginWrite(byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) { throw null; }
+ public override System.IAsyncResult BeginRead(byte[] array, int offset, int count, System.AsyncCallback? asyncCallback, object? asyncState) { throw null; }
+ public override System.IAsyncResult BeginWrite(byte[] array, int offset, int count, System.AsyncCallback? asyncCallback, object? asyncState) { throw null; }
public override void CopyTo(System.IO.Stream destination, int bufferSize) { }
public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) { throw null; }
protected override void Dispose(bool disposing) { }
public ZipArchive(System.IO.Stream stream) { }
public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode) { }
public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen) { }
- public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen, System.Text.Encoding entryNameEncoding) { }
+ public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen, System.Text.Encoding? entryNameEncoding) { }
public System.Collections.ObjectModel.ReadOnlyCollection<System.IO.Compression.ZipArchiveEntry> Entries { get { throw null; } }
public System.IO.Compression.ZipArchiveMode Mode { get { throw null; } }
public System.IO.Compression.ZipArchiveEntry CreateEntry(string entryName) { throw null; }
public System.IO.Compression.ZipArchiveEntry CreateEntry(string entryName, System.IO.Compression.CompressionLevel compressionLevel) { throw null; }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
- public System.IO.Compression.ZipArchiveEntry GetEntry(string entryName) { throw null; }
+ public System.IO.Compression.ZipArchiveEntry? GetEntry(string entryName) { throw null; }
}
public partial class ZipArchiveEntry
{
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configurations>netcoreapp-Debug;netcoreapp-Release;uap-Debug;uap-Release</Configurations>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Include="System.IO.Compression.cs" />
<OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>netcoreapp-Unix-Debug;netcoreapp-Unix-Release;netcoreapp-Windows_NT-Debug;netcoreapp-Windows_NT-Release;uap-Windows_NT-Debug;uap-Windows_NT-Release</Configurations>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<!-- Default configurations to help VS understand the options -->
<ItemGroup>
<Compile Include="$(SharedOpenSourcePath)System\IO\Compression\ZipHelper.cs" />
<Compile Include="$(SharedOpenSourcePath)System\IO\Compression\ZipVersion.cs" />
<Compile Include="System\IO\Compression\DeflateManaged\BlockType.cs" />
- <Compile Include="System\IO\Compression\DeflateManaged\DeflateInput.cs" />
<Compile Include="System\IO\Compression\DeflateManaged\DeflateManagedStream.cs" />
<Compile Include="System\IO\Compression\DeflateManaged\FileFormats.cs" />
<Compile Include="System\IO\Compression\DeflateManaged\HuffmanTree.cs" />
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-
-namespace System.IO.Compression
-{
- internal sealed class DeflateInput
- {
- internal byte[] Buffer { get; set; }
- internal int Count { get; set; }
- internal int StartIndex { get; set; }
-
- internal void ConsumeBytes(int n)
- {
- Debug.Assert(n <= Count, "Should use more bytes than what we have in the buffer");
- StartIndex += n;
- Count -= n;
- Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!");
- }
-
- internal InputState DumpState() => new InputState(Count, StartIndex);
-
- internal void RestoreState(InputState state)
- {
- Count = state._count;
- StartIndex = state._startIndex;
- }
-
- internal readonly struct InputState
- {
- internal readonly int _count;
- internal readonly int _startIndex;
-
- internal InputState(int count, int startIndex)
- {
- _count = count;
- _startIndex = startIndex;
- }
- }
- }
-}
{
internal const int DefaultBufferSize = 8192;
- private Stream _stream;
- private bool _leaveOpen;
+ private Stream? _stream;
private InflaterManaged _inflater;
- private byte[] _buffer;
+ private readonly byte[] _buffer;
private int _asyncOperations;
// A specific constructor to allow decompression of Deflate64
- internal DeflateManagedStream(Stream stream, ZipArchiveEntry.CompressionMethodValues method, long uncompressedSize)
+ internal DeflateManagedStream(Stream stream, ZipArchiveEntry.CompressionMethodValues method, long uncompressedSize = -1)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
if (!stream.CanRead)
throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
-
- InitializeInflater(stream, false, null, method, uncompressedSize);
- }
-
- /// <summary>
- /// Sets up this DeflateManagedStream to be used for Inflation/Decompression
- /// </summary>
- internal void InitializeInflater(Stream stream, bool leaveOpen, IFileFormatReader reader = null, ZipArchiveEntry.CompressionMethodValues method = ZipArchiveEntry.CompressionMethodValues.Deflate, long uncompressedSize = -1)
- {
- Debug.Assert(stream != null);
- Debug.Assert(method == ZipArchiveEntry.CompressionMethodValues.Deflate64);
if (!stream.CanRead)
throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
- _inflater = new InflaterManaged(reader, method == ZipArchiveEntry.CompressionMethodValues.Deflate64 ? true : false, uncompressedSize);
+ Debug.Assert(method == ZipArchiveEntry.CompressionMethodValues.Deflate64);
+
+ _inflater = new InflaterManaged(null, method == ZipArchiveEntry.CompressionMethodValues.Deflate64 ? true : false, uncompressedSize);
_stream = stream;
- _leaveOpen = leaveOpen;
_buffer = new byte[DefaultBufferSize];
}
break;
}
- int bytes = _stream.Read(_buffer, 0, _buffer.Length);
+ int bytes = _stream!.Read(_buffer, 0, _buffer.Length);
if (bytes <= 0)
{
break;
throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
}
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) =>
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, object? asyncState) =>
TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState);
public override int EndRead(IAsyncResult asyncResult) =>
}
Interlocked.Increment(ref _asyncOperations);
- Task<int> readTask = null;
+ Task<int>? readTask = null;
try
{
// If there is no data on the output buffer and we are not at
// the end of the stream, we need to get more data from the base stream
- readTask = _stream.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken);
+ readTask = _stream!.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken);
if (readTask == null)
{
throw new InvalidOperationException(SR.NotSupported_UnreadableStream);
{
// We could have read in head information and didn't get any data.
// Read from the base stream again.
- readTask = _stream.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken);
+ readTask = _stream!.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken);
if (readTask == null)
{
throw new InvalidOperationException(SR.NotSupported_UnreadableStream);
// In this case, we still need to clean up internal resources, hence the inner finally blocks.
try
{
- if (disposing && !_leaveOpen && _stream != null)
+ if (disposing && _stream != null)
_stream.Dispose();
}
finally
{
- _stream = null;
+ _stream = null!;
try
{
}
finally
{
- _inflater = null;
+ _inflater = null!;
base.Dispose(disposing);
}
}
private readonly short[] _right;
private readonly byte[] _codeLengthArray;
#if DEBUG
- private uint[] _codeArrayDebug;
+ private uint[]? _codeArrayDebug;
#endif
private readonly int _tableMask;
private readonly OutputWindow _output;
private readonly InputBuffer _input;
- private HuffmanTree _literalLengthTree;
- private HuffmanTree _distanceTree;
+ private HuffmanTree? _literalLengthTree;
+ private HuffmanTree? _distanceTree;
private InflaterState _state;
private readonly bool _hasFormatReader;
private readonly byte[] _codeList; // temporary array to store the code length for literal/Length and distance
private readonly byte[] _codeLengthTreeCodeLength;
private readonly bool _deflate64;
- private HuffmanTree _codeLengthTree;
+ private HuffmanTree? _codeLengthTree;
private readonly long _uncompressedSize;
private long _currentInflatedCount;
- private readonly IFileFormatReader _formatReader; // class to decode header and footer (e.g. gzip)
+ private readonly IFileFormatReader? _formatReader; // class to decode header and footer (e.g. gzip)
- internal InflaterManaged(IFileFormatReader reader, bool deflate64, long uncompressedSize)
+ internal InflaterManaged(IFileFormatReader? reader, bool deflate64, long uncompressedSize)
{
_output = new OutputWindow();
_input = new InputBuffer();
{
if (_hasFormatReader)
{
+ Debug.Assert(_formatReader != null);
_formatReader.UpdateWithBytesRead(bytes, offset, copied);
}
// But some data in output window might not be copied out.
if (_output.AvailableBytes == 0)
{
+ Debug.Assert(_formatReader != null);
_formatReader.Validate();
}
}
if (_hasFormatReader)
{
+ Debug.Assert(_formatReader != null);
if (_state == InflaterState.ReadingHeader)
{
if (!_formatReader.ReadHeader(_input))
case InflaterState.DecodeTop:
// decode an element from the literal tree
+ Debug.Assert(_literalLengthTree != null);
// TODO: optimize this!!!
symbol = _literalLengthTree.GetNextSymbol(_input);
if (symbol < 0)
case InflaterState.HaveFullLength:
if (_blockType == BlockType.Dynamic)
{
+ Debug.Assert(_distanceTree != null);
_distanceCode = _distanceTree.GetNextSymbol(_input);
}
else
{
if (_state == InflaterState.ReadingTreeCodesBefore)
{
+ Debug.Assert(_codeLengthTree != null);
if ((_lengthCode = _codeLengthTree.GetNextSymbol(_input)) < 0)
{
return false;
internal sealed class InputBuffer
{
- private byte[] _buffer; // byte array to store input
+ private byte[]? _buffer; // byte array to store input
private int _start; // start poisition of the buffer
private int _end; // end position of the buffer
private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer
{
return false;
}
+ Debug.Assert(_buffer != null);
// insert a byte to bitbuffer
_bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
_bitsInBuffer += 8;
/// </summary>
public uint TryLoad16Bits()
{
+ Debug.Assert(_buffer != null);
if (_bitsInBuffer < 8)
{
if (_start < _end)
length = avail;
}
+ Debug.Assert(_buffer != null);
Array.Copy(_buffer, _start, output, offset, length);
_start += length;
return bytesFromBitBuffer + length;
{
private const int DefaultBufferSize = 8192;
- private Stream _stream;
+ private Stream _stream = null!; // field initialized in init methods called from constructor
private CompressionMode _mode;
private bool _leaveOpen;
- private Inflater _inflater;
- private Deflater _deflater;
- private byte[] _buffer;
+ private Inflater? _inflater;
+ private Deflater? _deflater;
+ private byte[]? _buffer;
private int _activeAsyncOperation; // 1 == true, 0 == false
private bool _wroteBytes;
switch (mode)
{
case CompressionMode.Decompress:
- InitializeInflater(stream, leaveOpen, windowBits, uncompressedSize);
+ if (!stream.CanRead)
+ throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
+
+ _inflater = new Inflater(windowBits, uncompressedSize);
+ _stream = stream;
+ _mode = CompressionMode.Decompress;
+ _leaveOpen = leaveOpen;
break;
case CompressionMode.Compress:
}
/// <summary>
- /// Sets up this DeflateStream to be used for Zlib Inflation/Decompression
- /// </summary>
- internal void InitializeInflater(Stream stream, bool leaveOpen, int windowBits, long uncompressedSize)
- {
- Debug.Assert(stream != null);
- if (!stream.CanRead)
- throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
-
- _inflater = new Inflater(windowBits, uncompressedSize);
-
- _stream = stream;
- _mode = CompressionMode.Decompress;
- _leaveOpen = leaveOpen;
- }
-
- /// <summary>
/// Sets up this DeflateStream to be used for Zlib Deflation/Compression
/// </summary>
internal void InitializeDeflater(Stream stream, bool leaveOpen, int windowBits, CompressionLevel compressionLevel)
AsyncOperationStarting();
try
{
+ Debug.Assert(_deflater != null && _buffer != null);
// Compress any bytes left:
await WriteDeflaterOutputAsync(cancellationToken).ConfigureAwait(false);
// Try to read a single byte from zlib without allocating an array, pinning an array, etc.
// If zlib doesn't have any data, fall back to the base stream implementation, which will do that.
byte b;
+ Debug.Assert(_inflater != null);
return _inflater.Inflate(out b) ? b : base.ReadByte();
}
int totalRead = 0;
+ Debug.Assert(_inflater != null);
while (true)
{
int bytesRead = _inflater.Inflate(buffer.Slice(totalRead));
if (_inflater.NeedsInput())
{
+ Debug.Assert(_buffer != null);
int bytes = _stream.Read(_buffer, 0, _buffer.Length);
if (bytes <= 0)
{
throw new InvalidOperationException(SR.CannotWriteToDeflateStream);
}
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) =>
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, object? asyncState) =>
TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState);
public override int EndRead(IAsyncResult asyncResult) =>
AsyncOperationStarting();
try
{
+ Debug.Assert(_inflater != null);
while (true)
{
// Finish inflating any bytes in the input buffer
{
try
{
+ Debug.Assert(_inflater != null && _buffer != null);
while (true)
{
if (_inflater.NeedsInput())
EnsureCompressionMode();
EnsureNotDisposed();
+ Debug.Assert(_deflater != null);
// Write compressed the bytes we already passed to the deflater:
WriteDeflaterOutput();
private void WriteDeflaterOutput()
{
+ Debug.Assert(_deflater != null && _buffer != null);
while (!_deflater.NeedsInput())
{
int compressedBytes = _deflater.GetDeflateOutput(_buffer);
// Compress any bytes left:
WriteDeflaterOutput();
+ Debug.Assert(_deflater != null && _buffer != null);
// Pull out any bytes left inside deflater:
bool flushSuccessful;
do
if (_mode != CompressionMode.Compress)
return;
+ Debug.Assert(_deflater != null && _buffer != null);
// Some deflaters (e.g. ZLib) write more than zero bytes for zero byte inputs.
// This round-trips and we should be ok with this, but our legacy managed deflater
// always wrote zero output for zero input and upstack code (e.g. ZipArchiveEntry)
if (_mode != CompressionMode.Compress)
return;
+ Debug.Assert(_deflater != null && _buffer != null);
// Some deflaters (e.g. ZLib) write more than zero bytes for zero byte inputs.
// This round-trips and we should be ok with this, but our legacy managed deflater
// always wrote zero output for zero input and upstack code (e.g. ZipArchiveEntry)
}
finally
{
- _stream = null;
+ _stream = null!;
try
{
_deflater = null;
_inflater = null;
- byte[] buffer = _buffer;
+ byte[]? buffer = _buffer;
if (buffer != null)
{
_buffer = null;
// Stream.Close() may throw here (may or may not be due to the same error).
// In this case, we still need to clean up internal resources, hence the inner finally blocks.
Stream stream = _stream;
- _stream = null;
+ _stream = null!;
try
{
if (!_leaveOpen && stream != null)
_deflater = null;
_inflater = null;
- byte[] buffer = _buffer;
+ byte[]? buffer = _buffer;
if (buffer != null)
{
_buffer = null;
}
}
- public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) =>
+ public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback? asyncCallback, object? asyncState) =>
TaskToApm.Begin(WriteAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState);
public override void EndWrite(IAsyncResult asyncResult) =>
{
await WriteDeflaterOutputAsync(cancellationToken).ConfigureAwait(false);
+ Debug.Assert(_deflater != null);
// Pass new bytes through deflater
_deflater.SetInput(buffer);
/// </summary>
private async Task WriteDeflaterOutputAsync(CancellationToken cancellationToken)
{
+ Debug.Assert(_deflater != null && _buffer != null);
while (!_deflater.NeedsInput())
{
int compressedBytes = _deflater.GetDeflateOutput(_buffer);
_deflateStream.AsyncOperationStarting();
try
{
+ Debug.Assert(_deflateStream._inflater != null);
// Flush any existing data in the inflater to the destination stream.
while (!_deflateStream._inflater.Finished())
{
_deflateStream.AsyncOperationCompleting();
ArrayPool<byte>.Shared.Return(_arrayPoolBuffer);
- _arrayPoolBuffer = null;
+ _arrayPoolBuffer = null!;
}
}
{
try
{
+ Debug.Assert(_deflateStream._inflater != null);
// Flush any existing data in the inflater to the destination stream.
while (!_deflateStream._inflater.Finished())
{
finally
{
ArrayPool<byte>.Shared.Return(_arrayPoolBuffer);
- _arrayPoolBuffer = null;
+ _arrayPoolBuffer = null!;
}
}
throw new InvalidDataException(SR.GenericInvalidData);
}
+ Debug.Assert(_deflateStream._inflater != null);
// Feed the data from base stream into the decompression engine.
_deflateStream._inflater.SetInput(buffer, offset, count);
throw new InvalidDataException(SR.GenericInvalidData);
}
+ Debug.Assert(_deflateStream._inflater != null);
// Feed the data from base stream into the decompression engine.
_deflateStream._inflater.SetInput(buffer, offset, count);
/// </summary>
internal sealed class Deflater : IDisposable
{
- private ZLibNative.ZLibStreamHandle _zlibStream;
+ private readonly ZLibNative.ZLibStreamHandle _zlibStream;
private MemoryHandle _inputBufferHandle;
private bool _isDisposed;
private const int minWindowBits = -15; // WindowBits must be between -8..-15 to write no header, 8..15 for a
ZLibNative.CompressionStrategy strategy = ZLibNative.CompressionStrategy.DefaultStrategy;
- DeflateInit(zlibCompressionLevel, windowBits, memLevel, strategy);
+ ZErrorCode errC;
+ try
+ {
+ errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, zlibCompressionLevel,
+ windowBits, memLevel, strategy);
+ }
+ catch (Exception cause)
+ {
+ throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
+ }
+
+ switch (errC)
+ {
+ case ZErrorCode.Ok:
+ return;
+
+ case ZErrorCode.MemError:
+ throw new ZLibException(SR.ZLibErrorNotEnoughMemory, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
+
+ case ZErrorCode.VersionError:
+ throw new ZLibException(SR.ZLibErrorVersionMismatch, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
+
+ case ZErrorCode.StreamError:
+ throw new ZLibException(SR.ZLibErrorIncorrectInitParameters, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
+
+ default:
+ throw new ZLibException(SR.ZLibErrorUnexpected, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
+ }
}
~Deflater()
}
}
- private void DeflateInit(ZLibNative.CompressionLevel compressionLevel, int windowBits, int memLevel,
- ZLibNative.CompressionStrategy strategy)
- {
- ZErrorCode errC;
- try
- {
- errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, compressionLevel,
- windowBits, memLevel, strategy);
- }
- catch (Exception cause)
- {
- throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
- }
-
- switch (errC)
- {
- case ZErrorCode.Ok:
- return;
-
- case ZErrorCode.MemError:
- throw new ZLibException(SR.ZLibErrorNotEnoughMemory, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
-
- case ZErrorCode.VersionError:
- throw new ZLibException(SR.ZLibErrorVersionMismatch, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
-
- case ZErrorCode.StreamError:
- throw new ZLibException(SR.ZLibErrorIncorrectInitParameters, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
-
- default:
- throw new ZLibException(SR.ZLibErrorUnexpected, "deflateInit2_", (int)errC, _zlibStream.GetErrorMessage());
- }
- }
-
private ZErrorCode Deflate(ZFlushCode flushCode)
{
ZErrorCode errC;
private bool _finished; // Whether the end of the stream has been reached
private bool _isDisposed; // Prevents multiple disposals
private readonly int _windowBits; // The WindowBits parameter passed to Inflater construction
- private ZLibNative.ZLibStreamHandle _zlibStream; // The handle to the primary underlying zlib stream
+ private ZLibNative.ZLibStreamHandle _zlibStream = null!; // The handle to the primary underlying zlib stream, initialized by a method that is called from the constructor
private GCHandle _inputBufferHandle; // The handle to the buffer that provides input to _zlibStream
private readonly long _uncompressedSize;
private long _currentInflatedCount;
[System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public class ZLibException : IOException, ISerializable
{
- private readonly string _zlibErrorContext = string.Empty;
- private readonly string _zlibErrorMessage = string.Empty;
+ private readonly string? _zlibErrorContext = string.Empty;
+ private readonly string? _zlibErrorMessage = string.Empty;
private readonly ZLibNative.ErrorCode _zlibErrorCode = ZLibNative.ErrorCode.Ok;
/// <summary>
/// <param name="zlibErrorContext">A description of the context within zlib where the error occurred (e.g. the function name).</param>
/// <param name="zlibErrorCode">The error code returned by a ZLib function that caused this exception.</param>
/// <param name="zlibErrorMessage">The string provided by ZLib as error information (unlocalised).</param>
- public ZLibException(string message, string zlibErrorContext, int zlibErrorCode, string zlibErrorMessage) : base(message)
+ public ZLibException(string? message, string? zlibErrorContext, int zlibErrorCode, string? zlibErrorMessage) : base(message)
{
_zlibErrorContext = zlibErrorContext;
_zlibErrorCode = (ZLibNative.ErrorCode)zlibErrorCode;
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a <code>null</code>.</param>
- public ZLibException(string message, Exception innerException) : base(message, innerException) { }
+ public ZLibException(string? message, Exception? innerException) : base(message, innerException) { }
/// <summary>
/// Initializes a new ZLibException with serialized data.
}
// This can work even after XxflateEnd().
- public string GetErrorMessage() => _zStream.msg != ZNullPtr ? Marshal.PtrToStringAnsi(_zStream.msg) : string.Empty;
+ public string GetErrorMessage() => _zStream.msg != ZNullPtr ? Marshal.PtrToStringAnsi(_zStream.msg)! : string.Empty;
}
public static ErrorCode CreateZLibStreamForDeflate(out ZLibStreamHandle zLibStreamHandle, CompressionLevel level,
return _deflateStream.ReadByte();
}
- public override IAsyncResult BeginRead(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) =>
+ public override IAsyncResult BeginRead(byte[] array, int offset, int count, AsyncCallback? asyncCallback, object? asyncState) =>
TaskToApm.Begin(ReadAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState);
public override int EndRead(IAsyncResult asyncResult) =>
}
}
- public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) =>
+ public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback? asyncCallback, object? asyncState) =>
TaskToApm.Begin(WriteAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState);
public override void EndWrite(IAsyncResult asyncResult) =>
{
_deflateStream.Dispose();
}
- _deflateStream = null;
+ _deflateStream = null!;
}
finally
{
return base.DisposeAsync();
}
- DeflateStream ds = _deflateStream;
+ DeflateStream? ds = _deflateStream;
if (ds != null)
{
- _deflateStream = null;
+ _deflateStream = null!;
return ds.DisposeAsync();
}
return default;
}
- public Stream BaseStream => _deflateStream?.BaseStream;
+ public Stream BaseStream => _deflateStream?.BaseStream!;
public override Task<int> ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken)
{
_stream.Write(buffer, offset, count);
}
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object? state)
{
_position += count;
return _stream.BeginWrite(buffer, offset, count, callback, state);
{
public class ZipArchive : IDisposable
{
- private Stream _archiveStream;
- private ZipArchiveEntry _archiveStreamOwner;
- private BinaryReader _archiveReader;
+ private readonly Stream _archiveStream;
+ private ZipArchiveEntry? _archiveStreamOwner;
+ private BinaryReader? _archiveReader;
private ZipArchiveMode _mode;
private List<ZipArchiveEntry> _entries;
private ReadOnlyCollection<ZipArchiveEntry> _entriesCollection;
private bool _isDisposed;
private uint _numberOfThisDisk; //only valid after ReadCentralDirectory
private long _expectedNumberOfEntries;
- private Stream _backingStream;
- private byte[] _archiveComment;
- private Encoding _entryNameEncoding;
+ private Stream? _backingStream;
+ private byte[]? _archiveComment;
+ private Encoding? _entryNameEncoding;
#if DEBUG_FORCE_ZIP64
public bool _forceZip64;
/// otherwise an <see cref="ArgumentException"/> is thrown.</para>
/// </param>
/// <exception cref="ArgumentException">If a Unicode encoding other than UTF-8 is specified for the <code>entryNameEncoding</code>.</exception>
- public ZipArchive(Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding entryNameEncoding)
+ public ZipArchive(Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding? entryNameEncoding)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
EntryNameEncoding = entryNameEncoding;
- Init(stream, mode, leaveOpen);
+ Stream? extraTempStream = null;
+
+ try
+ {
+ _backingStream = null;
+
+ // check stream against mode
+ switch (mode)
+ {
+ case ZipArchiveMode.Create:
+ if (!stream.CanWrite)
+ throw new ArgumentException(SR.CreateModeCapabilities);
+ break;
+ case ZipArchiveMode.Read:
+ if (!stream.CanRead)
+ throw new ArgumentException(SR.ReadModeCapabilities);
+ if (!stream.CanSeek)
+ {
+ _backingStream = stream;
+ extraTempStream = stream = new MemoryStream();
+ _backingStream.CopyTo(stream);
+ stream.Seek(0, SeekOrigin.Begin);
+ }
+ break;
+ case ZipArchiveMode.Update:
+ if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek)
+ throw new ArgumentException(SR.UpdateModeCapabilities);
+ break;
+ default:
+ // still have to throw this, because stream constructor doesn't do mode argument checks
+ throw new ArgumentOutOfRangeException(nameof(mode));
+ }
+
+ _mode = mode;
+ if (mode == ZipArchiveMode.Create && !stream.CanSeek)
+ _archiveStream = new PositionPreservingWriteOnlyStreamWrapper(stream);
+ else
+ _archiveStream = stream;
+ _archiveStreamOwner = null;
+ if (mode == ZipArchiveMode.Create)
+ _archiveReader = null;
+ else
+ _archiveReader = new BinaryReader(_archiveStream);
+ _entries = new List<ZipArchiveEntry>();
+ _entriesCollection = new ReadOnlyCollection<ZipArchiveEntry>(_entries);
+ _entriesDictionary = new Dictionary<string, ZipArchiveEntry>();
+ _readEntries = false;
+ _leaveOpen = leaveOpen;
+ _centralDirectoryStart = 0; // invalid until ReadCentralDirectory
+ _isDisposed = false;
+ _numberOfThisDisk = 0; // invalid until ReadCentralDirectory
+ _archiveComment = null;
+
+ switch (mode)
+ {
+ case ZipArchiveMode.Create:
+ _readEntries = true;
+ break;
+ case ZipArchiveMode.Read:
+ ReadEndOfCentralDirectory();
+ break;
+ case ZipArchiveMode.Update:
+ default:
+ Debug.Assert(mode == ZipArchiveMode.Update);
+ if (_archiveStream.Length == 0)
+ {
+ _readEntries = true;
+ }
+ else
+ {
+ ReadEndOfCentralDirectory();
+ EnsureCentralDirectoryRead();
+ foreach (ZipArchiveEntry entry in _entries)
+ {
+ entry.ThrowIfNotOpenable(needToUncompress: false, needToLoadIntoMemory: true);
+ }
+ }
+ break;
+ }
+ }
+ catch
+ {
+ if (extraTempStream != null)
+ extraTempStream.Dispose();
+
+ throw;
+ }
}
/// <summary>
/// <exception cref="InvalidDataException">The Zip archive is corrupt and the entries cannot be retrieved.</exception>
/// <param name="entryName">A path relative to the root of the archive, identifying the desired entry.</param>
/// <returns>A wrapper for the file entry in the archive. If no entry in the archive exists with the specified name, null will be returned.</returns>
- public ZipArchiveEntry GetEntry(string entryName)
+ public ZipArchiveEntry? GetEntry(string entryName)
{
if (entryName == null)
throw new ArgumentNullException(nameof(entryName));
throw new NotSupportedException(SR.EntriesInCreateMode);
EnsureCentralDirectoryRead();
- ZipArchiveEntry result;
- _entriesDictionary.TryGetValue(entryName, out result);
+ _entriesDictionary.TryGetValue(entryName, out ZipArchiveEntry? result);
return result;
}
- internal BinaryReader ArchiveReader => _archiveReader;
+ internal BinaryReader? ArchiveReader => _archiveReader;
internal Stream ArchiveStream => _archiveStream;
internal uint NumberOfThisDisk => _numberOfThisDisk;
- internal Encoding EntryNameEncoding
+ internal Encoding? EntryNameEncoding
{
get { return _entryNameEncoding; }
}
}
- private void Init(Stream stream, ZipArchiveMode mode, bool leaveOpen)
- {
- Stream extraTempStream = null;
-
- try
- {
- _backingStream = null;
-
- // check stream against mode
- switch (mode)
- {
- case ZipArchiveMode.Create:
- if (!stream.CanWrite)
- throw new ArgumentException(SR.CreateModeCapabilities);
- break;
- case ZipArchiveMode.Read:
- if (!stream.CanRead)
- throw new ArgumentException(SR.ReadModeCapabilities);
- if (!stream.CanSeek)
- {
- _backingStream = stream;
- extraTempStream = stream = new MemoryStream();
- _backingStream.CopyTo(stream);
- stream.Seek(0, SeekOrigin.Begin);
- }
- break;
- case ZipArchiveMode.Update:
- if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek)
- throw new ArgumentException(SR.UpdateModeCapabilities);
- break;
- default:
- // still have to throw this, because stream constructor doesn't do mode argument checks
- throw new ArgumentOutOfRangeException(nameof(mode));
- }
-
- _mode = mode;
- if (mode == ZipArchiveMode.Create && !stream.CanSeek)
- _archiveStream = new PositionPreservingWriteOnlyStreamWrapper(stream);
- else
- _archiveStream = stream;
- _archiveStreamOwner = null;
- if (mode == ZipArchiveMode.Create)
- _archiveReader = null;
- else
- _archiveReader = new BinaryReader(_archiveStream);
- _entries = new List<ZipArchiveEntry>();
- _entriesCollection = new ReadOnlyCollection<ZipArchiveEntry>(_entries);
- _entriesDictionary = new Dictionary<string, ZipArchiveEntry>();
- _readEntries = false;
- _leaveOpen = leaveOpen;
- _centralDirectoryStart = 0; // invalid until ReadCentralDirectory
- _isDisposed = false;
- _numberOfThisDisk = 0; // invalid until ReadCentralDirectory
- _archiveComment = null;
-
- switch (mode)
- {
- case ZipArchiveMode.Create:
- _readEntries = true;
- break;
- case ZipArchiveMode.Read:
- ReadEndOfCentralDirectory();
- break;
- case ZipArchiveMode.Update:
- default:
- Debug.Assert(mode == ZipArchiveMode.Update);
- if (_archiveStream.Length == 0)
- {
- _readEntries = true;
- }
- else
- {
- ReadEndOfCentralDirectory();
- EnsureCentralDirectoryRead();
- foreach (ZipArchiveEntry entry in _entries)
- {
- entry.ThrowIfNotOpenable(needToUncompress: false, needToLoadIntoMemory: true);
- }
- }
- break;
- }
- }
- catch
- {
- if (extraTempStream != null)
- extraTempStream.Dispose();
-
- throw;
- }
- }
-
private void ReadCentralDirectory()
{
try
long numberOfEntries = 0;
+ Debug.Assert(_archiveReader != null);
//read the central directory
ZipCentralDirectoryFileHeader currentHeader;
bool saveExtraFieldsAndComments = Mode == ZipArchiveMode.Update;
long eocdStart = _archiveStream.Position;
+ Debug.Assert(_archiveReader != null);
// read the EOCD
ZipEndOfCentralDirectoryBlock eocd;
bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(_archiveReader, out eocd);
private long? _storedOffsetOfCompressedData;
private uint _crc32;
// An array of buffers, each a maximum of MaxSingleBufferSize in size
- private byte[][] _compressedBytes;
- private MemoryStream _storedUncompressedData;
+ private byte[][]? _compressedBytes;
+ private MemoryStream? _storedUncompressedData;
private bool _currentlyOpenForWrite;
private bool _everOpenedForWrite;
- private Stream _outstandingWriteStream;
+ private Stream? _outstandingWriteStream;
private uint _externalFileAttr;
- private string _storedEntryName;
- private byte[] _storedEntryNameBytes;
+ private string _storedEntryName = null!; // indirectly set in constructor using FullName property
+ private byte[] _storedEntryNameBytes = null!;
// only apply to update mode
- private List<ZipGenericExtraField> _cdUnknownExtraFields;
- private List<ZipGenericExtraField> _lhUnknownExtraFields;
- private readonly byte[] _fileComment;
+ private List<ZipGenericExtraField>? _cdUnknownExtraFields;
+ private List<ZipGenericExtraField>? _lhUnknownExtraFields;
+ private readonly byte[]? _fileComment;
private readonly CompressionLevel? _compressionLevel;
// Initializes, attaches it to archive
_archive.ThrowIfDisposed();
_archive.RemoveEntry(this);
- _archive = null;
+ _archive = null!;
UnloadStreams();
}
{
if (_storedOffsetOfCompressedData == null)
{
+ Debug.Assert(_archive.ArchiveReader != null);
_archive.ArchiveStream.Seek(_offsetOfLocalHeader, SeekOrigin.Begin);
// by calling this, we are using local header _storedEntryNameBytes.Length and extraFieldLength
// to find start of data, but still using central directory size information
// can throw InvalidDataException
internal bool LoadLocalHeaderExtraFieldAndCompressedBytesIfNeeded()
{
- string message;
// we should have made this exact call in _archive.Init through ThrowIfOpenable
- Debug.Assert(IsOpenable(false, true, out message));
+ Debug.Assert(IsOpenable(false, true, out string? message));
// load local header's extra fields. it will be null if we couldn't read for some reason
if (_originallyInArchive)
{
_archive.ArchiveStream.Seek(_offsetOfLocalHeader, SeekOrigin.Begin);
+ Debug.Assert(_archive.ArchiveReader != null);
_lhUnknownExtraFields = ZipLocalFileHeader.GetExtraFields(_archive.ArchiveReader);
}
internal void ThrowIfNotOpenable(bool needToUncompress, bool needToLoadIntoMemory)
{
- string message;
- if (!IsOpenable(needToUncompress, needToLoadIntoMemory, out message))
+ if (!IsOpenable(needToUncompress, needToLoadIntoMemory, out string? message))
throw new InvalidDataException(message);
}
- private CheckSumAndSizeWriteStream GetDataCompressor(Stream backingStream, bool leaveBackingStreamOpen, EventHandler onClose)
+ private CheckSumAndSizeWriteStream GetDataCompressor(Stream backingStream, bool leaveBackingStreamOpen, EventHandler? onClose)
{
// stream stack: backingStream -> DeflateStream -> CheckSumWriteStream
leaveCompressorStreamOpenOnClose,
this,
onClose,
- (long initialPosition, long currentPosition, uint checkSum, Stream backing, ZipArchiveEntry thisRef, EventHandler closeHandler) =>
+ (long initialPosition, long currentPosition, uint checkSum, Stream backing, ZipArchiveEntry thisRef, EventHandler? closeHandler) =>
{
thisRef._crc32 = checkSum;
thisRef._uncompressedSize = currentPosition;
private Stream GetDataDecompressor(Stream compressedStreamToRead)
{
- Stream uncompressedStream = null;
+ Stream? uncompressedStream = null;
switch (CompressionMethod)
{
case CompressionMethodValues.Deflate:
_archive.DebugAssertIsStillArchiveStreamOwner(this);
_everOpenedForWrite = true;
- CheckSumAndSizeWriteStream crcSizeStream = GetDataCompressor(_archive.ArchiveStream, true, (object o, EventArgs e) =>
+ CheckSumAndSizeWriteStream crcSizeStream = GetDataCompressor(_archive.ArchiveStream, true, (object? o, EventArgs e) =>
{
// release the archive stream
- var entry = (ZipArchiveEntry)o;
+ var entry = (ZipArchiveEntry)o!;
entry._archive.ReleaseArchiveStream(entry);
entry._outstandingWriteStream = null;
});
// so we don't fill in any size information
// those fields get figured out when we call GetCompressor as we write it to
// the actual archive
- thisRef._currentlyOpenForWrite = false;
+ thisRef!._currentlyOpenForWrite = false;
});
}
- private bool IsOpenable(bool needToUncompress, bool needToLoadIntoMemory, out string message)
+ private bool IsOpenable(bool needToUncompress, bool needToLoadIntoMemory, out string? message)
{
message = null;
message = SR.LocalFileHeaderCorrupt;
return false;
}
+ Debug.Assert(_archive.ArchiveReader != null);
_archive.ArchiveStream.Seek(_offsetOfLocalHeader, SeekOrigin.Begin);
if (needToUncompress && !needToLoadIntoMemory)
{
// according to ZIP specs, zero-byte files MUST NOT include file data
if (_uncompressedSize != 0)
{
+ Debug.Assert(_compressedBytes != null);
foreach (byte[] compressedBytes in _compressedBytes)
{
_archive.ArchiveStream.Write(compressedBytes, 0, compressedBytes.Length);
return false;
// this pattern needed because nested using blocks trigger CA2202
- MemoryStream ms = null;
+ MemoryStream? ms = null;
try
{
ms = new MemoryStream(extraField.Data);
public long RelativeOffsetOfLocalHeader;
public byte[] Filename;
- public byte[] FileComment;
- public List<ZipGenericExtraField> ExtraFields;
+ public byte[]? FileComment;
+ public List<ZipGenericExtraField>? ExtraFields;
// if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null
// in either case, the zip64 extra field info will be incorporated into other fields
public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
public byte[] ArchiveComment;
- public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[] archiveComment)
+ public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[]? archiveComment)
{
BinaryWriter writer = new BinaryWriter(stream);
private readonly bool _closeBaseStream;
// Delegate that will be invoked on stream disposing
- private readonly Action<ZipArchiveEntry> _onClosed;
+ private readonly Action<ZipArchiveEntry?>? _onClosed;
// Instance that will be passed to _onClose delegate
- private readonly ZipArchiveEntry _zipArchiveEntry;
+ private readonly ZipArchiveEntry? _zipArchiveEntry;
private bool _isDisposed;
internal WrappedStream(Stream baseStream, bool closeBaseStream)
: this(baseStream, closeBaseStream, null, null) { }
- private WrappedStream(Stream baseStream, bool closeBaseStream, ZipArchiveEntry entry, Action<ZipArchiveEntry> onClosed)
+ private WrappedStream(Stream baseStream, bool closeBaseStream, ZipArchiveEntry? entry, Action<ZipArchiveEntry?>? onClosed)
{
_baseStream = baseStream;
_closeBaseStream = closeBaseStream;
_isDisposed = false;
}
- internal WrappedStream(Stream baseStream, ZipArchiveEntry entry, Action<ZipArchiveEntry> onClosed)
+ internal WrappedStream(Stream baseStream, ZipArchiveEntry entry, Action<ZipArchiveEntry?>? onClosed)
: this(baseStream, false, entry, onClosed) { }
public override long Length
// this is the position in BaseBaseStream
private long _initialPosition;
private readonly ZipArchiveEntry _zipArchiveEntry;
- private readonly EventHandler _onClose;
+ private readonly EventHandler? _onClose;
// Called when the stream is closed.
// parameters are initialPosition, currentPosition, checkSum, baseBaseStream, zipArchiveEntry and onClose handler
- private readonly Action<long, long, uint, Stream, ZipArchiveEntry, EventHandler> _saveCrcAndSizes;
+ private readonly Action<long, long, uint, Stream, ZipArchiveEntry, EventHandler?> _saveCrcAndSizes;
// parameters to saveCrcAndSizes are
// initialPosition (initialPosition in baseBaseStream),
// zipArchiveEntry passed here so as to avoid closure allocation,
// onClose handler passed here so as to avoid closure allocation
public CheckSumAndSizeWriteStream(Stream baseStream, Stream baseBaseStream, bool leaveOpenOnClose,
- ZipArchiveEntry entry, EventHandler onClose,
- Action<long, long, uint, Stream, ZipArchiveEntry, EventHandler> saveCrcAndSizes)
+ ZipArchiveEntry entry, EventHandler? onClose,
+ Action<long, long, uint, Stream, ZipArchiveEntry, EventHandler?> saveCrcAndSizes)
{
_baseStream = baseStream;
_baseBaseStream = baseBaseStream;