Browse Source

Merge branch 'main' into stefannikolei/nullable/exifprofile

pull/2320/head
James Jackson-South 3 years ago
committed by GitHub
parent
commit
b8fe22c586
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs
  2. 2
      src/ImageSharp/Compression/Zlib/Deflater.cs
  3. 12
      src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
  4. 9
      src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs
  5. 3
      src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs
  6. 2
      src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs
  7. 3
      src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs
  8. 6
      src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
  9. 7
      src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
  10. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  11. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
  12. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  13. 12
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  14. 22
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs
  15. 11
      src/ImageSharp/Memory/Allocators/Internals/Gen2GcCallback.cs
  16. 3
      src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs
  17. 15
      src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
  18. 7
      src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs
  19. 11
      src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs
  20. 2
      src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs
  21. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
  22. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs
  23. 6
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
  24. 10
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
  25. 8
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  26. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs
  27. 9
      tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs
  28. 11
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  29. 2
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  30. 1
      tests/ImageSharp.Tests/TestImages.cs
  31. 3
      tests/Images/Input/Jpg/issues/issue-2315.jpg

10
src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs

@ -1,23 +1,33 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Compression.Zlib;
internal static class DeflateThrowHelper
{
[DoesNotReturn]
public static void ThrowAlreadyFinished() => throw new InvalidOperationException("Finish() already called.");
[DoesNotReturn]
public static void ThrowAlreadyClosed() => throw new InvalidOperationException("Deflator already closed.");
[DoesNotReturn]
public static void ThrowUnknownCompression() => throw new InvalidOperationException("Unknown compression function.");
[DoesNotReturn]
public static void ThrowNotProcessed() => throw new InvalidOperationException("Old input was not completely processed.");
[DoesNotReturn]
public static void ThrowNull(string name) => throw new ArgumentNullException(name);
[DoesNotReturn]
public static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
[DoesNotReturn]
public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated.");
[DoesNotReturn]
public static void ThrowNoDeflate() => throw new ImageFormatException("Cannot deflate all input.");
}

2
src/ImageSharp/Compression/Zlib/Deflater.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
@ -285,7 +284,6 @@ internal sealed class Deflater : IDisposable
if (!this.isDisposed)
{
this.engine.Dispose();
this.engine = null;
this.isDisposed = true;
}
}

12
src/ImageSharp/Compression/Zlib/DeflaterEngine.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Runtime.CompilerServices;
@ -87,7 +86,7 @@ internal sealed unsafe class DeflaterEngine : IDisposable
/// <summary>
/// The input data for compression.
/// </summary>
private byte[] inputBuf;
private byte[]? inputBuf;
/// <summary>
/// The offset into inputBuf, where input data starts.
@ -222,7 +221,7 @@ internal sealed unsafe class DeflaterEngine : IDisposable
/// <param name="buffer">The buffer containing input data.</param>
/// <param name="offset">The offset of the first byte of data.</param>
/// <param name="count">The number of bytes of data to use as input.</param>
public void SetInput(byte[] buffer, int offset, int count)
public void SetInput(byte[]? buffer, int offset, int count)
{
if (buffer is null)
{
@ -362,6 +361,8 @@ internal sealed unsafe class DeflaterEngine : IDisposable
more = this.inputEnd - this.inputOff;
}
ArgumentNullException.ThrowIfNull(this.inputBuf);
Unsafe.CopyBlockUnaligned(
ref this.window.Span[this.strstart + this.lookahead],
ref this.inputBuf[this.inputOff],
@ -393,11 +394,6 @@ internal sealed unsafe class DeflaterEngine : IDisposable
this.prevMemoryHandle.Dispose();
this.prevMemoryOwner.Dispose();
this.windowMemoryOwner = null;
this.headMemoryOwner = null;
this.prevMemoryOwner = null;
this.huffman = null;
this.isDisposed = true;
}
}

