Skip to content

Commit ffa8af7

Browse files
authored
Merge branch 'master' into encode-multiframe
2 parents c58a385 + 684a4dc commit ffa8af7

File tree

79 files changed

+2014
-1178
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2014
-1178
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,5 @@ artifacts/
221221
# Tests
222222
**/Images/ActualOutput
223223
**/Images/ReferenceOutput
224+
**/Images/Input/MemoryStress
224225
.DS_Store

src/ImageSharp/Common/Extensions/StreamExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Buffers;
66
using System.IO;
7-
using SixLabors.ImageSharp.Memory;
87

98
namespace SixLabors.ImageSharp
109
{

src/ImageSharp/Common/Helpers/DebugGuard.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static void IsTrue(bool target, string message)
3737
/// <paramref name="target"/> has a different size than <paramref name="other"/>
3838
/// </exception>
3939
[Conditional("DEBUG")]
40-
public static void MustBeSameSized<T>(Span<T> target, Span<T> other, string parameterName)
40+
public static void MustBeSameSized<T>(ReadOnlySpan<T> target, ReadOnlySpan<T> other, string parameterName)
4141
where T : struct
4242
{
4343
if (target.Length != other.Length)
@@ -57,7 +57,7 @@ public static void MustBeSameSized<T>(Span<T> target, Span<T> other, string para
5757
/// <paramref name="target"/> has less items than <paramref name="minSpan"/>
5858
/// </exception>
5959
[Conditional("DEBUG")]
60-
public static void MustBeSizedAtLeast<T>(Span<T> target, Span<T> minSpan, string parameterName)
60+
public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> target, ReadOnlySpan<T> minSpan, string parameterName)
6161
where T : struct
6262
{
6363
if (target.Length < minSpan.Length)

src/ImageSharp/Common/Helpers/Numerics.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,5 +879,13 @@ ref MemoryMarshal.GetReference(Log2DeBruijn),
879879
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
880880
}
881881
#endif
882+
883+
/// <summary>
884+
/// Fast division with ceiling for <see cref="uint"/> numbers.
885+
/// </summary>
886+
/// <param name="value">Divident value.</param>
887+
/// <param name="divisor">Divisor value.</param>
888+
/// <returns>Ceiled division result.</returns>
889+
public static uint DivideCeil(uint value, uint divisor) => (value + divisor - 1) / divisor;
882890
}
883891
}

src/ImageSharp/Compression/Zlib/Deflater.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ public void SetLevel(int level)
222222
/// The number of compressed bytes added to the output, or 0 if either
223223
/// <see cref="IsNeedingInput"/> or <see cref="IsFinished"/> returns true or length is zero.
224224
/// </returns>
225-
public int Deflate(byte[] output, int offset, int length)
225+
public int Deflate(Span<byte> output, int offset, int length)
226226
{
227227
int origLength = length;
228228

src/ImageSharp/Compression/Zlib/DeflaterEngine.cs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ internal sealed unsafe class DeflaterEngine : IDisposable
130130
/// This array contains the part of the uncompressed stream that
131131
/// is of relevance. The current character is indexed by strstart.
132132
/// </summary>
133-
private IManagedByteBuffer windowMemoryOwner;
133+
private IMemoryOwner<byte> windowMemoryOwner;
134134
private MemoryHandle windowMemoryHandle;
135-
private readonly byte[] window;
135+
private readonly Memory<byte> window;
136136
private readonly byte* pinnedWindowPointer;
137137

138138
private int maxChain;
@@ -153,19 +153,19 @@ public DeflaterEngine(MemoryAllocator memoryAllocator, DeflateStrategy strategy)
153153

154154
// Create pinned pointers to the various buffers to allow indexing
155155
// without bounds checks.
156-
this.windowMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE);
157-
this.window = this.windowMemoryOwner.Array;
158-
this.windowMemoryHandle = this.windowMemoryOwner.Memory.Pin();
156+
this.windowMemoryOwner = memoryAllocator.Allocate<byte>(2 * DeflaterConstants.WSIZE);
157+
this.window = this.windowMemoryOwner.Memory;
158+
this.windowMemoryHandle = this.window.Pin();
159159
this.pinnedWindowPointer = (byte*)this.windowMemoryHandle.Pointer;
160160

