diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs index 400c41824ae8a0..3a862b1bb7a1c6 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/InflaterManaged.cs @@ -615,10 +615,8 @@ private bool DecodeDynamicBlockHeader() throw new InvalidDataException(); } - for (int j = 0; j < repeatCount; j++) - { - _codeList[_loopCounter++] = previousCode; - } + _codeList.AsSpan(_loopCounter, repeatCount).Fill(previousCode); + _loopCounter += repeatCount; } else if (_lengthCode == 17) { @@ -635,10 +633,8 @@ private bool DecodeDynamicBlockHeader() throw new InvalidDataException(); } - for (int j = 0; j < repeatCount; j++) - { - _codeList[_loopCounter++] = 0; - } + _codeList.AsSpan(_loopCounter, repeatCount).Clear(); + _loopCounter += repeatCount; } else { @@ -656,10 +652,8 @@ private bool DecodeDynamicBlockHeader() throw new InvalidDataException(); } - for (int j = 0; j < repeatCount; j++) - { - _codeList[_loopCounter++] = 0; - } + _codeList.AsSpan(_loopCounter, repeatCount).Clear(); + _loopCounter += repeatCount; } } _state = InflaterState.ReadingTreeCodesBefore; // we want to read the next code. diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.Async.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.Async.cs index a3a6c6f6c78ce3..a940928dcb03f9 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.Async.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.Async.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Text; @@ -199,9 +200,12 @@ private async Task ReadCentralDirectoryAsync(CancellationToken cancellationToken { cancellationToken.ThrowIfCancellationRequested(); + byte[] arrayPoolArray = ArrayPool.Shared.Rent(ReadCentralDirectoryReadBufferSize); try { - ReadCentralDirectoryInitialize(out byte[] fileBuffer, out long numberOfEntries, out bool saveExtraFieldsAndComments, out bool continueReadingCentralDirectory, out int bytesRead, out int currPosition, out int bytesConsumed); + Memory fileBuffer = arrayPoolArray; + + ReadCentralDirectoryInitialize(out long numberOfEntries, out bool saveExtraFieldsAndComments, out bool continueReadingCentralDirectory, out int bytesRead, out int currPosition, out int bytesConsumed); // read the central directory while (continueReadingCentralDirectory) @@ -209,13 +213,13 @@ private async Task ReadCentralDirectoryAsync(CancellationToken cancellationToken // the buffer read must always be large enough to fit the constant section size of at least one header int currBytesRead = await _archiveStream.ReadAtLeastAsync(fileBuffer, ZipCentralDirectoryFileHeader.BlockConstantSectionSize, throwOnEndOfStream: false, cancellationToken).ConfigureAwait(false); - byte[] sizedFileBuffer = fileBuffer[0..currBytesRead]; + ReadOnlyMemory sizedFileBuffer = fileBuffer[0..currBytesRead]; continueReadingCentralDirectory = currBytesRead >= ZipCentralDirectoryFileHeader.BlockConstantSectionSize; while (currPosition + ZipCentralDirectoryFileHeader.BlockConstantSectionSize <= currBytesRead) { (bool result, bytesConsumed, ZipCentralDirectoryFileHeader? currentHeader) = - await ZipCentralDirectoryFileHeader.TryReadBlockAsync(sizedFileBuffer.AsMemory(currPosition), _archiveStream, saveExtraFieldsAndComments, cancellationToken).ConfigureAwait(false); + await ZipCentralDirectoryFileHeader.TryReadBlockAsync(sizedFileBuffer.Slice(currPosition), _archiveStream, saveExtraFieldsAndComments, cancellationToken).ConfigureAwait(false); if (!ReadCentralDirectoryEndOfInnerLoopWork(result, currentHeader, bytesConsumed, ref continueReadingCentralDirectory, ref numberOfEntries, ref currPosition, ref bytesRead)) { @@ -223,7 +227,7 @@ private async Task ReadCentralDirectoryAsync(CancellationToken cancellationToken } } - ReadCentralDirectoryEndOfOuterLoopWork(ref currPosition, sizedFileBuffer); + ReadCentralDirectoryEndOfOuterLoopWork(ref currPosition, sizedFileBuffer.Span); } ReadCentralDirectoryPostOuterLoopWork(numberOfEntries); @@ -232,6 +236,10 @@ private async Task ReadCentralDirectoryAsync(CancellationToken cancellationToken { throw new InvalidDataException(SR.Format(SR.CentralDirectoryInvalid, ex)); } + finally + { + ArrayPool.Shared.Return(arrayPoolArray); + } } // This function reads all the EOCD stuff it needs to find the offset to the start of the central directory diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs index 6e1e36fd63b8eb..7a9ecf4c5b5f18 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs @@ -4,6 +4,7 @@ // Zip Spec here: http://www.pkware.com/documents/casestudies/APPNOTE.TXT +using System.Buffers; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; @@ -15,6 +16,8 @@ namespace System.IO.Compression { public partial class ZipArchive : IDisposable, IAsyncDisposable { + private const int ReadCentralDirectoryReadBufferSize = 4096; + private readonly Stream _archiveStream; private ZipArchiveEntry? _archiveStreamOwner; private readonly ZipArchiveMode _mode; @@ -475,12 +478,8 @@ private void EnsureCentralDirectoryRead() } } - private void ReadCentralDirectoryInitialize(out byte[] fileBuffer, out long numberOfEntries, out bool saveExtraFieldsAndComments, out bool continueReadingCentralDirectory, out int bytesRead, out int currPosition, out int bytesConsumed) + private void ReadCentralDirectoryInitialize(out long numberOfEntries, out bool saveExtraFieldsAndComments, out bool continueReadingCentralDirectory, out int bytesRead, out int currPosition, out int bytesConsumed) { - const int ReadCentralDirectoryReadBufferSize = 4096; - - fileBuffer = new byte[ReadCentralDirectoryReadBufferSize]; - // assume ReadEndOfCentralDirectory has been called and has populated _centralDirectoryStart _archiveStream.Seek(_centralDirectoryStart, SeekOrigin.Begin); @@ -550,19 +549,20 @@ private void ReadCentralDirectoryPostOuterLoopWork(long numberOfEntries) private void ReadCentralDirectory() { + byte[] arrayPoolArray = ArrayPool.Shared.Rent(ReadCentralDirectoryReadBufferSize); try { - ReadCentralDirectoryInitialize(out byte[] fileBuffer, out long numberOfEntries, out bool saveExtraFieldsAndComments, out bool continueReadingCentralDirectory, out int bytesRead, out int currPosition, out int bytesConsumed); + Span fileBuffer = arrayPoolArray.AsSpan(); - Span fileBufferSpan = fileBuffer.AsSpan(); + ReadCentralDirectoryInitialize(out long numberOfEntries, out bool saveExtraFieldsAndComments, out bool continueReadingCentralDirectory, out int bytesRead, out int currPosition, out int bytesConsumed); // read the central directory while (continueReadingCentralDirectory) { // the buffer read must always be large enough to fit the constant section size of at least one header - int currBytesRead = _archiveStream.ReadAtLeast(fileBufferSpan, ZipCentralDirectoryFileHeader.BlockConstantSectionSize, throwOnEndOfStream: false); + int currBytesRead = _archiveStream.ReadAtLeast(fileBuffer, ZipCentralDirectoryFileHeader.BlockConstantSectionSize, throwOnEndOfStream: false); - ReadOnlySpan sizedFileBuffer = fileBufferSpan.Slice(0, currBytesRead); + ReadOnlySpan sizedFileBuffer = fileBuffer.Slice(0, currBytesRead); continueReadingCentralDirectory = currBytesRead >= ZipCentralDirectoryFileHeader.BlockConstantSectionSize; while (currPosition + ZipCentralDirectoryFileHeader.BlockConstantSectionSize <= currBytesRead) @@ -585,6 +585,10 @@ private void ReadCentralDirectory() { throw new InvalidDataException(SR.Format(SR.CentralDirectoryInvalid, ex)); } + finally + { + ArrayPool.Shared.Return(arrayPoolArray); + } } private void ReadEndOfCentralDirectoryInnerWork(ZipEndOfCentralDirectoryBlock eocd) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.Async.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.Async.cs index 8305b5e42bfdce..fea75ba16db328 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.Async.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.Async.cs @@ -190,23 +190,20 @@ internal sealed partial class ZipCentralDirectoryFileHeader // Data needs to come from two sources, and we must thus copy data into a single address space. else { - if (dynamicHeaderSize > StackAllocationThreshold) - { - arrayPoolBuffer = ArrayPool.Shared.Rent(dynamicHeaderSize); - } - - byte[] collatedHeader = dynamicHeaderSize <= StackAllocationThreshold ? new byte[dynamicHeaderSize] : arrayPoolBuffer.AsSpan(0, dynamicHeaderSize).ToArray(); + arrayPoolBuffer = ArrayPool.Shared.Rent(dynamicHeaderSize); + Memory collatedHeader = arrayPoolBuffer.AsMemory(0, dynamicHeaderSize); buffer[FieldLocations.DynamicData..].CopyTo(collatedHeader); - Debug.Assert(bytesToRead == collatedHeader[remainingBufferLength..].Length); - int realBytesRead = await furtherReads.ReadAtLeastAsync(collatedHeader.AsMemory(remainingBufferLength..), bytesToRead, throwOnEndOfStream: false, cancellationToken).ConfigureAwait(false); + Debug.Assert(bytesToRead == collatedHeader.Length - remainingBufferLength); + int realBytesRead = await furtherReads.ReadAtLeastAsync(collatedHeader.Slice(remainingBufferLength), bytesToRead, throwOnEndOfStream: false, cancellationToken).ConfigureAwait(false); if (realBytesRead != bytesToRead) { return (false, bytesRead, null); } - dynamicHeader = collatedHeader; + + dynamicHeader = collatedHeader.Span; } TryReadBlockFinalize(header, dynamicHeader, dynamicHeaderSize, uncompressedSizeSmall, compressedSizeSmall, diskNumberStartSmall, relativeOffsetOfLocalHeaderSmall, saveExtraFieldsAndComments, ref bytesRead, out Zip64ExtraField zip64);