9
src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Runtime.CompilerServices;
@ -427,10 +426,6 @@ internal sealed unsafe class DeflaterHuffman : IDisposable
this.blTree.Dispose();
this.distTree.Dispose();
this.Pending = null;
this.literalTree = null;
this.blTree = null;
this.distTree = null;
this.isDisposed = true;
}
}
@ -977,10 +972,6 @@ internal sealed unsafe class DeflaterHuffman : IDisposable
this.codesMemoryHandle.Dispose();
this.codesMemoryOwner.Dispose();
this.frequenciesMemoryOwner = null;
this.lengthsMemoryOwner = null;
this.codesMemoryOwner = null;
this.isDisposed = true;
}
}

3
src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using SixLabors.ImageSharp.Memory;
@ -137,8 +136,6 @@ internal sealed class DeflaterOutputStream : Stream
this.memoryOwner.Dispose();
}
this.deflater = null;
this.memoryOwner = null;
this.isDisposed = true;
base.Dispose(disposing);
}

2
src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Runtime.CompilerServices;
@ -180,7 +179,6 @@ internal sealed unsafe class DeflaterPendingBuffer : IDisposable
{
this.bufferMemoryHandle.Dispose();
this.bufferMemoryOwner.Dispose();
this.bufferMemoryOwner = null;
this.isDisposed = true;
}
}

3
src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Png;
@ -172,8 +171,6 @@ internal sealed class ZlibDeflateStream : Stream
this.rawStream.WriteByte((byte)(crc & 0xFF));
}
this.deflateStream = null;
base.Dispose(disposing);
this.isDisposed = true;
}

6
src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.IO.Compression;
using SixLabors.ImageSharp.IO;
@ -90,7 +90,7 @@ internal sealed class ZlibInflateStream : Stream
/// <summary>
/// Gets the compressed stream over the deframed inner stream.
/// </summary>
public DeflateStream CompressedStream { get; private set; }
public DeflateStream? CompressedStream { get; private set; }
/// <summary>
/// Adds new bytes from a frame found in the original stream.
@ -98,6 +98,7 @@ internal sealed class ZlibInflateStream : Stream
/// <param name="bytes">The current remaining data according to the chunk length.</param>
/// <param name="isCriticalChunk">Whether the chunk to be inflated is a critical chunk.</param>
/// <returns>The <see cref="bool"/>.</returns>
[MemberNotNullWhen(true, nameof(CompressedStream))]
public bool AllocateNewBytes(int bytes, bool isCriticalChunk)
{
this.currentDataRemaining = bytes;
@ -210,6 +211,7 @@ internal sealed class ZlibInflateStream : Stream
this.isDisposed = true;
}
[MemberNotNullWhen(true, nameof(CompressedStream))]
private bool InitializeInflateStream(bool isCriticalChunk)
{
// Read the zlib header : http://tools.ietf.org/html/rfc1950

7
src/ImageSharp/Diagnostics/MemoryDiagnostics.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
namespace SixLabors.ImageSharp.Diagnostics;
@ -17,7 +16,7 @@ public static class MemoryDiagnostics
{
private static int totalUndisposedAllocationCount;
private static UndisposedAllocationDelegate undisposedAllocation;
private static UndisposedAllocationDelegate? undisposedAllocation;
private static int undisposedAllocationSubscriptionCounter;
private static readonly object SyncRoot = new();
@ -50,12 +49,12 @@ public static class MemoryDiagnostics
/// <summary>
/// Fires when ImageSharp allocates memory from a MemoryAllocator
/// </summary>
internal static event Action MemoryAllocated;
internal static event Action? MemoryAllocated;
/// <summary>
/// Fires when ImageSharp releases memory allocated from a MemoryAllocator
/// </summary>
internal static event Action MemoryReleased;
internal static event Action? MemoryReleased;
/// <summary>
/// Gets a value indicating the total number of memory resource objects leaked to the finalizer.

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -26,16 +26,6 @@ internal readonly struct JFifMarker : IEquatable<JFifMarker>
/// <param name="yDensity">The vertical pixel density.</param>
private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity)
{
if (xDensity <= 0)
{
JpegThrowHelper.ThrowInvalidImageContentException($"X-Density {xDensity} must be greater than 0.");
}
if (yDensity <= 0)
{
JpegThrowHelper.ThrowInvalidImageContentException($"Y-Density {yDensity} must be greater than 0.");
}
this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion;
@ -64,12 +54,12 @@ internal readonly struct JFifMarker : IEquatable<JFifMarker>
public PixelResolutionUnit DensityUnits { get; }
/// <summary>
/// Gets the horizontal pixel density. Must not be zero.
/// Gets the horizontal pixel density.
/// </summary>
public short XDensity { get; }
/// <summary>
/// Gets the vertical pixel density. Must not be zero.
/// Gets the vertical pixel density.
/// </summary>
public short YDensity { get; }
@ -88,12 +78,8 @@ internal readonly struct JFifMarker : IEquatable<JFifMarker>
byte densityUnits = bytes[7];
short xDensity = (short)((bytes[8] << 8) | bytes[9]);
short yDensity = (short)((bytes[10] << 8) | bytes[11]);
if (xDensity > 0 && yDensity > 0)
{
marker = new JFifMarker(majorVersion, minorVersion, densityUnits, xDensity, yDensity);
return true;
}
marker = new JFifMarker(majorVersion, minorVersion, densityUnits, xDensity, yDensity);
return true;
}
marker = default;

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs

@ -121,4 +121,10 @@ internal abstract class SpectralConverter
return size;
}
/// <summary>
/// Gets a value indicating whether the converter has a pixel buffer.
/// </summary>
/// <returns><see langword="true"/> if the converter has a pixel buffer; otherwise, <see langword="false"/>.</returns>
public abstract bool HasPixelBuffer();
}

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -85,6 +85,12 @@ internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable
/// </summary>
public Configuration Configuration { get; }
/// <summary>
/// Gets a value indicating whether the converter has a pixel buffer.
/// </summary>
/// <returns><see langword="true"/> if the converter has a pixel buffer; otherwise, <see langword="false"/>.</returns>
public override bool HasPixelBuffer() => this.pixelBuffer is not null;
/// <summary>
/// Gets converted pixel buffer.
/// </summary>