161161
this.headMemoryOwner = memoryAllocator.Allocate<short>(DeflaterConstants.HASH_SIZE);
162162
this.head = this.headMemoryOwner.Memory;
163-
this.headMemoryHandle = this.headMemoryOwner.Memory.Pin();
163+
this.headMemoryHandle = this.head.Pin();
164164
this.pinnedHeadPointer = (short*)this.headMemoryHandle.Pointer;
165165

166166
this.prevMemoryOwner = memoryAllocator.Allocate<short>(DeflaterConstants.WSIZE);
167167
this.prev = this.prevMemoryOwner.Memory;
168-
this.prevMemoryHandle = this.prevMemoryOwner.Memory.Pin();
168+
this.prevMemoryHandle = this.prev.Pin();
169169
this.pinnedPrevPointer = (short*)this.prevMemoryHandle.Pointer;
170170

171171
// We start at index 1, to avoid an implementation deficiency, that
@@ -303,7 +303,7 @@ public void SetLevel(int level)
303303
case DeflaterConstants.DEFLATE_STORED:
304304
if (this.strstart > this.blockStart)
305305
{
306-
this.huffman.FlushStoredBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
306+
this.huffman.FlushStoredBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, false);
307307
this.blockStart = this.strstart;
308308
}
309309

@@ -313,7 +313,7 @@ public void SetLevel(int level)
313313
case DeflaterConstants.DEFLATE_FAST:
314314
if (this.strstart > this.blockStart)
315315
{
316-
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
316+
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, false);
317317
this.blockStart = this.strstart;
318318
}
319319

@@ -327,7 +327,7 @@ public void SetLevel(int level)
327327

328328
if (this.strstart > this.blockStart)
329329
{
330-
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
330+
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, false);
331331
this.blockStart = this.strstart;
332332
}
333333

@@ -362,7 +362,10 @@ public void FillWindow()
362362
more = this.inputEnd - this.inputOff;
363363
}
364364

365-
Buffer.BlockCopy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
365+
Unsafe.CopyBlockUnaligned(
366+
ref this.window.Span[this.strstart + this.lookahead],
367+
ref this.inputBuf[this.inputOff],
368+
unchecked((uint)more));
366369

367370
this.inputOff += more;
368371
this.lookahead += more;
@@ -426,7 +429,11 @@ private int InsertString()
426429

427430
private void SlideWindow()
428431
{
429-
Unsafe.CopyBlockUnaligned(ref this.window[0], ref this.window[DeflaterConstants.WSIZE], DeflaterConstants.WSIZE);
432+
Unsafe.CopyBlockUnaligned(
433+
ref this.window.Span[0],
434+
ref this.window.Span[DeflaterConstants.WSIZE],
435+
DeflaterConstants.WSIZE);
436+
430437
this.matchStart -= DeflaterConstants.WSIZE;
431438
this.strstart -= DeflaterConstants.WSIZE;
432439
this.blockStart -= DeflaterConstants.WSIZE;
@@ -663,7 +670,7 @@ private bool DeflateStored(bool flush, bool finish)
663670
lastBlock = false;
664671
}
665672

666-
this.huffman.FlushStoredBlock(this.window, this.blockStart, storedLength, lastBlock);
673+
this.huffman.FlushStoredBlock(this.window.Span, this.blockStart, storedLength, lastBlock);
667674
this.blockStart += storedLength;
668675
return !(lastBlock || storedLength == 0);
669676
}
@@ -683,7 +690,7 @@ private bool DeflateFast(bool flush, bool finish)
683690
if (this.lookahead == 0)
684691
{
685692
// We are flushing everything
686-
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
693+
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, finish);
687694
this.blockStart = this.strstart;
688695
return false;
689696
}
@@ -743,7 +750,7 @@ private bool DeflateFast(bool flush, bool finish)
743750
if (this.huffman.IsFull())
744751
{
745752
bool lastBlock = finish && (this.lookahead == 0);
746-
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, lastBlock);
753+
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, lastBlock);
747754
this.blockStart = this.strstart;
748755
return !lastBlock;
749756
}
@@ -771,7 +778,7 @@ private bool DeflateSlow(bool flush, bool finish)
771778
this.prevAvailable = false;
772779

773780
// We are flushing everything
774-
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
781+
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, finish);
775782
this.blockStart = this.strstart;
776783
return false;
777784
}
@@ -846,7 +853,7 @@ private bool DeflateSlow(bool flush, bool finish)
846853
}
847854

