Browse Source

Use pinned buffers for distances

af/merge-core
James Jackson-South 7 years ago
parent
commit
b66a95bd5e
  1. 7
      src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs
  2. 205
      src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
  3. 1
      src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs

7
src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs

@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowUnknownCompression() => throw new InvalidOperationException("Unknown compression function.");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotProcessed() => throw new InvalidOperationException("Old input was not completely processed.");
[MethodImpl(InliningOptions.ColdPath)]
@ -24,5 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
[MethodImpl(InliningOptions.ColdPath)]
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.");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated.");
}
}

205
src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs

@ -2,8 +2,8 @@
// 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
@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
///
/// author of the original java version : Jochen Hoenicke
/// </summary>
public sealed class DeflaterHuffman : IDisposable
public sealed unsafe class DeflaterHuffman : IDisposable
{
private const int BufferSize = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// Number of codes used to transfer bit lengths
private const int BitLengthNumber = 19;
// repeat previous bit length 3-6 times (2 bits of repeat count)
// Repeat previous bit length 3-6 times (2 bits of repeat count)
private const int Repeat3To6 = 16;
// repeat a zero length 3-10 times (3 bits of repeat count)
// Repeat a zero length 3-10 times (3 bits of repeat count)
private const int Repeat3To10 = 17;
// repeat a zero length 11-138 times (7 bits of repeat count)
// Repeat a zero length 11-138 times (7 bits of repeat count)
private const int Repeat11To138 = 18;
private const int EofSymbol = 256;
@ -46,19 +46,24 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
private static short[] staticLCodes;
private static byte[] staticLLength;
private static short[] staticDCodes;
private static byte[] staticDLength;
private static readonly short[] StaticLCodes;
private static readonly byte[] StaticLLength;
private static readonly short[] StaticDCodes;
private static readonly byte[] StaticDLength;
private Tree literalTree;
private Tree distTree;
private Tree blTree;
// Buffer for distances
private short[] distanceBuffer;
private readonly IMemoryOwner<short> distanceManagedBuffer;
private readonly short* pinnedDistanceBuffer;
private MemoryHandle distanceBufferHandle;
private readonly IMemoryOwner<short> literalManagedBuffer;
private readonly short* pinnedLiteralBuffer;
private MemoryHandle literalBufferHandle;
private byte[] literalBuffer;
private int lastLiteral;
private int extraBits;
private bool isDisposed;
@ -67,41 +72,41 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
// See RFC 1951 3.2.6
// Literal codes
staticLCodes = new short[LiteralNumber];
staticLLength = new byte[LiteralNumber];
StaticLCodes = new short[LiteralNumber];
StaticLLength = new byte[LiteralNumber];
int i = 0;
while (i < 144)
{
staticLCodes[i] = BitReverse((0x030 + i) << 8);
staticLLength[i++] = 8;
StaticLCodes[i] = BitReverse((0x030 + i) << 8);
StaticLLength[i++] = 8;
}
while (i < 256)
{
staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);
staticLLength[i++] = 9;
StaticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);
StaticLLength[i++] = 9;
}
while (i < 280)
{
staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);
staticLLength[i++] = 7;
StaticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);
StaticLLength[i++] = 7;
}
while (i < LiteralNumber)
{
staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8);
staticLLength[i++] = 8;
StaticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8);
StaticLLength[i++] = 8;
}
// Distance codes
staticDCodes = new short[DistanceNumber];
staticDLength = new byte[DistanceNumber];
StaticDCodes = new short[DistanceNumber];
StaticDLength = new byte[DistanceNumber];
for (i = 0; i < DistanceNumber; i++)
{
staticDCodes[i] = BitReverse(i << 11);
staticDLength[i] = 5;
StaticDCodes[i] = BitReverse(i << 11);
StaticDLength[i] = 5;
}
}
@ -113,12 +118,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
this.Pending = new DeflaterPendingBuffer(memoryAllocator);
this.literalTree = new Tree(this, LiteralNumber, 257, 15);
this.distTree = new Tree(this, DistanceNumber, 1, 15);
this.blTree = new Tree(this, BitLengthNumber, 4, 7);
this.literalTree = new Tree(LiteralNumber, 257, 15);
this.distTree = new Tree(DistanceNumber, 1, 15);
this.blTree = new Tree(BitLengthNumber, 4, 7);
this.distanceManagedBuffer = memoryAllocator.Allocate<short>(BufferSize);
this.distanceBufferHandle = this.distanceManagedBuffer.Memory.Pin();
this.pinnedDistanceBuffer = (short*)this.distanceBufferHandle.Pointer;
this.distanceBuffer = new short[BufferSize];
this.literalBuffer = new byte[BufferSize];
this.literalManagedBuffer = memoryAllocator.Allocate<short>(BufferSize);
this.literalBufferHandle = this.literalManagedBuffer.Memory.Pin();
this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer;
}
/// <summary>
@ -155,8 +165,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.Pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3);
}
this.literalTree.WriteTree(this.blTree);
this.distTree.WriteTree(this.blTree);
this.literalTree.WriteTree(this.Pending, this.blTree);
this.distTree.WriteTree(this.Pending, this.blTree);
}
/// <summary>
@ -164,14 +174,18 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary>
public void CompressBlock()
{
DeflaterPendingBuffer pendingBuffer = this.Pending;
short* pinnedDistance = this.pinnedDistanceBuffer;
short* pinnedLiteral = this.pinnedLiteralBuffer;
for (int i = 0; i < this.lastLiteral; i++)
{
int litlen = this.literalBuffer[i] & 0xff;
int dist = this.distanceBuffer[i];
int litlen = pinnedLiteral[i] & 0xFF;
int dist = pinnedDistance[i];
if (dist-- != 0)
{
int lc = Lcode(litlen);
this.literalTree.WriteSymbol(lc);
this.literalTree.WriteSymbol(pendingBuffer, lc);
int bits = (lc - 261) / 4;
if (bits > 0 && bits <= 5)
@ -180,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
int dc = Dcode(dist);
this.distTree.WriteSymbol(dc);
this.distTree.WriteSymbol(pendingBuffer, dc);
bits = (dc / 2) - 1;
if (bits > 0)
@ -190,11 +204,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
else
{
this.literalTree.WriteSymbol(litlen);
this.literalTree.WriteSymbol(pendingBuffer, litlen);
}
}
this.literalTree.WriteSymbol(EofSymbol);
this.literalTree.WriteSymbol(pendingBuffer, EofSymbol);
}
/// <summary>
@ -245,19 +259,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
int opt_len = 14 + (blTreeCodes * 3) + this.blTree.GetEncodedLength() +
this.literalTree.GetEncodedLength() + this.distTree.GetEncodedLength() +
this.extraBits;
int opt_len = 14 + (blTreeCodes * 3) + this.blTree.GetEncodedLength()
+ this.literalTree.GetEncodedLength() + this.distTree.GetEncodedLength()
+ this.extraBits;
int static_len = this.extraBits;
for (int i = 0; i < LiteralNumber; i++)
{
static_len += this.literalTree.Freqs[i] * staticLLength[i];
static_len += this.literalTree.Freqs[i] * StaticLLength[i];
}
for (int i = 0; i < DistanceNumber; i++)
{
static_len += this.distTree.Freqs[i] * staticDLength[i];
static_len += this.distTree.Freqs[i] * StaticDLength[i];
}
if (opt_len >= static_len)
@ -275,8 +289,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
// Encode with static tree
this.Pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3);
this.literalTree.SetStaticCodes(staticLCodes, staticLLength);
this.distTree.SetStaticCodes(staticDCodes, staticDLength);
this.literalTree.SetStaticCodes(StaticLCodes, StaticLLength);
this.distTree.SetStaticCodes(StaticDCodes, StaticDLength);
this.CompressBlock();
this.Reset();
}
@ -294,20 +308,19 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// Get value indicating if internal buffer is full
/// </summary>
/// <returns>true if buffer is full</returns>
public bool IsFull()
{
return this.lastLiteral >= BufferSize;
}
[MethodImpl(InliningOptions.ShortMethod)]
public bool IsFull() => this.lastLiteral >= BufferSize;
/// <summary>
/// Add literal to buffer
/// </summary>
/// <param name="literal">Literal value to add to buffer.</param>
/// <returns>Value indicating internal buffer is full</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public bool TallyLit(int literal)
{
this.distanceBuffer[this.lastLiteral] = 0;
this.literalBuffer[this.lastLiteral++] = (byte)literal;
this.pinnedDistanceBuffer[this.lastLiteral] = 0;
this.pinnedLiteralBuffer[this.lastLiteral++] = (byte)literal;
this.literalTree.Freqs[literal]++;
return this.IsFull();
}
@ -318,10 +331,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <param name="distance">Distance code</param>
/// <param name="length">Length</param>
/// <returns>Value indicating if internal buffer is full</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public bool TallyDist(int distance, int length)
{
this.distanceBuffer[this.lastLiteral] = (short)distance;
this.literalBuffer[this.lastLiteral++] = (byte)(length - 3);
this.pinnedDistanceBuffer[this.lastLiteral] = (short)distance;
this.pinnedLiteralBuffer[this.lastLiteral++] = (byte)(length - 3);
int lc = Lcode(length - 3);
this.literalTree.Freqs[lc]++;
@ -345,12 +359,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary>
/// <param name="toReverse">Value to reverse bits</param>
/// <returns>Value with bits reversed</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static short BitReverse(int toReverse)
{
return (short)(Bit4Reverse[toReverse & 0xF] << 12 |
Bit4Reverse[(toReverse >> 4) & 0xF] << 8 |
Bit4Reverse[(toReverse >> 8) & 0xF] << 4 |
Bit4Reverse[toReverse >> 12]);
return (short)(Bit4Reverse[toReverse & 0xF] << 12
| Bit4Reverse[(toReverse >> 4) & 0xF] << 8
| Bit4Reverse[(toReverse >> 8) & 0xF] << 4
| Bit4Reverse[toReverse >> 12]);
}
/// <inheritdoc/>
@ -360,6 +375,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
GC.SuppressFinalize(this);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Lcode(int length)
{
if (length == 255)
@ -377,6 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
return code + length;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Dcode(int distance)
{
int code = 0;
@ -396,6 +413,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (disposing)
{
this.Pending.Dispose();
this.distanceBufferHandle.Dispose();
this.distanceManagedBuffer.Dispose();
this.literalBufferHandle.Dispose();
this.literalManagedBuffer.Dispose();
}
this.Pending = null;
@ -403,20 +424,18 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
private class Tree
private sealed class Tree
{
private readonly int minNumCodes;
private short[] codes;
private readonly int[] bitLengthCounts;
private readonly int maxLength;
private readonly DeflaterHuffman dh;
public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength)
public Tree(int elements, int minCodes, int maxLength)
{
this.dh = dh;
this.minNumCodes = minCodes;
this.maxLength = maxLength;
this.Freqs = new short[elems];
this.Freqs = new short[elements];
this.bitLengthCounts = new int[maxLength];
}
@ -429,6 +448,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <summary>
/// Resets the internal state of the tree
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void Reset()
{
for (int i = 0; i < this.Freqs.Length; i++)
@ -440,17 +460,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.Length = null;
}
public void WriteSymbol(int code)
{
this.dh.Pending.WriteBits(this.codes[code] & 0xffff, this.Length[code]);
}
[MethodImpl(InliningOptions.ShortMethod)]
public void WriteSymbol(DeflaterPendingBuffer pendingBuffer, int code)
=> pendingBuffer.WriteBits(this.codes[code] & 0xFFFF, this.Length[code]);
/// <summary>
/// Check that all frequencies are zero
/// </summary>
/// <exception cref="ImageFormatException">
/// <exception cref="InvalidOperationException">
/// At least one frequency is non-zero
/// </exception>
[MethodImpl(InliningOptions.ShortMethod)]
public void CheckEmpty()
{
bool empty = true;
@ -461,7 +481,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (!empty)
{
throw new ImageFormatException("!Empty");
DeflateThrowHelper.ThrowFrequencyNotEmpty();
}
}
@ -481,7 +501,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary>
public void BuildCodes()
{
int numSymbols = this.Freqs.Length;
int[] nextCode = new int[this.maxLength];
int code = 0;
@ -544,8 +563,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
// this case, both literals get a 1 bit code.
while (heapLen < 2)
{
int node = maxCode < 2 ? ++maxCode : 0;
heap[heapLen++] = node;
heap[heapLen++] = maxCode < 2 ? ++maxCode : 0;
}
this.NumCodes = Math.Max(maxCode + 1, this.minNumCodes);
@ -602,7 +620,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
last = numNodes++;
childs[2 * last] = first;
childs[(2 * last) + 1] = second;
int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff);
int mindepth = Math.Min(values[first] & 0xFF, values[second] & 0xFF);
values[last] = lastVal = values[first] + values[second] - mindepth + 1;
// Again, propagate the hole to the leafs
@ -633,7 +651,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
if (heap[0] != (childs.Length / 2) - 1)
{
throw new ImageFormatException("Heap invariant violated");
DeflateThrowHelper.ThrowHeapViolated();
}
this.BuildLength(childs);
@ -718,10 +736,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
/// <summary>
/// Write tree values
/// Write the tree values.
/// </summary>
/// <param name="blTree">Tree to write</param>
public void WriteTree(Tree blTree)
/// <param name="pendingBuffer">The pending buffer.</param>
/// <param name="bitLengthTree">The tree to write.</param>
public void WriteTree(DeflaterPendingBuffer pendingBuffer, Tree bitLengthTree)
{
int max_count; // max repeat count
int min_count; // min repeat count
@ -744,7 +763,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
min_count = 3;
if (curlen != nextlen)
{
blTree.WriteSymbol(nextlen);
bitLengthTree.WriteSymbol(pendingBuffer, nextlen);
count = 0;
}
}
@ -765,31 +784,31 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
while (count-- > 0)
{
blTree.WriteSymbol(curlen);
bitLengthTree.WriteSymbol(pendingBuffer, curlen);
}
}
else if (curlen != 0)
{
blTree.WriteSymbol(Repeat3To6);
this.dh.Pending.WriteBits(count - 3, 2);
bitLengthTree.WriteSymbol(pendingBuffer, Repeat3To6);
pendingBuffer.WriteBits(count - 3, 2);
}
else if (count <= 10)
{
blTree.WriteSymbol(Repeat3To10);
this.dh.Pending.WriteBits(count - 3, 3);
bitLengthTree.WriteSymbol(pendingBuffer, Repeat3To10);
pendingBuffer.WriteBits(count - 3, 3);
}
else
{
blTree.WriteSymbol(Repeat11To138);
this.dh.Pending.WriteBits(count - 11, 7);
bitLengthTree.WriteSymbol(pendingBuffer, Repeat11To138);
pendingBuffer.WriteBits(count - 11, 7);
}
}
}
private void BuildLength(int[] childs)
private void BuildLength(int[] children)
{
this.Length = new byte[this.Freqs.Length];
int numNodes = childs.Length / 2;
int numNodes = children.Length / 2;
int numLeafs = (numNodes + 1) / 2;
int overflow = 0;
@ -804,7 +823,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
for (int i = numNodes - 1; i >= 0; i--)
{
if (childs[(2 * i) + 1] != -1)
if (children[(2 * i) + 1] != -1)
{
int bitLength = lengths[i] + 1;
if (bitLength > this.maxLength)
@ -813,14 +832,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
overflow++;
}
lengths[childs[2 * i]] = lengths[childs[(2 * i) + 1]] = bitLength;
lengths[children[2 * i]] = lengths[children[(2 * i) + 1]] = bitLength;
}
else
{
// A leaf node
int bitLength = lengths[i];
this.bitLengthCounts[bitLength - 1]++;
this.Length[childs[2 * i]] = (byte)lengths[i];
this.Length[children[2 * i]] = (byte)lengths[i];
}
}
@ -867,11 +886,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
int n = this.bitLengthCounts[bits - 1];
while (n > 0)
{
int childPtr = 2 * childs[nodePtr++];
if (childs[childPtr + 1] == -1)
int childPtr = 2 * children[nodePtr++];
if (children[childPtr + 1] == -1)
{
// We found another leaf
this.Length[childs[childPtr]] = (byte)bits;
this.Length[children[childPtr]] = (byte)bits;
n--;
}
}

1
src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs

@ -29,7 +29,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <param name="memoryAllocator">The memory allocator to use for buffer allocations.</param>
public DeflaterPendingBuffer(MemoryAllocator memoryAllocator)
{
this.buffer = new byte[DeflaterConstants.PENDING_BUF_SIZE];
this.managedBuffer = memoryAllocator.AllocateManagedByteBuffer(DeflaterConstants.PENDING_BUF_SIZE);
this.buffer = this.managedBuffer.Array;
this.handle = this.managedBuffer.Memory.Pin();

Loading…
Cancel
Save