12
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -356,6 +356,18 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals
// to uint to avoid sign extension.
if (stream.RemainingBytes < (uint)markerContentByteSize)
{
if (metadataOnly && this.Metadata != null && this.Frame != null)
{
// We have enough data to decode the image, so we can stop parsing.
return;
}
if (this.Metadata != null && this.Frame != null && spectralConverter.HasPixelBuffer())
{
// We have enough data to decode the image, so we can stop parsing.
return;
}
JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
}

22
src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs

@ -50,19 +50,21 @@ internal sealed class DeflateTiffCompression : TiffBaseDecompressor
return left > 0 ? left : 0;
}))
{
deframeStream.AllocateNewBytes(byteCount, true);
DeflateStream dataStream = deframeStream.CompressedStream;
int totalRead = 0;
while (totalRead < buffer.Length)
if (deframeStream.AllocateNewBytes(byteCount, true))
{
int bytesRead = dataStream.Read(buffer, totalRead, buffer.Length - totalRead);
if (bytesRead <= 0)
DeflateStream? dataStream = deframeStream.CompressedStream;
int totalRead = 0;
while (totalRead < buffer.Length)
{
break;
}
int bytesRead = dataStream.Read(buffer, totalRead, buffer.Length - totalRead);
if (bytesRead <= 0)
{
break;
}
totalRead += bytesRead;
totalRead += bytesRead;
}
}
}

