diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs b/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs index a360e5e87..2698c9b99 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs @@ -13,5 +13,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib [MethodImpl(InliningOptions.ColdPath)] public static void ThrowAlreadyClosed() => throw new InvalidOperationException("Deflator already closed."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowUnknownCompression() => throw new InvalidOperationException("Unknown compression function."); + + public static void ThrowNotProcessed() => throw new InvalidOperationException("Old input was not completely processed."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNull(string name) => throw new ArgumentNullException(name); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name); } } diff --git a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs index 5732f82d2..ef1d0e116 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs @@ -295,8 +295,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { if (disposing) { - this.pending.Dispose(); this.engine.Dispose(); + this.pending.Dispose(); } this.pending = null; diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs index 0870f629c..4db87c57e 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs @@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// private int inputEnd; - private DeflateStrategy strategy; - private DeflaterPendingBuffer pending; + private readonly DeflateStrategy strategy; + private readonly DeflaterPendingBuffer pending; private DeflaterHuffman huffman; private bool isDisposed; @@ -110,7 +110,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// Note that the array should really be unsigned short, so you need /// to and the values with 0xFFFF. /// - private short[] head; + private readonly IMemoryOwner headBuffer; + private MemoryHandle headBufferHandle; + private readonly Memory head; + private readonly short* pinnedHeadPointer; /// /// prev[index & WMASK] points to the previous index that has the @@ -119,7 +122,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// Note that the array should really be unsigned short, so you need /// to and the values with 0xFFFF. /// - private short[] prev; + private readonly IMemoryOwner prevBuffer; + private MemoryHandle prevBufferHandle; + private readonly Memory prev; + private readonly short* pinnedPrevPointer; /// /// This array contains the part of the uncompressed stream that @@ -127,7 +133,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// private readonly IManagedByteBuffer windowBuffer; private MemoryHandle windowBufferHandle; - private byte[] window; + private readonly byte[] window; private readonly byte* pinnedWindowPointer; private int maxChain; @@ -147,13 +153,22 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.huffman = new DeflaterHuffman(pending); this.strategy = strategy; - this.windowBuffer = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE); + // Create pinned pointers to the various buffers to allow indexing + // without bounds checks. + this.windowBuffer = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE, AllocationOptions.Clean); this.window = this.windowBuffer.Array; this.windowBufferHandle = this.windowBuffer.Memory.Pin(); this.pinnedWindowPointer = (byte*)this.windowBufferHandle.Pointer; - this.head = new short[DeflaterConstants.HASH_SIZE]; - this.prev = new short[DeflaterConstants.WSIZE]; + this.headBuffer = memoryAllocator.Allocate(DeflaterConstants.HASH_SIZE, AllocationOptions.Clean); + this.head = this.headBuffer.Memory; + this.headBufferHandle = this.headBuffer.Memory.Pin(); + this.pinnedHeadPointer = (short*)this.headBufferHandle.Pointer; + + this.prevBuffer = memoryAllocator.Allocate(DeflaterConstants.WSIZE, AllocationOptions.Clean); + this.prev = this.prevBuffer.Memory; + this.prevBufferHandle = this.prevBuffer.Memory.Pin(); + this.pinnedPrevPointer = (short*)this.prevBufferHandle.Pointer; // We start at index 1, to avoid an implementation deficiency, that // we cannot build a repeat pattern at index 0. @@ -168,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// Returns true if progress has been made. public bool Deflate(bool flush, bool finish) { - bool progress; + bool progress = false; do { this.FillWindow(); @@ -189,7 +204,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib break; default: - throw new InvalidOperationException("unknown compressionFunction"); + DeflateThrowHelper.ThrowUnknownCompression(); + break; } } while (this.pending.IsFlushed && progress); // repeat while we have no pending output and progress was made @@ -197,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } /// - /// Sets input data to be deflated. Should only be called when NeedsInput() + /// Sets input data to be deflated. Should only be called when /// returns true /// /// The buffer containing input data. @@ -205,33 +221,33 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// The number of bytes of data to use as input. public void SetInput(byte[] buffer, int offset, int count) { - if (buffer == null) + if (buffer is null) { - throw new ArgumentNullException(nameof(buffer)); + DeflateThrowHelper.ThrowNull(nameof(buffer)); } if (offset < 0) { - throw new ArgumentOutOfRangeException(nameof(offset)); + DeflateThrowHelper.ThrowOutOfRange(nameof(offset)); } if (count < 0) { - throw new ArgumentOutOfRangeException(nameof(count)); + DeflateThrowHelper.ThrowOutOfRange(nameof(count)); } if (this.inputOff < this.inputEnd) { - throw new InvalidOperationException("Old input was not completely processed."); + DeflateThrowHelper.ThrowNotProcessed(); } int end = offset + count; - // We want to throw an ArrayIndexOutOfBoundsException early. + // We want to throw an ArgumentOutOfRangeException early. // The check is very tricky: it also handles integer wrap around. if ((offset > end) || (end > buffer.Length)) { - throw new ArgumentOutOfRangeException(nameof(count)); + DeflateThrowHelper.ThrowOutOfRange(nameof(count)); } this.inputBuf = buffer; @@ -243,47 +259,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// Determines if more input is needed. /// /// Return true if input is needed via SetInput - public bool NeedsInput() - { - return this.inputEnd == this.inputOff; - } - - /// - /// Set compression dictionary - /// - /// The buffer containing the dictionary data - /// The offset in the buffer for the first byte of data - /// The length of the dictionary data. - public void SetDictionary(byte[] buffer, int offset, int length) - { - if (length < DeflaterConstants.MIN_MATCH) - { - return; - } - - if (length > DeflaterConstants.MAX_DIST) - { - offset += length - DeflaterConstants.MAX_DIST; - length = DeflaterConstants.MAX_DIST; - } - - System.Array.Copy(buffer, offset, this.window, this.strstart, length); - - this.UpdateHash(); - --length; - while (--length > 0) - { - this.InsertString(); - this.strstart++; - } - - this.strstart += 2; - this.blockStart = this.strstart; - } + [MethodImpl(InliningOptions.ShortMethod)] + public bool NeedsInput() => this.inputEnd == this.inputOff; /// /// Reset internal state /// + [MethodImpl(InliningOptions.ShortMethod)] public void Reset() { this.huffman.Reset(); @@ -291,16 +273,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.lookahead = 0; this.prevAvailable = false; this.matchLen = DeflaterConstants.MIN_MATCH - 1; - - for (int i = 0; i < DeflaterConstants.HASH_SIZE; i++) - { - this.head[i] = 0; - } - - for (int i = 0; i < DeflaterConstants.WSIZE; i++) - { - this.prev[i] = 0; - } + this.head.Span.Slice(0, DeflaterConstants.HASH_SIZE).Clear(); + this.prev.Span.Slice(0, DeflaterConstants.WSIZE).Clear(); } /// @@ -311,7 +285,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { if ((level < 0) || (level > 9)) { - throw new ArgumentOutOfRangeException(nameof(level)); + DeflateThrowHelper.ThrowOutOfRange(nameof(level)); } this.goodLength = DeflaterConstants.GOOD_LENGTH[level]; @@ -417,13 +391,15 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// value for this hash. /// /// The previous hash value + [MethodImpl(InliningOptions.ShortMethod)] private int InsertString() { short match; int hash = ((this.insertHashIndex << DeflaterConstants.HASH_SHIFT) ^ this.pinnedWindowPointer[this.strstart + (DeflaterConstants.MIN_MATCH - 1)]) & DeflaterConstants.HASH_MASK; - this.prev[this.strstart & DeflaterConstants.WMASK] = match = this.head[hash]; - this.head[hash] = unchecked((short)this.strstart); + short* pinnedHead = this.pinnedHeadPointer; + this.pinnedPrevPointer[this.strstart & DeflaterConstants.WMASK] = match = pinnedHead[hash]; + pinnedHead[hash] = unchecked((short)this.strstart); this.insertHashIndex = hash; return match & 0xFFFF; } @@ -437,17 +413,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib // Slide the hash table (could be avoided with 32 bit values // at the expense of memory usage). + short* pinnedHead = this.pinnedHeadPointer; for (int i = 0; i < DeflaterConstants.HASH_SIZE; ++i) { - int m = this.head[i] & 0xFFFF; - this.head[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); + int m = pinnedHead[i] & 0xFFFF; + pinnedHead[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); } // Slide the prev table. + short* pinnedPrev = this.pinnedPrevPointer; for (int i = 0; i < DeflaterConstants.WSIZE; i++) { - int m = this.prev[i] & 0xFFFF; - this.prev[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); + int m = pinnedPrev[i] & 0xFFFF; + pinnedPrev[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); } } @@ -473,8 +451,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib int scanMax = scan + Math.Min(DeflaterConstants.MAX_MATCH, this.lookahead) - 1; int limit = Math.Max(scan - DeflaterConstants.MAX_DIST, 0); - byte[] window = this.window; - short[] prev = this.prev; int chainLength = this.maxChain; int niceLength = Math.Min(this.niceLength, this.lookahead); @@ -495,6 +471,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib chainLength >>= 2; } + short* pinnedPrev = this.pinnedPrevPointer; do { match = curMatch; @@ -631,7 +608,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib scan_end = pinnedWindow[scan]; } } - while ((curMatch = prev[curMatch & DeflaterConstants.WMASK] & 0xFFFF) > limit && --chainLength != 0); + while ((curMatch = pinnedPrev[curMatch & DeflaterConstants.WMASK] & 0xFFFF) > limit && --chainLength != 0); return this.matchLen >= DeflaterConstants.MIN_MATCH; } @@ -859,6 +836,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { this.windowBufferHandle.Dispose(); this.windowBuffer.Dispose(); + + this.headBufferHandle.Dispose(); + this.headBuffer.Dispose(); + + this.prevBufferHandle.Dispose(); + this.prevBuffer.Dispose(); } this.isDisposed = true; diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs index ac5f229aa..837e8b795 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs @@ -97,11 +97,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib break; } - //if (cryptoTransform_ != null) - //{ - // EncryptBlock(buffer_, 0, len); - //} - baseOutputStream_.Write(buffer_, 0, len); } @@ -111,16 +106,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } baseOutputStream_.Flush(); - - //if (cryptoTransform_ != null) - //{ - // if (cryptoTransform_ is ZipAESTransform) - // { - // AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); - // } - // cryptoTransform_.Dispose(); - // cryptoTransform_ = null; - //} } /// @@ -394,12 +379,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib try { Finish(); - //if (cryptoTransform_ != null) - //{ - // GetAuthCodeIfAES(); - // cryptoTransform_.Dispose(); - // cryptoTransform_ = null; - //} } finally {