848855
bool lastBlock = finish && (this.lookahead == 0) && !this.prevAvailable;
849-
this.huffman.FlushBlock(this.window, this.blockStart, len, lastBlock);
856+
this.huffman.FlushBlock(this.window.Span, this.blockStart, len, lastBlock);
850857
this.blockStart += len;
851858
return !lastBlock;
852859
}

src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ internal sealed unsafe class DeflaterHuffman : IDisposable
4141
private Tree blTree;
4242

4343
// Buffer for distances
44-
private readonly IMemoryOwner<short> distanceManagedBuffer;
44+
private readonly IMemoryOwner<short> distanceMemoryOwner;
4545
private readonly short* pinnedDistanceBuffer;
4646
private MemoryHandle distanceBufferHandle;
4747

48-
private readonly IMemoryOwner<short> literalManagedBuffer;
48+
private readonly IMemoryOwner<short> literalMemoryOwner;
4949
private readonly short* pinnedLiteralBuffer;
5050
private MemoryHandle literalBufferHandle;
5151

@@ -65,12 +65,12 @@ public DeflaterHuffman(MemoryAllocator memoryAllocator)
6565
this.distTree = new Tree(memoryAllocator, DistanceNumber, 1, 15);
6666
this.blTree = new Tree(memoryAllocator, BitLengthNumber, 4, 7);
6767

68-
this.distanceManagedBuffer = memoryAllocator.Allocate<short>(BufferSize);
69-
this.distanceBufferHandle = this.distanceManagedBuffer.Memory.Pin();
68+
this.distanceMemoryOwner = memoryAllocator.Allocate<short>(BufferSize);
69+
this.distanceBufferHandle = this.distanceMemoryOwner.Memory.Pin();
7070
this.pinnedDistanceBuffer = (short*)this.distanceBufferHandle.Pointer;
7171

72-
this.literalManagedBuffer = memoryAllocator.Allocate<short>(BufferSize);
73-
this.literalBufferHandle = this.literalManagedBuffer.Memory.Pin();
72+
this.literalMemoryOwner = memoryAllocator.Allocate<short>(BufferSize);
73+
this.literalBufferHandle = this.literalMemoryOwner.Memory.Pin();
7474
this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer;
7575
}
7676

