diff --git a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs
index d1588c384..77355e908 100644
--- a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs
@@ -1,8 +1,9 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
@@ -141,9 +142,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
this.crc ^= CrcSeed;
+ ref uint crcTableRef = ref MemoryMarshal.GetReference(CrcTable.AsSpan());
for (int i = 0; i < data.Length; i++)
{
- this.crc = CrcTable[(this.crc ^ data[i]) & 0xFF] ^ (this.crc >> 8);
+ this.crc = Unsafe.Add(ref crcTableRef, (int)((this.crc ^ data[i]) & 0xFF)) ^ (this.crc >> 8);
}
this.crc ^= CrcSeed;
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs b/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs
index d7198c4ee..5f62b13c7 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs
@@ -27,9 +27,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
[MethodImpl(InliningOptions.ColdPath)]
- public static void ThrowFrequencyNotEmpty() => throw new InvalidOperationException("Huffman frequency entry non empty.");
+ public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated.");
[MethodImpl(InliningOptions.ColdPath)]
- public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated.");
+ public static void ThrowNoDeflate() => throw new ImageFormatException("Cannot deflate all input.");
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs
index d1560eb4b..fb2538f8c 100644
--- a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// This class compresses input with the deflate algorithm described in RFC 1951.
/// It has several compression levels and three different strategies described below.
///
- public sealed class Deflater : IDisposable
+ internal sealed class Deflater : IDisposable
{
///
/// The best and slowest compression level. This tries to find very
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
index 8f57f51f9..327279e72 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
@@ -49,7 +49,7 @@ 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 sealed unsafe class DeflaterEngine : IDisposable
+ internal sealed unsafe class DeflaterEngine : IDisposable
{
private const int TooFar = 4096;
@@ -109,8 +109,8 @@ 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 readonly IMemoryOwner headBuffer;
- private MemoryHandle headBufferHandle;
+ private IMemoryOwner headMemoryOwner;
+ private MemoryHandle headMemoryHandle;
private readonly Memory head;
private readonly short* pinnedHeadPointer;
@@ -121,17 +121,17 @@ 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 readonly IMemoryOwner prevBuffer;
- private MemoryHandle prevBufferHandle;
+ private IMemoryOwner prevMemoryOwner;
+ private MemoryHandle prevMemoryHandle;
private readonly Memory prev;
private readonly short* pinnedPrevPointer;
///
/// This array contains the part of the uncompressed stream that
- /// is of relevance. The current character is indexed by strstart.
+ /// is of relevance. The current character is indexed by strstart.
///
- private readonly IManagedByteBuffer windowBuffer;
- private MemoryHandle windowBufferHandle;
+ private IManagedByteBuffer windowMemoryOwner;
+ private MemoryHandle windowMemoryHandle;
private readonly byte[] window;
private readonly byte* pinnedWindowPointer;
@@ -153,20 +153,20 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// Create pinned pointers to the various buffers to allow indexing
// without bounds checks.
- 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.windowMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE);
+ this.window = this.windowMemoryOwner.Array;
+ this.windowMemoryHandle = this.windowMemoryOwner.Memory.Pin();
+ this.pinnedWindowPointer = (byte*)this.windowMemoryHandle.Pointer;
- this.headBuffer = memoryAllocator.Allocate(DeflaterConstants.HASH_SIZE);
- this.head = this.headBuffer.Memory;
- this.headBufferHandle = this.headBuffer.Memory.Pin();
- this.pinnedHeadPointer = (short*)this.headBufferHandle.Pointer;
+ this.headMemoryOwner = memoryAllocator.Allocate(DeflaterConstants.HASH_SIZE);
+ this.head = this.headMemoryOwner.Memory;
+ this.headMemoryHandle = this.headMemoryOwner.Memory.Pin();
+ this.pinnedHeadPointer = (short*)this.headMemoryHandle.Pointer;
- this.prevBuffer = memoryAllocator.Allocate(DeflaterConstants.WSIZE);
- this.prev = this.prevBuffer.Memory;
- this.prevBufferHandle = this.prevBuffer.Memory.Pin();
- this.pinnedPrevPointer = (short*)this.prevBufferHandle.Pointer;
+ this.prevMemoryOwner = memoryAllocator.Allocate(DeflaterConstants.WSIZE);
+ this.prev = this.prevMemoryOwner.Memory;
+ this.prevMemoryHandle = this.prevMemoryOwner.Memory.Pin();
+ this.pinnedPrevPointer = (short*)this.prevMemoryHandle.Pointer;
// We start at index 1, to avoid an implementation deficiency, that
// we cannot build a repeat pattern at index 0.
@@ -377,8 +377,27 @@ 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);
+ if (!this.isDisposed)
+ {
+ this.huffman.Dispose();
+
+ this.windowMemoryHandle.Dispose();
+ this.windowMemoryOwner.Dispose();
+
+ this.headMemoryHandle.Dispose();
+ this.headMemoryOwner.Dispose();
+
+ this.prevMemoryHandle.Dispose();
+ this.prevMemoryOwner.Dispose();
+
+ this.windowMemoryOwner = null;
+ this.headMemoryOwner = null;
+ this.prevMemoryOwner = null;
+ this.huffman = null;
+
+ this.isDisposed = true;
+ }
+
GC.SuppressFinalize(this);
}
@@ -830,29 +849,5 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
return true;
}
-
- private void Dispose(bool disposing)
- {
- if (!this.isDisposed)
- {
- if (disposing)
- {
- this.huffman.Dispose();
-
- this.windowBufferHandle.Dispose();
- this.windowBuffer.Dispose();
-
- this.headBufferHandle.Dispose();
- this.headBuffer.Dispose();
-
- this.prevBufferHandle.Dispose();
- this.prevBuffer.Dispose();
- }
-
- this.huffman = null;
-
- this.isDisposed = true;
- }
- }
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
index 50aa1c095..7118703d0 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
/// Performs Deflate Huffman encoding.
///
- public sealed unsafe class DeflaterHuffman : IDisposable
+ internal sealed unsafe class DeflaterHuffman : IDisposable
{
private const int BufferSize = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
@@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
int dc = Dcode(dist);
this.distTree.WriteSymbol(pendingBuffer, dc);
- bits = (dc / 2) - 1;
+ bits = (dc >> 1) - 1;
if (bits > 0)
{
this.Pending.WriteBits(dist & ((1 << bits) - 1), bits);
@@ -349,7 +349,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.distTree.Frequencies[dc]++;
if (dc >= 4)
{
- this.extraBits += (dc / 2) - 1;
+ this.extraBits += (dc >> 1) - 1;
}
return this.IsFull();
@@ -443,9 +443,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private readonly int elementCount;
+ private readonly MemoryAllocator memoryAllocator;
+
private IMemoryOwner codesMemoryOwner;
private MemoryHandle codesMemoryHandle;
- private short* codes;
+ private readonly short* codes;
private IMemoryOwner frequenciesMemoryOwner;
private MemoryHandle frequenciesMemoryHandle;
@@ -455,8 +457,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int maxLength)
{
+ this.memoryAllocator = memoryAllocator;
this.elementCount = elements;
-
this.minNumCodes = minCodes;
this.maxLength = maxLength;
@@ -497,27 +499,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public void WriteSymbol(DeflaterPendingBuffer pendingBuffer, int code)
=> pendingBuffer.WriteBits(this.codes[code] & 0xFFFF, this.Length[code]);
- ///
- /// Check that all frequencies are zero
- ///
- ///
- /// At least one frequency is non-zero
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void CheckEmpty()
- {
- bool empty = true;
- for (int i = 0; i < this.elementCount; i++)
- {
- empty &= this.Frequencies[i] == 0;
- }
-
- if (!empty)
- {
- DeflateThrowHelper.ThrowFrequencyNotEmpty();
- }
- }
-
///
/// Set static codes and length
///
@@ -569,130 +550,141 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
//
// The binary tree is encoded in an array: 0 is root node and
// the nodes 2*n+1, 2*n+2 are the child nodes of node n.
- // Maxes out at 286 * 4
- Span heap = stackalloc int[numSymbols];
- ref int heapRef = ref MemoryMarshal.GetReference(heap);
-
- int heapLen = 0;
- int maxCode = 0;
- for (int n = 0; n < numSymbols; n++)
- {
- int freq = this.Frequencies[n];
- if (freq != 0)
- {
- // Insert n into heap
- int pos = heapLen++;
- int ppos;
- while (pos > 0 && this.Frequencies[Unsafe.Add(ref heapRef, ppos = (pos - 1) / 2)] > freq)
- {
- Unsafe.Add(ref heapRef, pos) = Unsafe.Add(ref heapRef, ppos);
- pos = ppos;
- }
-
- heap[pos] = n;
-
- maxCode = n;
- }
- }
-
- // We could encode a single literal with 0 bits but then we
- // don't see the literals. Therefore we force at least two
- // literals to avoid this case. We don't care about order in
- // this case, both literals get a 1 bit code.
- while (heapLen < 2)
- {
- Unsafe.Add(ref heapRef, heapLen++) = maxCode < 2 ? ++maxCode : 0;
- }
-
- this.NumCodes = Math.Max(maxCode + 1, this.minNumCodes);
-
- int numLeafs = heapLen;
- int[] childs = new int[(4 * heapLen) - 2];
- int[] values = new int[(2 * heapLen) - 1];
- int numNodes = numLeafs;
- for (int i = 0; i < heapLen; i++)
- {
- int node = Unsafe.Add(ref heapRef, i);
- childs[2 * i] = node;
- childs[(2 * i) + 1] = -1;
- values[i] = this.Frequencies[node] << 8;
- heap[i] = i;
- }
-
- // Construct the Huffman tree by repeatedly combining the least two
- // frequent nodes.
- do
+ // Maxes out at 286 * 4 so too large for the stack.
+ using (IMemoryOwner heapMemoryOwner = this.memoryAllocator.Allocate(numSymbols))
{
- int first = heap[0];
- int last = Unsafe.Add(ref heapRef, --heapLen);
-
- // Propagate the hole to the leafs of the heap
- int ppos = 0;
- int path = 1;
+ ref int heapRef = ref MemoryMarshal.GetReference(heapMemoryOwner.Memory.Span);
- while (path < heapLen)
+ int heapLen = 0;
+ int maxCode = 0;
+ for (int n = 0; n < numSymbols; n++)
{
- if (path + 1 < heapLen && values[Unsafe.Add(ref heapRef, path)] > values[Unsafe.Add(ref heapRef, path + 1)])
+ int freq = this.Frequencies[n];
+ if (freq != 0)
{
- path++;
+ // Insert n into heap
+ int pos = heapLen++;
+ int ppos;
+ while (pos > 0 && this.Frequencies[Unsafe.Add(ref heapRef, ppos = (pos - 1) >> 1)] > freq)
+ {
+ Unsafe.Add(ref heapRef, pos) = Unsafe.Add(ref heapRef, ppos);
+ pos = ppos;
+ }
+
+ Unsafe.Add(ref heapRef, pos) = n;
+
+ maxCode = n;
}
-
- Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path);
- ppos = path;
- path = (path * 2) + 1;
}
- // Now propagate the last element down along path. Normally
- // it shouldn't go too deep.
- int lastVal = values[last];
- while ((path = ppos) > 0 && values[Unsafe.Add(ref heapRef, ppos = (path - 1) / 2)] > lastVal)
+ // We could encode a single literal with 0 bits but then we
+ // don't see the literals. Therefore we force at least two
+ // literals to avoid this case. We don't care about order in
+ // this case, both literals get a 1 bit code.
+ while (heapLen < 2)
{
- Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos);
+ Unsafe.Add(ref heapRef, heapLen++) = maxCode < 2 ? ++maxCode : 0;
}
- Unsafe.Add(ref heapRef, path) = last;
-
- int second = Unsafe.Add(ref heapRef, 0);
+ this.NumCodes = Math.Max(maxCode + 1, this.minNumCodes);
- // Create a new node father of first and second
- last = numNodes++;
- childs[2 * last] = first;
- childs[(2 * last) + 1] = second;
- int mindepth = Math.Min(values[first] & 0xFF, values[second] & 0xFF);
- values[last] = lastVal = values[first] + values[second] - mindepth + 1;
+ int numLeafs = heapLen;
+ int childrenLength = (4 * heapLen) - 2;
+ using (IMemoryOwner childrenMemoryOwner = this.memoryAllocator.Allocate(childrenLength))
+ using (IMemoryOwner valuesMemoryOwner = this.memoryAllocator.Allocate((2 * heapLen) - 1))
+ {
+ ref int childrenRef = ref MemoryMarshal.GetReference(childrenMemoryOwner.Memory.Span);
+ ref int valuesRef = ref MemoryMarshal.GetReference(valuesMemoryOwner.Memory.Span);
+ int numNodes = numLeafs;
- // Again, propagate the hole to the leafs
- ppos = 0;
- path = 1;
+ for (int i = 0; i < heapLen; i++)
+ {
+ int node = Unsafe.Add(ref heapRef, i);
+ int i2 = 2 * i;
+ Unsafe.Add(ref childrenRef, i2) = node;
+ Unsafe.Add(ref childrenRef, i2 + 1) = -1;
+ Unsafe.Add(ref valuesRef, i) = this.Frequencies[node] << 8;
+ Unsafe.Add(ref heapRef, i) = i;
+ }
- while (path < heapLen)
- {
- if (path + 1 < heapLen && values[Unsafe.Add(ref heapRef, path)] > values[Unsafe.Add(ref heapRef, path + 1)])
+ // Construct the Huffman tree by repeatedly combining the least two
+ // frequent nodes.
+ do
{
- path++;
+ int first = Unsafe.Add(ref heapRef, 0);
+ int last = Unsafe.Add(ref heapRef, --heapLen);
+
+ // Propagate the hole to the leafs of the heap
+ int ppos = 0;
+ int path = 1;
+
+ while (path < heapLen)
+ {
+ if (path + 1 < heapLen && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path)) > Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path + 1)))
+ {
+ path++;
+ }
+
+ Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path);
+ ppos = path;
+ path = (path * 2) + 1;
+ }
+
+ // Now propagate the last element down along path. Normally
+ // it shouldn't go too deep.
+ int lastVal = Unsafe.Add(ref valuesRef, last);
+ while ((path = ppos) > 0
+ && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, ppos = (path - 1) >> 1)) > lastVal)
+ {
+ Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos);
+ }
+
+ Unsafe.Add(ref heapRef, path) = last;
+
+ int second = Unsafe.Add(ref heapRef, 0);
+
+ // Create a new node father of first and second
+ last = numNodes++;
+ Unsafe.Add(ref childrenRef, 2 * last) = first;
+ Unsafe.Add(ref childrenRef, (2 * last) + 1) = second;
+ int mindepth = Math.Min(Unsafe.Add(ref valuesRef, first) & 0xFF, Unsafe.Add(ref valuesRef, second) & 0xFF);
+ Unsafe.Add(ref valuesRef, last) = lastVal = Unsafe.Add(ref valuesRef, first) + Unsafe.Add(ref valuesRef, second) - mindepth + 1;
+
+ // Again, propagate the hole to the leafs
+ ppos = 0;
+ path = 1;
+
+ while (path < heapLen)
+ {
+ if (path + 1 < heapLen
+ && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path)) > Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path + 1)))
+ {
+ path++;
+ }
+
+ Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path);
+ ppos = path;
+ path = (ppos * 2) + 1;
+ }
+
+ // Now propagate the new element down along path
+ while ((path = ppos) > 0 && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, ppos = (path - 1) >> 1)) > lastVal)
+ {
+ Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos);
+ }
+
+ Unsafe.Add(ref heapRef, path) = last;
}
+ while (heapLen > 1);
- Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path);
- ppos = path;
- path = (ppos * 2) + 1;
- }
+ if (Unsafe.Add(ref heapRef, 0) != (childrenLength >> 1) - 1)
+ {
+ DeflateThrowHelper.ThrowHeapViolated();
+ }
- // Now propagate the new element down along path
- while ((path = ppos) > 0 && values[Unsafe.Add(ref heapRef, ppos = (path - 1) / 2)] > lastVal)
- {
- Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos);
+ this.BuildLength(childrenMemoryOwner.Memory.Span);
}
-
- Unsafe.Add(ref heapRef, path) = last;
}
- while (heapLen > 1);
-
- if (Unsafe.Add(ref heapRef, 0) != (childs.Length / 2) - 1)
- {
- DeflateThrowHelper.ThrowHeapViolated();
- }
-
- this.BuildLength(childs);
}
///
@@ -717,10 +709,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
public void CalcBLFreq(Tree blTree)
{
- int max_count; /* max repeat count */
- int min_count; /* min repeat count */
- int count; /* repeat count of the current code */
- int curlen = -1; /* length of current code */
+ int maxCount; // max repeat count
+ int minCount; // min repeat count
+ int count; // repeat count of the current code
+ int curLen = -1; // length of current code
int i = 0;
while (i < this.NumCodes)
@@ -729,37 +721,37 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
int nextlen = this.Length[i];
if (nextlen == 0)
{
- max_count = 138;
- min_count = 3;
+ maxCount = 138;
+ minCount = 3;
}
else
{
- max_count = 6;
- min_count = 3;
- if (curlen != nextlen)
+ maxCount = 6;
+ minCount = 3;
+ if (curLen != nextlen)
{
blTree.Frequencies[nextlen]++;
count = 0;
}
}
- curlen = nextlen;
+ curLen = nextlen;
i++;
- while (i < this.NumCodes && curlen == this.Length[i])
+ while (i < this.NumCodes && curLen == this.Length[i])
{
i++;
- if (++count >= max_count)
+ if (++count >= maxCount)
{
break;
}
}
- if (count < min_count)
+ if (count < minCount)
{
- blTree.Frequencies[curlen] += (short)count;
+ blTree.Frequencies[curLen] += (short)count;
}
- else if (curlen != 0)
+ else if (curLen != 0)
{
blTree.Frequencies[Repeat3To6]++;
}
@@ -781,10 +773,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// The tree to write.
public void WriteTree(DeflaterPendingBuffer pendingBuffer, Tree bitLengthTree)
{
- int max_count; // max repeat count
- int min_count; // min repeat count
- int count; // repeat count of the current code
- int curlen = -1; // length of current code
+ int maxCount; // max repeat count
+ int minCount; // min repeat count
+ int count; // repeat count of the current code
+ int curLen = -1; // length of current code
int i = 0;
while (i < this.NumCodes)
@@ -793,40 +785,40 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
int nextlen = this.Length[i];
if (nextlen == 0)
{
- max_count = 138;
- min_count = 3;
+ maxCount = 138;
+ minCount = 3;
}
else
{
- max_count = 6;
- min_count = 3;
- if (curlen != nextlen)
+ maxCount = 6;
+ minCount = 3;
+ if (curLen != nextlen)
{
bitLengthTree.WriteSymbol(pendingBuffer, nextlen);
count = 0;
}
}
- curlen = nextlen;
+ curLen = nextlen;
i++;
- while (i < this.NumCodes && curlen == this.Length[i])
+ while (i < this.NumCodes && curLen == this.Length[i])
{
i++;
- if (++count >= max_count)
+ if (++count >= maxCount)
{
break;
}
}
- if (count < min_count)
+ if (count < minCount)
{
while (count-- > 0)
{
- bitLengthTree.WriteSymbol(pendingBuffer, curlen);
+ bitLengthTree.WriteSymbol(pendingBuffer, curLen);
}
}
- else if (curlen != 0)
+ else if (curLen != 0)
{
bitLengthTree.WriteSymbol(pendingBuffer, Repeat3To6);
pendingBuffer.WriteBits(count - 3, 2);
@@ -844,10 +836,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
- private void BuildLength(int[] children)
+ private void BuildLength(ReadOnlySpan children)
{
- int numNodes = children.Length / 2;
- int numLeafs = (numNodes + 1) / 2;
+ int numNodes = children.Length >> 1;
+ int numLeafs = (numNodes + 1) >> 1;
int overflow = 0;
for (int i = 0; i < this.maxLength; i++)
@@ -936,26 +928,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
public void Dispose()
- {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
{
if (!this.isDisposed)
{
- if (disposing)
- {
- this.frequenciesMemoryHandle.Dispose();
- this.frequenciesMemoryOwner.Dispose();
+ this.frequenciesMemoryHandle.Dispose();
+ this.frequenciesMemoryOwner.Dispose();
- this.lengthsMemoryHandle.Dispose();
- this.lengthsMemoryOwner.Dispose();
+ this.lengthsMemoryHandle.Dispose();
+ this.lengthsMemoryOwner.Dispose();
- this.codesMemoryHandle.Dispose();
- this.codesMemoryOwner.Dispose();
- }
+ this.codesMemoryHandle.Dispose();
+ this.codesMemoryOwner.Dispose();
this.frequenciesMemoryOwner = null;
this.lengthsMemoryOwner = null;
@@ -963,6 +946,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.isDisposed = true;
}
+
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs
index 837e8b795..eb214aae2 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs
@@ -1,466 +1,155 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-//
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Text;
+using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
///
/// A special stream deflating or compressing the bytes that are
- /// written to it. It uses a Deflater to perform actual deflating.
- /// Authors of the original java version : Tom Tromey, Jochen Hoenicke
+ /// written to it. It uses a Deflater to perform actual deflating.
///
- public class DeflaterOutputStream : Stream
+ internal sealed class DeflaterOutputStream : Stream
{
- #region Constructors
- ///
- /// Creates a new DeflaterOutputStream with the given Deflater and
- /// default buffer size.
- ///
- ///
- /// the output stream where deflated output should be written.
- ///
- ///
- /// the underlying deflater.
- ///
- public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
- : this(baseOutputStream, deflater, 512)
- {
- }
+ private const int BufferLength = 512;
+ private IManagedByteBuffer memoryOwner;
+ private readonly byte[] buffer;
+ private Deflater deflater;
+ private readonly Stream rawStream;
+ private bool isDisposed;
///
- /// Creates a new DeflaterOutputStream with the given Deflater and
- /// buffer size.
+ /// Initializes a new instance of the class.
///
- ///
- /// The output stream where deflated output is written.
- ///
- ///
- /// The underlying deflater to use
- ///
- ///
- /// The buffer size in bytes to use when deflating (minimum value 512)
- ///
- ///
- /// bufsize is less than or equal to zero.
- ///
- ///
- /// baseOutputStream does not support writing
- ///
- ///
- /// deflater instance is null
- ///
- public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
+ /// The memory allocator to use for buffer allocations.
+ /// The output stream where deflated output is written.
+ /// The compression level.
+ public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, int compressionLevel)
{
- if (baseOutputStream == null)
- {
- throw new ArgumentNullException(nameof(baseOutputStream));
- }
-
- if (baseOutputStream.CanWrite == false)
- {
- throw new ArgumentException("Must support writing", nameof(baseOutputStream));
- }
-
- if (bufferSize < 512)
- {
- throw new ArgumentOutOfRangeException(nameof(bufferSize));
- }
-
- baseOutputStream_ = baseOutputStream;
- buffer_ = new byte[bufferSize];
- deflater_ = deflater ?? throw new ArgumentNullException(nameof(deflater));
+ this.rawStream = rawStream;
+ this.memoryOwner = memoryAllocator.AllocateManagedByteBuffer(BufferLength);
+ this.buffer = this.memoryOwner.Array;
+ this.deflater = new Deflater(memoryAllocator, compressionLevel);
}
- #endregion Constructors
+ ///
+ public override bool CanRead => false;
- #region Public API
+ ///
+ public override bool CanSeek => false;
- ///
- /// Finishes the stream by calling finish() on the deflater.
- ///
- ///
- /// Not all input is deflated
- ///
- public virtual void Finish()
- {
- deflater_.Finish();
- while (!deflater_.IsFinished)
- {
- int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
- if (len <= 0)
- {
- break;
- }
+ ///
+ public override bool CanWrite => this.rawStream.CanWrite;
- baseOutputStream_.Write(buffer_, 0, len);
- }
+ ///
+ public override long Length => this.rawStream.Length;
- if (!deflater_.IsFinished)
+ ///
+ public override long Position
+ {
+ get
{
- throw new ImageFormatException("Can't deflate all input?");
+ return this.rawStream.Position;
}
- baseOutputStream_.Flush();
- }
-
- ///
- /// Gets or sets a flag indicating ownership of underlying stream.
- /// When the flag is true will close the underlying stream also.
- ///
- /// The default value is true.
- public bool IsStreamOwner { get; set; } = true;
-
- ///
- /// Allows client to determine if an entry can be patched after its added
- ///
- public bool CanPatchEntries
- {
- get
+ set
{
- return baseOutputStream_.CanSeek;
+ throw new NotSupportedException();
}
}
- #endregion Public API
-
- //#region Encryption
-
- //private string password;
-
- //private ICryptoTransform cryptoTransform_;
+ ///
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
- /////
- ///// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
- /////
- //protected byte[] AESAuthCode;
+ ///
+ public override void SetLength(long value) => throw new NotSupportedException();
- /////
- ///// Get/set the password used for encryption.
- /////
- ///// When set to null or if the password is empty no encryption is performed
- //public string Password
- //{
- // get
- // {
- // return password;
- // }
- // set
- // {
- // if ((value != null) && (value.Length == 0))
- // {
- // password = null;
- // }
- // else
- // {
- // password = value;
- // }
- // }
- //}
+ ///
+ public override int ReadByte() => throw new NotSupportedException();
- /////
- ///// Encrypt a block of data
- /////
- /////
- ///// Data to encrypt. NOTE the original contents of the buffer are lost
- /////
- /////
- ///// Offset of first byte in buffer to encrypt
- /////
- /////
- ///// Number of bytes in buffer to encrypt
- /////
- //protected void EncryptBlock(byte[] buffer, int offset, int length)
- //{
- // cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
- //}
+ ///
+ public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
- /////
- ///// Initializes encryption keys based on given .
- /////
- ///// The password.
- //protected void InitializePassword(string password)
- //{
- // var pkManaged = new PkzipClassicManaged();
- // byte[] key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(password));
- // cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
- //}
-
- /////
- ///// Initializes encryption keys based on given password.
- /////
- //protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
- // out byte[] salt, out byte[] pwdVerifier)
- //{
- // salt = new byte[entry.AESSaltLen];
- // // Salt needs to be cryptographically random, and unique per file
- // if (_aesRnd == null)
- // _aesRnd = RandomNumberGenerator.Create();
- // _aesRnd.GetBytes(salt);
- // int blockSize = entry.AESKeySize / 8; // bits to bytes
-
- // cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
- // pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
- //}
-
- //#endregion Encryption
-
- #region Deflation Support
+ ///
+ public override void Flush()
+ {
+ this.deflater.Flush();
+ this.Deflate(true);
+ this.rawStream.Flush();
+ }
- ///
- /// Deflates everything in the input buffers. This will call
- /// def.deflate() until all bytes from the input buffers
- /// are processed.
- ///
- protected void Deflate()
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
{
- Deflate(false);
+ this.deflater.SetInput(buffer, offset, count);
+ this.Deflate();
}
+ private void Deflate() => this.Deflate(false);
+
private void Deflate(bool flushing)
{
- while (flushing || !deflater_.IsNeedingInput)
+ while (flushing || !this.deflater.IsNeedingInput)
{
- int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
+ int deflateCount = this.deflater.Deflate(this.buffer, 0, BufferLength);
if (deflateCount <= 0)
{
break;
}
- //if (cryptoTransform_ != null)
- //{
- // EncryptBlock(buffer_, 0, deflateCount);
- //}
- baseOutputStream_.Write(buffer_, 0, deflateCount);
+ this.rawStream.Write(this.buffer, 0, deflateCount);
}
- if (!deflater_.IsNeedingInput)
+ if (!this.deflater.IsNeedingInput)
{
- throw new ImageFormatException("DeflaterOutputStream can't deflate all input?");
+ DeflateThrowHelper.ThrowNoDeflate();
}
}
- #endregion Deflation Support
-
- #region Stream Overrides
-
- ///
- /// Gets value indicating stream can be read from
- ///
- public override bool CanRead
+ private void Finish()
{
- get
+ this.deflater.Finish();
+ while (!this.deflater.IsFinished)
{
- return false;
- }
- }
+ int len = this.deflater.Deflate(this.buffer, 0, BufferLength);
+ if (len <= 0)
+ {
+ break;
+ }
- ///
- /// Gets a value indicating if seeking is supported for this stream
- /// This property always returns false
- ///
- public override bool CanSeek
- {
- get
- {
- return false;
+ this.rawStream.Write(this.buffer, 0, len);
}
- }
- ///
- /// Get value indicating if this stream supports writing
- ///
- public override bool CanWrite
- {
- get
+ if (!this.deflater.IsFinished)
{
- return baseOutputStream_.CanWrite;
+ DeflateThrowHelper.ThrowNoDeflate();
}
- }
- ///
- /// Get current length of stream
- ///
- public override long Length
- {
- get
- {
- return baseOutputStream_.Length;
- }
+ this.rawStream.Flush();
}
- ///
- /// Gets the current position within the stream.
- ///
- /// Any attempt to set position
- public override long Position
+ ///
+ protected override void Dispose(bool disposing)
{
- get
- {
- return baseOutputStream_.Position;
- }
- set
+ if (this.isDisposed)
{
- throw new NotSupportedException("Position property not supported");
+ return;
}
- }
-
- ///
- /// Sets the current position of this stream to the given value. Not supported by this class!
- ///
- /// The offset relative to the to seek.
- /// The to seek from.
- /// The new position in the stream.
- /// Any access
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException("DeflaterOutputStream Seek not supported");
- }
-
- ///
- /// Sets the length of this stream to the given value. Not supported by this class!
- ///
- /// The new stream length.
- /// Any access
- public override void SetLength(long value)
- {
- throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
- }
-
- ///
- /// Read a byte from stream advancing position by one
- ///
- /// The byte read cast to an int. THe value is -1 if at the end of the stream.
- /// Any access
- public override int ReadByte()
- {
- throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
- }
-
- ///
- /// Read a block of bytes from stream
- ///
- /// The buffer to store read data in.
- /// The offset to start storing at.
- /// The maximum number of bytes to read.
- /// The actual number of bytes read. Zero if end of stream is detected.
- /// Any access
- public override int Read(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException("DeflaterOutputStream Read not supported");
- }
-
- ///
- /// Flushes the stream by calling Flush on the deflater and then
- /// on the underlying stream. This ensures that all bytes are flushed.
- ///
- public override void Flush()
- {
- deflater_.Flush();
- Deflate(true);
- baseOutputStream_.Flush();
- }
- ///
- /// Calls and closes the underlying
- /// stream when is true.
- ///
- protected override void Dispose(bool disposing)
- {
- if (!isClosed_)
+ if (disposing)
{
- isClosed_ = true;
-
- try
- {
- Finish();
- }
- finally
- {
- if (IsStreamOwner)
- {
- baseOutputStream_.Dispose();
- }
- }
+ this.Finish();
+ this.deflater.Dispose();
+ this.memoryOwner.Dispose();
}
- }
-
- /////
- ///// Get the Auth code for AES encrypted entries
- /////
- //protected void GetAuthCodeIfAES()
- //{
- // if (cryptoTransform_ is ZipAESTransform)
- // {
- // AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
- // }
- //}
- ///
- /// Writes a single byte to the compressed output stream.
- ///
- ///
- /// The byte value.
- ///
- public override void WriteByte(byte value)
- {
- byte[] b = new byte[1];
- b[0] = value;
- Write(b, 0, 1);
- }
-
- ///
- /// Writes bytes from an array to the compressed stream.
- ///
- ///
- /// The byte array
- ///
- ///
- /// The offset into the byte array where to start.
- ///
- ///
- /// The number of bytes to write.
- ///
- public override void Write(byte[] buffer, int offset, int count)
- {
- deflater_.SetInput(buffer, offset, count);
- Deflate();
+ this.deflater = null;
+ this.memoryOwner = null;
+ this.isDisposed = true;
+ base.Dispose(disposing);
}
-
- #endregion Stream Overrides
-
- #region Instance Fields
-
- ///
- /// This buffer is used temporarily to retrieve the bytes from the
- /// deflater and write them to the underlying output stream.
- ///
- private byte[] buffer_;
-
- ///
- /// The deflater which is used to deflate the stream.
- ///
- protected Deflater deflater_;
-
- ///
- /// Base stream the deflater depends on.
- ///
- protected Stream baseOutputStream_;
-
- private bool isClosed_;
-
- #endregion Instance Fields
-
- #region Static Fields
-
- // Static to help ensure that multiple files within a zip will get different random salt
- //private static RandomNumberGenerator _aesRnd = RandomNumberGenerator.Create();
-
- #endregion Static Fields
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs
index d4af8cb5a..a5f00f03c 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
/// Stores pending data for writing data to the Deflater.
///
- public sealed unsafe class DeflaterPendingBuffer : IDisposable
+ internal sealed unsafe class DeflaterPendingBuffer : IDisposable
{
private readonly byte[] buffer;
private readonly byte* pinnedBuffer;
@@ -164,25 +164,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
public void Dispose()
- {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
{
if (!this.isDisposed)
{
- if (disposing)
- {
- this.bufferMemoryHandle.Dispose();
- this.bufferMemoryOwner.Dispose();
- }
-
+ this.bufferMemoryHandle.Dispose();
+ this.bufferMemoryOwner.Dispose();
this.bufferMemoryOwner = null;
-
this.isDisposed = true;
}
+
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/README.md b/src/ImageSharp/Formats/Png/Zlib/README.md
index c297a91d5..59f75d05f 100644
--- a/src/ImageSharp/Formats/Png/Zlib/README.md
+++ b/src/ImageSharp/Formats/Png/Zlib/README.md
@@ -1,2 +1,5 @@
-Adler32.cs and Crc32.cs have been copied from
-https://github.com/ygrenier/SharpZipLib.Portable
+Deflatestream implementation adapted from
+
+https://github.com/icsharpcode/SharpZipLib
+
+LIcensed under MIT
diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
index 5724e027d..36bacc5ec 100644
--- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
@@ -41,8 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// private DeflateStream deflateStream;
private DeflaterOutputStream deflateStream;
- private Deflater deflater;
-
///
/// Initializes a new instance of the class.
///
@@ -92,21 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.rawStream.WriteByte(Cmf);
this.rawStream.WriteByte((byte)flg);
- // Initialize the deflate Stream.
- // CompressionLevel level = CompressionLevel.Optimal;
- //
- // if (compressionLevel >= 1 && compressionLevel <= 5)
- // {
- // level = CompressionLevel.Fastest;
- // }
- // else if (compressionLevel == 0)
- // {
- // level = CompressionLevel.NoCompression;
- // }
- this.deflater = new Deflater(memoryAllocator, compressionLevel);
- this.deflateStream = new DeflaterOutputStream(this.rawStream, this.deflater) { IsStreamOwner = false };
-
- // this.deflateStream = new DeflateStream(this.rawStream, level, true);
+ this.deflateStream = new DeflaterOutputStream(memoryAllocator, this.rawStream, compressionLevel);
}
///
@@ -116,16 +100,23 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public override bool CanSeek => false;
///
- public override bool CanWrite => true;
+ public override bool CanWrite => this.rawStream.CanWrite;
///
- public override long Length => throw new NotSupportedException();
+ public override long Length => this.rawStream.Length;
///
public override long Position
{
- get => throw new NotSupportedException();
- set => throw new NotSupportedException();
+ get
+ {
+ return this.rawStream.Position;
+ }
+
+ set
+ {
+ throw new NotSupportedException();
+ }
}
///
@@ -174,10 +165,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
this.deflateStream.Dispose();
this.deflateStream = null;
-
- // TODO: Remove temporal coupling here.
- this.deflater.Dispose();
- this.deflater = null;
}
else
{
@@ -195,10 +182,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
base.Dispose(disposing);
-
- // Call the appropriate methods to clean up
- // unmanaged resources here.
- // Note disposing is done.
this.isDisposed = true;
}
}