diff --git a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs
index 33b3019b89..5732f82d22 100644
--- a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs
@@ -54,7 +54,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private DeflaterEngine engine;
private bool isDisposed;
- private const int IsSetDict = 0x01;
private const int IsFlushing = 0x04;
private const int IsFinishing = 0x08;
private const int BusyState = 0x10;
@@ -82,8 +81,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
this.pending = new DeflaterPendingBuffer(memoryAllocator);
- this.engine = new DeflaterEngine(this.pending, true);
- this.engine.Strategy = DeflateStrategy.Default;
+
+ // TODO: Possibly provide DeflateStrategy as an option.
+ this.engine = new DeflaterEngine(memoryAllocator, this.pending, DeflateStrategy.Default);
+
this.SetLevel(level);
this.Reset();
}
@@ -233,37 +234,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
DeflateThrowHelper.ThrowAlreadyClosed();
}
- if (this.state < BusyState)
- {
- // Output header
- int header = (Deflated + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
- int levelFlags = (this.level - 1) >> 1;
- if (levelFlags < 0 || levelFlags > 3)
- {
- levelFlags = 3;
- }
-
- header |= levelFlags << 6;
- if ((this.state & IsSetDict) != 0)
- {
- // Dictionary was set
- header |= DeflaterConstants.PRESET_DICT;
- }
-
- header += 31 - (header % 31);
-
- this.pending.WriteShortMSB(header);
- if ((this.state & IsSetDict) != 0)
- {
- int chksum = this.engine.Adler;
- this.engine.ResetAdler();
- this.pending.WriteShortMSB(chksum >> 16);
- this.pending.WriteShortMSB(chksum & 0xffff);
- }
-
- this.state = BusyState | (this.state & (IsFlushing | IsFinishing));
- }
-
while (true)
{
int count = this.pending.Flush(output, offset, length);
@@ -326,9 +296,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (disposing)
{
this.pending.Dispose();
+ this.engine.Dispose();
}
this.pending = null;
+ this.engine = null;
this.isDisposed = true;
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
index ddf571884a..0870f629c5 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
@@ -2,8 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Collections.Generic;
-using System.Text;
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
@@ -48,30 +49,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// Low level compression engine for deflate algorithm which uses a 32K sliding window
/// with secondary compression from Huffman/Shannon-Fano codes.
///
- public class DeflaterEngine
+ public sealed unsafe class DeflaterEngine : IDisposable
{
private const int TooFar = 4096;
// Hash index of string to be inserted
private int insertHashIndex;
- ///
- /// Hashtable, hashing three characters to an index for window, so
- /// that window[index]..window[index+2] have this hash code.
- /// Note that the array should really be unsigned short, so you need
- /// to and the values with 0xFFFF.
- ///
- private short[] head;
-
- ///
- /// prev[index & WMASK] points to the previous index that has the
- /// same hash code as the string starting at index. This way
- /// entries with the same hash code are in a linked list.
- /// Note that the array should really be unsigned short, so you need
- /// to and the values with 0xFFFF.
- ///
- private short[] prev;
-
private int matchStart;
// Length of best match
@@ -95,16 +79,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
private int lookahead;
- ///
- /// This array contains the part of the uncompressed stream that
- /// is of relevance. The current character is indexed by strstart.
- ///
- private byte[] window;
- private int maxChain;
- private int maxLazy;
- private int niceLength;
- private int goodLength;
-
///
/// The current compression function.
///
@@ -115,11 +89,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
private byte[] inputBuf;
- ///
- /// The total bytes of input read.
- ///
- private long totalIn;
-
///
/// The offset into inputBuf, where input data starts.
///
@@ -130,31 +99,59 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
private int inputEnd;
+ private DeflateStrategy strategy;
private DeflaterPendingBuffer pending;
private DeflaterHuffman huffman;
+ private bool isDisposed;
///
- /// The adler checksum
+ /// Hashtable, hashing three characters to an index for window, so
+ /// that window[index]..window[index+2] have this hash code.
+ /// Note that the array should really be unsigned short, so you need
+ /// to and the values with 0xFFFF.
///
- private Adler32 adler;
+ private short[] head;
+
+ ///
+ /// prev[index & WMASK] points to the previous index that has the
+ /// same hash code as the string starting at index. This way
+ /// entries with the same hash code are in a linked list.
+ /// Note that the array should really be unsigned short, so you need
+ /// to and the values with 0xFFFF.
+ ///
+ private short[] prev;
+
+ ///
+ /// This array contains the part of the uncompressed stream that
+ /// is of relevance. The current character is indexed by strstart.
+ ///
+ private readonly IManagedByteBuffer windowBuffer;
+ private MemoryHandle windowBufferHandle;
+ private byte[] window;
+ private readonly byte* pinnedWindowPointer;
+
+ private int maxChain;
+ private int maxLazy;
+ private int niceLength;
+ private int goodLength;
///
/// Initializes a new instance of the class.
///
+ /// The memory allocator to use for buffer allocations.
/// The pending buffer to use.
- ///
- /// If no adler calculation should be performed
- ///
- public DeflaterEngine(DeflaterPendingBuffer pending, bool noAdlerCalculation)
+ /// The deflate strategy to use.
+ public DeflaterEngine(MemoryAllocator memoryAllocator, DeflaterPendingBuffer pending, DeflateStrategy strategy)
{
this.pending = pending;
this.huffman = new DeflaterHuffman(pending);
- if (!noAdlerCalculation)
- {
- this.adler = new Adler32();
- }
+ this.strategy = strategy;
+
+ this.windowBuffer = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE);
+ this.window = this.windowBuffer.Array;
+ this.windowBufferHandle = this.windowBuffer.Memory.Pin();
+ this.pinnedWindowPointer = (byte*)this.windowBufferHandle.Pointer;
- this.window = new byte[2 * DeflaterConstants.WSIZE];
this.head = new short[DeflaterConstants.HASH_SIZE];
this.prev = new short[DeflaterConstants.WSIZE];
@@ -163,33 +160,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.blockStart = this.strstart = 1;
}
- ///
- /// Gets the current value of Adler checksum
- ///
- public int Adler
- {
- get
- {
- return (this.adler != null) ? unchecked((int)this.adler.Value) : 0;
- }
- }
-
- ///
- /// Gets the total data processed
- ///
- public long TotalIn
- {
- get
- {
- return this.totalIn;
- }
- }
-
- ///
- /// Gets or sets the deflate strategy
- ///
- public DeflateStrategy Strategy { get; set; }
-
///
/// Deflate drives actual compression of data
///
@@ -286,7 +256,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// The length of the dictionary data.
public void SetDictionary(byte[] buffer, int offset, int length)
{
- this.adler?.Update(new ArraySegment(buffer, offset, length));
if (length < DeflaterConstants.MIN_MATCH)
{
return;
@@ -318,10 +287,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public void Reset()
{
this.huffman.Reset();
- this.adler?.Reset();
this.blockStart = this.strstart = 1;
this.lookahead = 0;
- this.totalIn = 0;
this.prevAvailable = false;
this.matchLen = DeflaterConstants.MIN_MATCH - 1;
@@ -336,14 +303,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
- ///
- /// Reset Adler checksum
- ///
- public void ResetAdler()
- {
- this.adler?.Reset();
- }
-
///
/// Set the deflate level (0-9)
///
@@ -386,7 +345,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
case DeflaterConstants.DEFLATE_SLOW:
if (this.prevAvailable)
{
- this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
+ this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart - 1] & 0xFF);
}
if (this.strstart > this.blockStart)
@@ -427,10 +386,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
Array.Copy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
- this.adler?.Update(new ArraySegment(this.inputBuf, this.inputOff, more));
this.inputOff += more;
- this.totalIn += more;
this.lookahead += more;
}
@@ -440,9 +397,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
private void UpdateHash()
{
- this.insertHashIndex = (this.window[this.strstart] << DeflaterConstants.HASH_SHIFT) ^ this.window[this.strstart + 1];
+ byte* pinned = this.pinnedWindowPointer;
+ this.insertHashIndex = (pinned[this.strstart] << DeflaterConstants.HASH_SHIFT) ^ pinned[this.strstart + 1];
}
///
@@ -453,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private int InsertString()
{
short match;
- int hash = ((this.insertHashIndex << DeflaterConstants.HASH_SHIFT) ^ this.window[this.strstart + (DeflaterConstants.MIN_MATCH - 1)]) & DeflaterConstants.HASH_MASK;
+ 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);
@@ -463,7 +430,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private void SlideWindow()
{
- Array.Copy(this.window, DeflaterConstants.WSIZE, this.window, 0, DeflaterConstants.WSIZE);
+ Unsafe.CopyBlockUnaligned(ref this.window[0], ref this.window[DeflaterConstants.WSIZE], DeflaterConstants.WSIZE);
this.matchStart -= DeflaterConstants.WSIZE;
this.strstart -= DeflaterConstants.WSIZE;
this.blockStart -= DeflaterConstants.WSIZE;
@@ -518,8 +485,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
return false;
}
- byte scan_end1 = window[scan + this.matchLen - 1];
- byte scan_end = window[scan + this.matchLen];
+ byte* pinnedWindow = this.pinnedWindowPointer;
+ byte scan_end1 = pinnedWindow[scan + this.matchLen - 1];
+ byte scan_end = pinnedWindow[scan + this.matchLen];
// Do not waste too much time if we already have a good match:
if (this.matchLen >= this.goodLength)
@@ -532,10 +500,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
match = curMatch;
scan = this.strstart;
- if (window[match + this.matchLen] != scan_end
- || window[match + this.matchLen - 1] != scan_end1
- || window[match] != window[scan]
- || window[++match] != window[++scan])
+ if (pinnedWindow[match + this.matchLen] != scan_end
+ || pinnedWindow[match + this.matchLen - 1] != scan_end1
+ || pinnedWindow[match] != pinnedWindow[scan]
+ || pinnedWindow[++match] != pinnedWindow[++scan])
{
continue;
}
@@ -547,7 +515,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
switch ((scanMax - scan) % 8)
{
case 1:
- if (window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -555,8 +523,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
case 2:
- if (window[++scan] == window[++match]
- && window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -564,9 +532,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
case 3:
- if (window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -574,10 +542,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
case 4:
- if (window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -585,11 +553,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
case 5:
- if (window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -597,12 +565,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
case 6:
- if (window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -610,13 +578,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
case 7:
- if (window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match])
+ if (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match])
{
break;
}
@@ -624,7 +592,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
}
- if (window[scan] == window[match])
+ if (pinnedWindow[scan] == pinnedWindow[match])
{
// We check for insufficient lookahead only every 8th comparison;
// the 256th check will be made at strstart + 258 unless lookahead is
@@ -639,14 +607,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
}
}
- while (window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]
- && window[++scan] == window[++match]);
+ while (pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]
+ && pinnedWindow[++scan] == pinnedWindow[++match]);
}
if (scan - this.strstart > this.matchLen)
@@ -659,8 +627,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
break;
}
- scan_end1 = window[scan - 1];
- scan_end = window[scan];
+ scan_end1 = pinnedWindow[scan - 1];
+ scan_end = pinnedWindow[scan];
}
}
while ((curMatch = prev[curMatch & DeflaterConstants.WMASK] & 0xFFFF) > limit && --chainLength != 0);
@@ -727,7 +695,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
int hashHead;
if (this.lookahead >= DeflaterConstants.MIN_MATCH &&
(hashHead = this.InsertString()) != 0 &&
- this.Strategy != DeflateStrategy.HuffmanOnly &&
+ this.strategy != DeflateStrategy.HuffmanOnly &&
this.strstart - hashHead <= DeflaterConstants.MAX_DIST &&
this.FindLongestMatch(hashHead))
{
@@ -763,7 +731,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
else
{
// No match found
- this.huffman.TallyLit(this.window[this.strstart] & 0xff);
+ this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart] & 0xff);
++this.strstart;
--this.lookahead;
}
@@ -793,7 +761,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
if (this.prevAvailable)
{
- this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
+ this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart - 1] & 0xff);
}
this.prevAvailable = false;
@@ -818,14 +786,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
int hashHead = this.InsertString();
- if (this.Strategy != DeflateStrategy.HuffmanOnly &&
+ if (this.strategy != DeflateStrategy.HuffmanOnly &&
hashHead != 0 &&
this.strstart - hashHead <= DeflaterConstants.MAX_DIST &&
this.FindLongestMatch(hashHead))
{
// longestMatch sets matchStart and matchLen
// Discard match if too small and too far away
- if (this.matchLen <= 5 && (this.Strategy == DeflateStrategy.Filtered || (this.matchLen == DeflaterConstants.MIN_MATCH && this.strstart - this.matchStart > TooFar)))
+ if (this.matchLen <= 5 && (this.strategy == DeflateStrategy.Filtered || (this.matchLen == DeflaterConstants.MIN_MATCH && this.strstart - this.matchStart > TooFar)))
{
this.matchLen = DeflaterConstants.MIN_MATCH - 1;
}
@@ -835,15 +803,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// previous match was better
if ((prevLen >= DeflaterConstants.MIN_MATCH) && (this.matchLen <= prevLen))
{
-#if DebugDeflation
- if (DeflaterConstants.DEBUGGING)
- {
- for (int i = 0 ; i < matchLen; i++) {
- if (window[strstart-1+i] != window[prevMatch + i])
- throw new ImageFormatException();
- }
- }
-#endif
this.huffman.TallyDist(this.strstart - 1 - prevMatch, prevLen);
prevLen -= 2;
do
@@ -866,7 +825,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
if (this.prevAvailable)
{
- this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
+ this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart - 1] & 0xff);
}
this.prevAvailable = true;
@@ -891,5 +850,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
return true;
}
+
+ private void Dispose(bool disposing)
+ {
+ if (!this.isDisposed)
+ {
+ if (disposing)
+ {
+ this.windowBufferHandle.Dispose();
+ this.windowBuffer.Dispose();
+ }
+
+ this.isDisposed = true;
+ }
+ }
}
}