11
src/ImageSharp/Memory/Allocators/Internals/Gen2GcCallback.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
// Port of BCL internal utility:
// https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/System.Private.CoreLib/src/System/Gen2GcCallback.cs
@ -15,8 +14,8 @@ namespace SixLabors.ImageSharp.Memory.Internals;
/// </summary>
internal sealed class Gen2GcCallback : CriticalFinalizerObject
{
private readonly Func<bool> callback0;
private readonly Func<object, bool> callback1;
private readonly Func<bool>? callback0;
private readonly Func<object, bool>? callback1;
private GCHandle weakTargetObj;
private Gen2GcCallback(Func<bool> callback) => this.callback0 = callback;
@ -32,7 +31,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject
if (this.weakTargetObj.IsAllocated)
{
// Check to see if the target object is still alive.
object targetObj = this.weakTargetObj.Target;
object? targetObj = this.weakTargetObj.Target;
if (targetObj == null)
{
// The target object is dead, so this callback object is no longer needed.
@ -43,7 +42,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject
// Execute the callback method.
try
{
if (!this.callback1(targetObj))
if (!this.callback1!(targetObj))
{
// If the callback returns false, this callback object is no longer needed.
this.weakTargetObj.Free();
@ -64,7 +63,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject
// Execute the callback method.
try
{
if (!this.callback0())
if (!this.callback0!())
{
// If the callback returns false, this callback object is no longer needed.
return;

3
src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.Diagnostics;
@ -15,7 +14,7 @@ internal abstract class RefCountedMemoryLifetimeGuard : IDisposable
private int refCount = 1;
private int disposed;
private int released;
private string allocationStackTrace;
private string? allocationStackTrace;
protected RefCountedMemoryLifetimeGuard()
{

15
src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -22,7 +22,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
this.lifetimeGuard = new LifetimeGuard(this.Array);
}
public byte[] Array { get; private set; }
public byte[]? Array { get; private set; }
protected override void Dispose(bool disposing)
{
@ -41,7 +41,11 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
return MemoryMarshal.Cast<byte, T>(this.Array.AsSpan(0, this.lengthInBytes));
}
protected override object GetPinnableObject() => this.Array;
protected override object GetPinnableObject()
{
this.CheckDisposed();
return this.Array;
}
public void AddRef()
{
@ -52,6 +56,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
public void ReleaseRef() => this.lifetimeGuard.ReleaseRef();
[Conditional("DEBUG")]
[MemberNotNull(nameof(Array))]
private void CheckDisposed()
{
if (this.Array == null)
@ -62,7 +67,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard
{
private byte[] array;
private byte[]? array;
public LifetimeGuard(byte[] array) => this.array = array;
@ -73,7 +78,7 @@ internal class SharedArrayPoolBuffer<T> : ManagedBufferBase<T>, IRefCounted
// This is not ideal, but subsequent leaks will end up returning arrays to per-cpu buckets,
// meaning likely a different bucket than it was rented from,
// but this is PROBABLY better than not returning the arrays at all.
ArrayPool<byte>.Shared.Return(this.array);
ArrayPool<byte>.Shared.Return(this.array!);
this.array = null;
}
}

7
src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics;
@ -13,7 +12,7 @@ internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedEx
{
private static int minTrimPeriodMilliseconds = int.MaxValue;
private static readonly List<WeakReference<UniformUnmanagedMemoryPool>> AllPools = new();
private static Timer trimTimer;
private static Timer? trimTimer;
private static readonly Stopwatch Stopwatch = Stopwatch.StartNew();
@ -97,7 +96,7 @@ internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedEx
/// <summary>
/// Rent <paramref name="bufferCount"/> buffers or return 'null' if the pool is full.
/// </summary>
public UnmanagedMemoryHandle[] Rent(int bufferCount)
public UnmanagedMemoryHandle[]? Rent(int bufferCount)
{
UnmanagedMemoryHandle[] buffersLocal = this.buffers;
@ -248,7 +247,7 @@ internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedEx
foreach (WeakReference<UniformUnmanagedMemoryPool> weakPoolRef in AllPools)
{
if (weakPoolRef.TryGetTarget(out UniformUnmanagedMemoryPool pool))
if (weakPoolRef.TryGetTarget(out UniformUnmanagedMemoryPool? pool))
{
pool.Trim();
}

11
src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Runtime.InteropServices;
@ -20,7 +19,7 @@ internal struct UnmanagedMemoryHandle : IEquatable<UnmanagedMemoryHandle>
private static long totalOomRetries;
// A Monitor to wait/signal when we are low on memory.
private static object lowMemoryMonitor;
private static object? lowMemoryMonitor;
public static readonly UnmanagedMemoryHandle NullHandle;
@ -114,9 +113,9 @@ internal struct UnmanagedMemoryHandle : IEquatable<UnmanagedMemoryHandle>
if (Volatile.Read(ref lowMemoryMonitor) != null)
{
// We are low on memory. Signal all threads waiting in AllocateHandle().
Monitor.Enter(lowMemoryMonitor);
Monitor.PulseAll(lowMemoryMonitor);
Monitor.Exit(lowMemoryMonitor);
Monitor.Enter(lowMemoryMonitor!);
Monitor.PulseAll(lowMemoryMonitor!);
Monitor.Exit(lowMemoryMonitor!);
}
this.lengthInBytes = 0;
@ -124,7 +123,7 @@ internal struct UnmanagedMemoryHandle : IEquatable<UnmanagedMemoryHandle>
public bool Equals(UnmanagedMemoryHandle other) => this.handle.Equals(other.handle);
public override bool Equals(object obj) => obj is UnmanagedMemoryHandle other && this.Equals(other);
public override bool Equals(object? obj) => obj is UnmanagedMemoryHandle other && this.Equals(other);
public override int GetHashCode() => this.handle.GetHashCode();
}

2
src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs

@ -135,7 +135,7 @@ internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocato
}
// Attempt to rent the whole group from the pool, allocate a group of unmanaged buffers if the attempt fails:
if (MemoryGroup<T>.TryAllocate(this.pool, totalLength, bufferAlignment, options, out MemoryGroup<T> poolGroup))
if (MemoryGroup<T>.TryAllocate(this.pool, totalLength, bufferAlignment, options, out MemoryGroup<T>? poolGroup))
{
return poolGroup;
}

2
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs

@ -106,7 +106,7 @@ internal static class MemoryGroupExtensions
}
}
internal static void CopyTo<T>(this IMemoryGroup<T> source, IMemoryGroup<T> target)
internal static void CopyTo<T>(this IMemoryGroup<T>? source, IMemoryGroup<T>? target)
where T : struct
{
Guard.NotNull(source, nameof(source));

2
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Memory;
internal unsafe struct MemoryGroupSpanCache
{
public SpanCacheMode Mode;
public byte[] SingleArray;
public byte[]? SingleArray;
public void* SinglePointer;
public void*[] MultiPointer;

6
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory;
@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory;
internal class MemoryGroupView<T> : IMemoryGroup<T>
where T : struct
{
private MemoryGroup<T> owner;
private MemoryGroup<T>? owner;
private readonly MemoryOwnerWrapper[] memoryWrappers;
public MemoryGroupView(MemoryGroup<T> owner)
@ -63,6 +63,7 @@ internal class MemoryGroupView<T> : IMemoryGroup<T>
}
}
[MemberNotNullWhen(true, nameof(owner))]
public bool IsValid => this.owner != null;
public Memory<T> this[int index]
@ -99,6 +100,7 @@ internal class MemoryGroupView<T> : IMemoryGroup<T>
this.owner = null;
}
[MemberNotNull(nameof(owner))]
private void EnsureIsValid()
{
if (!this.IsValid)

10
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs

@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory.Internals;
@ -15,8 +15,8 @@ internal abstract partial class MemoryGroup<T>
/// </summary>
public sealed class Owned : MemoryGroup<T>, IEnumerable<Memory<T>>
{
private IMemoryOwner<T>[] memoryOwners;
private RefCountedMemoryLifetimeGuard groupLifetimeGuard;
private IMemoryOwner<T>[]? memoryOwners;
private RefCountedMemoryLifetimeGuard? groupLifetimeGuard;
public Owned(IMemoryOwner<T>[] memoryOwners, int bufferLength, long totalLength, bool swappable)
: base(bufferLength, totalLength)
@ -149,7 +149,7 @@ internal abstract partial class MemoryGroup<T>
}
else
{
foreach (IMemoryOwner<T> memoryOwner in this.memoryOwners)
foreach (IMemoryOwner<T> memoryOwner in this.memoryOwners!)
{
memoryOwner.Dispose();
}
@ -161,6 +161,7 @@ internal abstract partial class MemoryGroup<T>
}
[MethodImpl(InliningOptions.ShortMethod)]
[MemberNotNull(nameof(memoryOwners))]
private void EnsureNotDisposed()
{
if (this.memoryOwners is null)
@ -170,6 +171,7 @@ internal abstract partial class MemoryGroup<T>
}
[MethodImpl(MethodImplOptions.NoInlining)]
[DoesNotReturn]
private static void ThrowObjectDisposedException() => throw new ObjectDisposedException(nameof(MemoryGroup<T>));
// When the MemoryGroup points to multiple buffers via `groupLifetimeGuard`,

8
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory.Internals;
@ -41,7 +41,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
/// <inheritdoc />
public bool IsValid { get; private set; } = true;
public MemoryGroupView<T> View { get; private set; }
public MemoryGroupView<T> View { get; private set; } = null!;
/// <inheritdoc />
public abstract Memory<T> this[int index] { get; }
@ -150,7 +150,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
long totalLengthInElements,
int bufferAlignmentInElements,
AllocationOptions options,
out MemoryGroup<T> memoryGroup)
[NotNullWhen(true)] out MemoryGroup<T>? memoryGroup)
{
Guard.NotNull(pool, nameof(pool));
Guard.MustBeGreaterThanOrEqualTo(totalLengthInElements, 0, nameof(totalLengthInElements));
@ -188,7 +188,7 @@ internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
bufferCount++;
}
UnmanagedMemoryHandle[] arrays = pool.Rent(bufferCount);
UnmanagedMemoryHandle[]? arrays = pool.Rent(bufferCount);
if (arrays == null)
{

2
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs

@ -54,6 +54,8 @@ public class DecodeJpegParseStreamOnly
{
}
public override bool HasPixelBuffer() => throw new NotImplementedException();
public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
{
}

9
tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs

@ -46,15 +46,6 @@ public class JFifMarkerTests
Assert.Equal(default, marker);
}
[Fact]
public void MarkerIgnoresCorrectHeaderButInvalidDensities()
{
bool isJFif = JFifMarker.TryParse(this.bytes3, out JFifMarker marker);
Assert.False(isJFif);
Assert.Equal(default, marker);
}
[Fact]
public void MarkerEqualityIsCorrect()
{

11
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -300,4 +300,15 @@ public partial class JpegDecoderTests
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
// https://github.com/SixLabors/ImageSharp/issues/2315
[Theory]
[WithFile(TestImages.Jpeg.Issues.Issue2315_NotEnoughBytes, PixelTypes.Rgba32)]
public void Issue2315_DecodeWorks<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
}

2
tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs

@ -187,6 +187,8 @@ public class SpectralJpegTests
}
}
public override bool HasPixelBuffer() => throw new NotImplementedException();
public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
{
this.frame = frame;

1
tests/ImageSharp.Tests/TestImages.cs

@ -281,6 +281,7 @@ public static class TestImages
public const string ValidExifArgumentNullExceptionOnEncode = "Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg";
public const string Issue2133_DeduceColorSpace = "Jpg/issues/Issue2133.jpg";
public const string Issue2136_ScanMarkerExtraneousBytes = "Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg";
public const string Issue2315_NotEnoughBytes = "Jpg/issues/issue-2315.jpg";
public static class Fuzz
{

3
tests/Images/Input/Jpg/issues/issue-2315.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f129b057efb499d492e9afcffdd98de62aac1e04b97a09a75b4799ba498cd3c1
size 319056
Loading…
Cancel
Save