@@ -239,7 +239,7 @@ public void CompressBlock()
239239
/// <param name="storedLength">Count of bytes to write</param>
240240
/// <param name="lastBlock">True if this is the last block</param>
241241
[MethodImpl(InliningOptions.ShortMethod)]
242-
public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
242+
public void FlushStoredBlock(ReadOnlySpan<byte> stored, int storedOffset, int storedLength, bool lastBlock)
243243
{
244244
this.Pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3);
245245
this.Pending.AlignToByte();
@@ -256,7 +256,7 @@ public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength,
256256
/// <param name="storedOffset">Index of first byte to flush</param>
257257
/// <param name="storedLength">Count of bytes to flush</param>
258258
/// <param name="lastBlock">True if this is the last block</param>
259-
public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
259+
public void FlushBlock(ReadOnlySpan<byte> stored, int storedOffset, int storedLength, bool lastBlock)
260260
{
261261
this.literalTree.Frequencies[EofSymbol]++;
262262

@@ -286,13 +286,13 @@ public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool l
286286
+ this.extraBits;
287287

288288
int static_len = this.extraBits;
289-
ref byte staticLLengthRef = ref MemoryMarshal.GetReference<byte>(StaticLLength);
289+
ref byte staticLLengthRef = ref MemoryMarshal.GetReference(StaticLLength);
290290
for (int i = 0; i < LiteralNumber; i++)
291291
{
292292
static_len += this.literalTree.Frequencies[i] * Unsafe.Add(ref staticLLengthRef, i);
293293
}
294294

295-
ref byte staticDLengthRef = ref MemoryMarshal.GetReference<byte>(StaticDLength);
295+
ref byte staticDLengthRef = ref MemoryMarshal.GetReference(StaticDLength);
296296
for (int i = 0; i < DistanceNumber; i++)
297297
{
298298
static_len += this.distTree.Frequencies[i] * Unsafe.Add(ref staticDLengthRef, i);
@@ -419,9 +419,9 @@ public void Dispose()
419419
{
420420
this.Pending.Dispose();
421421
this.distanceBufferHandle.Dispose();
422-
this.distanceManagedBuffer.Dispose();
422+
this.distanceMemoryOwner.Dispose();
423423
this.literalBufferHandle.Dispose();
424-
this.literalManagedBuffer.Dispose();
424+
this.literalMemoryOwner.Dispose();
425425

426426
this.literalTree.Dispose();
427427
this.blTree.Dispose();
@@ -484,7 +484,7 @@ private sealed class Tree : IDisposable
484484
private IMemoryOwner<short> frequenciesMemoryOwner;
485485
private MemoryHandle frequenciesMemoryHandle;
486486

487-
private IManagedByteBuffer lengthsMemoryOwner;
487+
private IMemoryOwner<byte> lengthsMemoryOwner;
488488
private MemoryHandle lengthsMemoryHandle;
489489

490490
public Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int maxLength)
@@ -498,7 +498,7 @@ public Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int max
498498
this.frequenciesMemoryHandle = this.frequenciesMemoryOwner.Memory.Pin();
499499
this.Frequencies = (short*)this.frequenciesMemoryHandle.Pointer;
500500

501-
this.lengthsMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(elements);
501+
this.lengthsMemoryOwner = memoryAllocator.Allocate<byte>(elements);
502502
this.lengthsMemoryHandle = this.lengthsMemoryOwner.Memory.Pin();
503503
this.Length = (byte*)this.lengthsMemoryHandle.Pointer;
504504

src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using System.Buffers;
56
using System.IO;
67
using SixLabors.ImageSharp.Memory;
78

@@ -14,8 +15,8 @@ namespace SixLabors.ImageSharp.Compression.Zlib
1415
internal sealed class DeflaterOutputStream : Stream
1516
{
1617
private const int BufferLength = 512;
17-
private IManagedByteBuffer memoryOwner;
18-
private readonly byte[] buffer;
18+
private IMemoryOwner<byte> memoryOwner;
19+
private readonly Memory<byte> buffer;
1920
private Deflater deflater;
2021
private readonly Stream rawStream;
2122
private bool isDisposed;
@@ -29,8 +30,8 @@ internal sealed class DeflaterOutputStream : Stream
2930
public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, int compressionLevel)
3031
{
3132
this.rawStream = rawStream;
32-
this.memoryOwner = memoryAllocator.AllocateManagedByteBuffer(BufferLength);
33-
this.buffer = this.memoryOwner.Array;
33+
this.memoryOwner = memoryAllocator.Allocate<byte>(BufferLength);
34+
this.buffer = this.memoryOwner.Memory;
3435
this.deflater = new Deflater(memoryAllocator, compressionLevel);
3536
}
3637

@@ -49,15 +50,9 @@ public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, i
4950
/// <inheritdoc/>
5051
public override long Position
5152
{
52-
get
53-
{
54-
return this.rawStream.Position;
55-
}
53+
get => this.rawStream.Position;
5654

57-
set
58-
{
59-
throw new NotSupportedException();
60-
}
55+
set => throw new NotSupportedException();
6156
}
6257

6358
/// <inheritdoc/>
@@ -93,14 +88,14 @@ private void Deflate(bool flushing)
9388
{
9489
while (flushing || !this.deflater.IsNeedingInput)
9590
{
96-
int deflateCount = this.deflater.Deflate(this.buffer, 0, BufferLength);
91+
int deflateCount = this.deflater.Deflate(this.buffer.Span, 0, BufferLength);
9792

9893
if (deflateCount <= 0)
9994
{
10095
break;
10196
}
10297

103-
this.rawStream.Write(this.buffer, 0, deflateCount);
98+
this.rawStream.Write(this.buffer.Span.Slice(0, deflateCount));
10499
}
105100

106101
if (!this.deflater.IsNeedingInput)
@@ -114,13 +109,13 @@ private void Finish()
114109
this.deflater.Finish();
115110
while (!this.deflater.IsFinished)
116111
{
117-
int len = this.deflater.Deflate(this.buffer, 0, BufferLength);
112+
int len = this.deflater.Deflate(this.buffer.Span, 0, BufferLength);
118113
if (len <= 0)
119114
{
120115
break;
121116
}
122117

123-
this.rawStream.Write(this.buffer, 0, len);
118+
this.rawStream.Write(this.buffer.Span.Slice(0, len));
124119
}
125120

126121
if (!this.deflater.IsFinished)

0 commit comments

Comments
 (0)