Browse Source

Optimize DeflaterEngine

af/merge-core
James Jackson-South 6 years ago
parent
commit
bac80eedcc
  1. 11
      src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs
  2. 2
      src/ImageSharp/Formats/Png/Zlib/Deflater.cs
  3. 133
      src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
  4. 21
      src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs

11
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);
}
}

2
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;

133
src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs

@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary>
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.
/// </summary>
private short[] head;
private readonly IMemoryOwner<short> headBuffer;
private MemoryHandle headBufferHandle;
private readonly Memory<short> head;
private readonly short* pinnedHeadPointer;
/// <summary>
/// <code>prev[index &amp; WMASK]</code> 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.
/// </summary>
private short[] prev;
private readonly IMemoryOwner<short> prevBuffer;
private MemoryHandle prevBufferHandle;
private readonly Memory<short> prev;
private readonly short* pinnedPrevPointer;
/// <summary>
/// This array contains the part of the uncompressed stream that
@ -127,7 +133,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary>
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<short>(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<short>(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>Returns true if progress has been made.</returns>
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
}
/// <summary>
/// Sets input data to be deflated. Should only be called when <code>NeedsInput()</code>
/// Sets input data to be deflated. Should only be called when <see cref="NeedsInput"/>
/// returns true
/// </summary>
/// <param name="buffer">The buffer containing input data.</param>
@ -205,33 +221,33 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <param name="count">The number of bytes of data to use as input.</param>
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 <see cref="SetInput">input</see> is needed.
/// </summary>
/// <returns>Return true if input is needed via <see cref="SetInput">SetInput</see></returns>
public bool NeedsInput()
{
return this.inputEnd == this.inputOff;
}
/// <summary>
/// Set compression dictionary
/// </summary>
/// <param name="buffer">The buffer containing the dictionary data</param>
/// <param name="offset">The offset in the buffer for the first byte of data</param>
/// <param name="length">The length of the dictionary data.</param>
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;
/// <summary>
/// Reset internal state
/// </summary>
[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();
}
/// <summary>
@ -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.
/// </summary>
/// <returns>The previous hash value</returns>
[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;

21
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;
//}
}
/// <summary>
@ -394,12 +379,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
try
{
Finish();
//if (cryptoTransform_ != null)
//{
// GetAuthCodeIfAES();
// cryptoTransform_.Dispose();
// cryptoTransform_ = null;
//}
}
finally
{

Loading…
Cancel
Save