Browse Source

PinnedBuffer<T> ==> Buffer<T> with explicit pinning capability

pull/174/head
Anton Firszov 9 years ago
parent
commit
615163d360
  1. 2
      src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs
  2. 2
      src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs
  3. 2
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  4. 2
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs
  5. 2
      src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs
  6. 2
      src/ImageSharp/Colors/Color.BulkOperations.cs
  7. 89
      src/ImageSharp/Common/Memory/Buffer.cs
  8. 22
      src/ImageSharp/Common/Memory/Buffer2D.cs
  9. 2
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  10. 4
      src/ImageSharp/Common/Memory/IBuffer2D.cs
  11. 6
      src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs
  12. 18
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  13. 6
      src/ImageSharp/Image/PixelArea{TColor}.cs
  14. 6
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
  15. 4
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  16. 14
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs
  17. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
  18. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  19. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
  20. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
  21. 6
      tests/ImageSharp.Benchmarks/General/ClearBuffer.cs
  22. 7
      tests/ImageSharp.Benchmarks/General/IterateArray.cs
  23. 10
      tests/ImageSharp.Benchmarks/General/PixelIndexing.cs
  24. 16
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  25. 27
      tests/ImageSharp.Tests/Common/Buffer2DTests.cs
  26. 6
      tests/ImageSharp.Tests/Common/BufferSpanTests.cs
  27. 93
      tests/ImageSharp.Tests/Common/BufferTests.cs

2
src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs

@ -117,7 +117,7 @@ namespace ImageSharp.Drawing.Brushes
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs

@ -150,7 +150,7 @@ namespace ImageSharp.Drawing.Brushes
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors
{
DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs

@ -141,7 +141,7 @@ namespace ImageSharp.Drawing.Brushes
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs

@ -89,7 +89,7 @@ namespace ImageSharp.Drawing.Brushes
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp/Colors/Color.BulkOperations.cs

@ -64,7 +64,7 @@ namespace ImageSharp
ref uint src = ref Unsafe.As<Color, uint>(ref sourceColors.DangerousGetPinnableReference());
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>(
using (Buffer<uint> tempBuf = new Buffer<uint>(
unpackedRawCount + Vector<uint>.Count))
{
uint[] temp = tempBuf.Array;

89
src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs → src/ImageSharp/Common/Memory/Buffer.cs

@ -1,4 +1,4 @@
// <copyright file="PinnedBuffer{T}.cs" company="James Jackson-South">
// <copyright file="Buffer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -11,13 +11,18 @@ namespace ImageSharp
using System.Runtime.InteropServices;
/// <summary>
/// Manages a pinned buffer of value type objects as a Disposable resource.
/// Manages a buffer of value type objects as a Disposable resource.
/// The backing array is either pooled or comes from the outside.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal class PinnedBuffer<T> : IDisposable
internal class Buffer<T> : IDisposable
where T : struct
{
/// <summary>
/// A pointer to the first element of <see cref="Array"/> when pinned.
/// </summary>
private IntPtr pointer;
/// <summary>
/// A handle that allows to access the managed <see cref="Array"/> as an unmanaged memory by pinning.
/// </summary>
@ -25,40 +30,38 @@ namespace ImageSharp
/// <summary>
/// A value indicating wheter <see cref="Array"/> should be returned to <see cref="PixelDataPool{T}"/>
/// when disposing this <see cref="PinnedBuffer{T}"/> instance.
/// when disposing this <see cref="Buffer{T}"/> instance.
/// </summary>
private bool isPoolingOwner;
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary>
/// <param name="length">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int length)
public Buffer(int length)
{
this.Length = length;
this.Array = PixelDataPool<T>.Rent(length);
this.isPoolingOwner = true;
this.Pin();
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary>
/// <param name="array">The array to pin.</param>
public PinnedBuffer(T[] array)
public Buffer(T[] array)
{
this.Length = array.Length;
this.Array = array;
this.isPoolingOwner = false;
this.Pin();
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary>
/// <param name="array">The array to pin.</param>
/// <param name="length">The count of "relevant" elements in 'array'.</param>
public PinnedBuffer(T[] array, int length)
public Buffer(T[] array, int length)
{
if (array.Length < length)
{
@ -68,19 +71,18 @@ namespace ImageSharp
this.Length = length;
this.Array = array;
this.isPoolingOwner = false;
this.Pin();
}
/// <summary>
/// Finalizes an instance of the <see cref="PinnedBuffer{T}"/> class.
/// Finalizes an instance of the <see cref="Buffer{T}"/> class.
/// </summary>
~PinnedBuffer()
~Buffer()
{
this.UnPin();
}
/// <summary>
/// Gets a value indicating whether this <see cref="PinnedBuffer{T}"/> instance is disposed, or has lost ownership of <see cref="Array"/>.
/// Gets a value indicating whether this <see cref="Buffer{T}"/> instance is disposed, or has lost ownership of <see cref="Array"/>.
/// </summary>
public bool IsDisposedOrLostArrayOwnership { get; private set; }
@ -94,11 +96,6 @@ namespace ImageSharp
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets a pointer to the pinned <see cref="Array"/>.
/// </summary>
public IntPtr Pointer { get; private set; }
/// <summary>
/// Gets a <see cref="BufferSpan{T}"/> to the backing buffer.
/// </summary>
@ -109,37 +106,35 @@ namespace ImageSharp
/// </summary>
/// <param name="index">The index</param>
/// <returns>The reference to the specified element</returns>
public unsafe ref T this[int index]
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
byte* ptr = (byte*)this.Pointer + BufferSpan.SizeOf<T>(index);
return ref Unsafe.AsRef<T>(ptr);
return ref this.Array[index];
}
}
/// <summary>
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferSpan{T}"/>.
/// Converts <see cref="Buffer{T}"/> to an <see cref="BufferSpan{T}"/>.
/// </summary>
/// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator BufferSpan<T>(PinnedBuffer<T> buffer)
public static implicit operator BufferSpan<T>(Buffer<T> buffer)
{
return new BufferSpan<T>(buffer.Array, 0, buffer.Length);
}
/// <summary>
/// Creates a clean instance of <see cref="PinnedBuffer{T}"/> initializing it's elements with 'default(T)'.
/// Creates a clean instance of <see cref="Buffer{T}"/> initializing it's elements with 'default(T)'.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
/// <returns>The <see cref="PinnedBuffer{T}"/> instance</returns>
/// <returns>The <see cref="Buffer{T}"/> instance</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PinnedBuffer<T> CreateClean(int count)
public static Buffer<T> CreateClean(int count)
{
PinnedBuffer<T> buffer = new PinnedBuffer<T>(count);
Buffer<T> buffer = new Buffer<T>(count);
buffer.Clear();
return buffer;
}
@ -168,7 +163,7 @@ namespace ImageSharp
}
/// <summary>
/// Disposes the <see cref="PinnedBuffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
/// Disposes the <see cref="Buffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
@ -199,11 +194,11 @@ namespace ImageSharp
/// </summary>
/// <returns>The unpinned <see cref="Array"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] UnPinAndTakeArrayOwnership()
public T[] TakeArrayOwnership()
{
if (this.IsDisposedOrLostArrayOwnership)
{
throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer<T> is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!");
throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer<T> is disposed or TakeArrayOwnership() has been called multiple times!");
}
this.IsDisposedOrLostArrayOwnership = true;
@ -220,17 +215,29 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
((BufferSpan<T>)this).Clear();
this.Span.Clear();
}
/// <summary>
/// Pins <see cref="Array"/>.
/// </summary>
/// <returns>The pinned pointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Pin()
public IntPtr Pin()
{
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned);
this.Pointer = this.handle.AddrOfPinnedObject();
if (this.IsDisposedOrLostArrayOwnership)
{
throw new InvalidOperationException(
"Pin() is invalid on a buffer with IsDisposedOrLostArrayOwnership == true!");
}
if (this.pointer == IntPtr.Zero)
{
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned);
this.pointer = this.handle.AddrOfPinnedObject();
}
return this.pointer;
}
/// <summary>
@ -239,13 +246,13 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UnPin()
{
if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated)
if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated)
{
return;
}
this.handle.Free();
this.Pointer = IntPtr.Zero;
this.pointer = IntPtr.Zero;
}
}
}

22
src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs → src/ImageSharp/Common/Memory/Buffer2D.cs

@ -1,4 +1,4 @@
// <copyright file="PinnedImageBuffer{T}.cs" company="James Jackson-South">
// <copyright file="Buffer2D{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -9,19 +9,19 @@ namespace ImageSharp
using System.Runtime.CompilerServices;
/// <summary>
/// Represents a pinned buffer of value type objects
/// Represents a buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal class PinnedImageBuffer<T> : PinnedBuffer<T>, IPinnedImageBuffer<T>
internal class Buffer2D<T> : Buffer<T>, IBuffer2D<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="PinnedImageBuffer{T}"/> class.
/// Initializes a new instance of the <see cref="Buffer2D{T}"/> class.
/// </summary>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
public PinnedImageBuffer(int width, int height)
public Buffer2D(int width, int height)
: base(width * height)
{
this.Width = width;
@ -29,12 +29,12 @@ namespace ImageSharp
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedImageBuffer{T}"/> class.
/// Initializes a new instance of the <see cref="Buffer2D{T}"/> class.
/// </summary>
/// <param name="array">The array to pin</param>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
public PinnedImageBuffer(T[] array, int width, int height)
public Buffer2D(T[] array, int width, int height)
: base(array, width * height)
{
this.Width = width;
@ -63,14 +63,14 @@ namespace ImageSharp
}
/// <summary>
/// Creates a clean instance of <see cref="PinnedImageBuffer{T}"/> initializing it's elements with 'default(T)'.
/// Creates a clean instance of <see cref="Buffer2D{T}"/> initializing it's elements with 'default(T)'.
/// </summary>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
/// <returns>The <see cref="PinnedBuffer{T}"/> instance</returns>
public static PinnedImageBuffer<T> CreateClean(int width, int height)
/// <returns>The <see cref="Buffer{T}"/> instance</returns>
public static Buffer2D<T> CreateClean(int width, int height)
{
PinnedImageBuffer<T> buffer = new PinnedImageBuffer<T>(width, height);
Buffer2D<T> buffer = new Buffer2D<T>(width, height);
buffer.Clear();
return buffer;
}

2
src/ImageSharp/Common/Memory/BufferSpan{T}.cs

@ -12,7 +12,7 @@ namespace ImageSharp
/// <summary>
/// Represents a contiguous region of a pinned managed array.
/// The array is usually owned by a <see cref="PinnedBuffer{T}"/> instance.
/// The array is usually owned by a <see cref="Buffer{T}"/> instance.
/// </summary>
/// <remarks>
/// <see cref="BufferSpan{T}"/> is very similar to corefx System.Span&lt;T&gt;, and we try to maintain a compatible API.

4
src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs → src/ImageSharp/Common/Memory/IBuffer2D.cs

@ -1,4 +1,4 @@
// <copyright file="IPinnedImageBuffer{T}.cs" company="James Jackson-South">
// <copyright file="IBuffer2D{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -10,7 +10,7 @@ namespace ImageSharp
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal interface IPinnedImageBuffer<T>
internal interface IBuffer2D<T>
where T : struct
{
/// <summary>

6
src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using System.Runtime.CompilerServices;
/// <summary>
/// Defines extension methods for <see cref="IPinnedImageBuffer{T}"/>.
/// Defines extension methods for <see cref="IBuffer2D{T}"/>.
/// </summary>
internal static class PinnedImageBufferExtensions
{
@ -22,7 +22,7 @@ namespace ImageSharp
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferSpan<T> GetRowSpan<T>(this IPinnedImageBuffer<T> buffer, int x, int y)
public static BufferSpan<T> GetRowSpan<T>(this IBuffer2D<T> buffer, int x, int y)
where T : struct
{
return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x);
@ -36,7 +36,7 @@ namespace ImageSharp
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferSpan<T> GetRowSpan<T>(this IPinnedImageBuffer<T> buffer, int y)
public static BufferSpan<T> GetRowSpan<T>(this IBuffer2D<T> buffer, int y)
where T : struct
{
return buffer.Span.Slice(y * buffer.Width, buffer.Width);

18
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -15,7 +15,7 @@ namespace ImageSharp
/// Provides per-pixel access to generic <see cref="Image{TColor}"/> pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public sealed class PixelAccessor<TColor> : IDisposable, IPinnedImageBuffer<TColor>
public sealed class PixelAccessor<TColor> : IDisposable, IBuffer2D<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>
@ -30,9 +30,9 @@ namespace ImageSharp
private bool isDisposed;
/// <summary>
/// The <see cref="PinnedBuffer{T}"/> containing the pixel data.
/// The <see cref="Buffer{T}"/> containing the pixel data.
/// </summary>
private PinnedImageBuffer<TColor> pixelBuffer;
private Buffer2D<TColor> pixelBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
@ -54,7 +54,7 @@ namespace ImageSharp
/// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
public PixelAccessor(int width, int height)
: this(width, height, PinnedImageBuffer<TColor>.CreateClean(width, height))
: this(width, height, Buffer2D<TColor>.CreateClean(width, height))
{
}
@ -64,7 +64,7 @@ namespace ImageSharp
/// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param>
private PixelAccessor(int width, int height, PinnedImageBuffer<TColor> pixels)
private PixelAccessor(int width, int height, Buffer2D<TColor> pixels)
{
Guard.NotNull(pixels, nameof(pixels));
Guard.MustBeGreaterThan(width, 0, nameof(width));
@ -114,7 +114,7 @@ namespace ImageSharp
public ParallelOptions ParallelOptions { get; }
/// <inheritdoc />
BufferSpan<TColor> IPinnedImageBuffer<TColor>.Span => this.pixelBuffer;
BufferSpan<TColor> IBuffer2D<TColor>.Span => this.pixelBuffer;
private static BulkPixelOperations<TColor> Operations => BulkPixelOperations<TColor>.Instance;
@ -239,7 +239,7 @@ namespace ImageSharp
/// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelDataPool.Return()"/> is called.</remarks>
internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels)
{
TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership();
TColor[] oldPixels = this.pixelBuffer.TakeArrayOwnership();
this.SetPixelBufferUnsafe(width, height, pixels);
return oldPixels;
}
@ -410,7 +410,7 @@ namespace ImageSharp
private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels)
{
this.SetPixelBufferUnsafe(width, height, new PinnedImageBuffer<TColor>(pixels, width, height));
this.SetPixelBufferUnsafe(width, height, new Buffer2D<TColor>(pixels, width, height));
}
/// <summary>
@ -419,7 +419,7 @@ namespace ImageSharp
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixel buffer</param>
private void SetPixelBufferUnsafe(int width, int height, PinnedImageBuffer<TColor> pixels)
private void SetPixelBufferUnsafe(int width, int height, Buffer2D<TColor> pixels)
{
this.pixelBuffer = pixels;

6
src/ImageSharp/Image/PixelArea{TColor}.cs

@ -30,7 +30,7 @@ namespace ImageSharp
/// <summary>
/// The underlying buffer containing the raw pixel data.
/// </summary>
private PinnedBuffer<byte> byteBuffer;
private Buffer<byte> byteBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
@ -66,7 +66,7 @@ namespace ImageSharp
this.RowStride = width * GetComponentCount(componentOrder);
this.Length = bytes.Length; // TODO: Is this the right value for Length?
this.byteBuffer = new PinnedBuffer<byte>(bytes);
this.byteBuffer = new Buffer<byte>(bytes);
}
/// <summary>
@ -116,7 +116,7 @@ namespace ImageSharp
this.RowStride = (width * GetComponentCount(componentOrder)) + padding;
this.Length = this.RowStride * height;
this.byteBuffer = new PinnedBuffer<byte>(this.Length);
this.byteBuffer = new Buffer<byte>(this.Length);
}
/// <summary>

6
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs

@ -107,7 +107,7 @@ namespace ImageSharp.Processing.Processors
/// <param name="x">The column position</param>
/// <returns>The weighted sum</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer<Vector4> firstPassPixels, int x)
public Vector4 ComputeWeightedColumnSum(Buffer2D<Vector4> firstPassPixels, int x)
{
ref float verticalValues = ref this.Ptr;
int left = this.Left;
@ -131,7 +131,7 @@ namespace ImageSharp.Processing.Processors
/// </summary>
internal class WeightsBuffer : IDisposable
{
private PinnedImageBuffer<float> dataBuffer;
private Buffer2D<float> dataBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="WeightsBuffer"/> class.
@ -140,7 +140,7 @@ namespace ImageSharp.Processing.Processors
/// <param name="destinationSize">The size of the destination window</param>
public WeightsBuffer(int sourceSize, int destinationSize)
{
this.dataBuffer = PinnedImageBuffer<float>.CreateClean(sourceSize, destinationSize);
this.dataBuffer = Buffer2D<float>.CreateClean(sourceSize, destinationSize);
this.Weights = new WeightsWindow[destinationSize];
}

4
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -106,7 +106,7 @@ namespace ImageSharp.Processing.Processors
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PinnedImageBuffer<Vector4> firstPassPixels = new PinnedImageBuffer<Vector4>(width, source.Height))
using (Buffer2D<Vector4> firstPassPixels = new Buffer2D<Vector4>(width, source.Height))
{
firstPassPixels.Clear();
@ -117,7 +117,7 @@ namespace ImageSharp.Processing.Processors
y =>
{
// TODO: Without Parallel.For() this buffer object could be reused:
using (PinnedBuffer<Vector4> tempRowBuffer = new PinnedBuffer<Vector4>(sourcePixels.Width))
using (Buffer<Vector4> tempRowBuffer = new Buffer<Vector4>(sourcePixels.Width))
{
BufferSpan<TColor> sourceRow = sourcePixels.GetRowSpan(y);

14
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs

@ -14,9 +14,9 @@
/// </summary>
public unsafe class PackFromVector4ReferenceVsPointer
{
private PinnedBuffer<ImageSharp.Color> destination;
private Buffer<ImageSharp.Color> destination;
private PinnedBuffer<Vector4> source;
private Buffer<Vector4> source;
[Params(16, 128, 1024)]
public int Count { get; set; }
@ -24,8 +24,10 @@
[Setup]
public void Setup()
{
this.destination = new PinnedBuffer<ImageSharp.Color>(this.Count);
this.source = new PinnedBuffer<Vector4>(this.Count * 4);
this.destination = new Buffer<ImageSharp.Color>(this.Count);
this.source = new Buffer<Vector4>(this.Count * 4);
this.source.Pin();
this.destination.Pin();
}
[Cleanup]
@ -38,8 +40,8 @@
[Benchmark(Baseline = true)]
public void PackUsingPointers()
{
Vector4* sp = (Vector4*)this.source.Pointer;
byte* dp = (byte*)this.destination.Pointer;
Vector4* sp = (Vector4*)this.source.Pin();
byte* dp = (byte*)this.destination.Pin();
int count = this.Count;
int size = sizeof(ImageSharp.Color);

8
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs

@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class PackFromXyzw<TColor>
where TColor : struct, IPixel<TColor>
{
private PinnedBuffer<TColor> destination;
private Buffer<TColor> destination;
private PinnedBuffer<byte> source;
private Buffer<byte> source;
[Params(16, 128, 1024)]
public int Count { get; set; }
@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup]
public void Setup()
{
this.destination = new PinnedBuffer<TColor>(this.Count);
this.source = new PinnedBuffer<byte>(this.Count * 4);
this.destination = new Buffer<TColor>(this.Count);
this.source = new Buffer<byte>(this.Count * 4);
}
[Cleanup]

8
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs

@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class ToVector4<TColor>
where TColor : struct, IPixel<TColor>
{
private PinnedBuffer<TColor> source;
private Buffer<TColor> source;
private PinnedBuffer<Vector4> destination;
private Buffer<Vector4> destination;
[Params(64, 300, 1024)]
public int Count { get; set; }
@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup]
public void Setup()
{
this.source = new PinnedBuffer<TColor>(this.Count);
this.destination = new PinnedBuffer<Vector4>(this.Count);
this.source = new Buffer<TColor>(this.Count);
this.destination = new Buffer<Vector4>(this.Count);
}
[Cleanup]

8
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs

@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class ToXyz<TColor>
where TColor : struct, IPixel<TColor>
{
private PinnedBuffer<TColor> source;
private Buffer<TColor> source;
private PinnedBuffer<byte> destination;
private Buffer<byte> destination;
[Params(16, 128, 1024)]
public int Count { get; set; }
@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup]
public void Setup()
{
this.source = new PinnedBuffer<TColor>(this.Count);
this.destination = new PinnedBuffer<byte>(this.Count * 3);
this.source = new Buffer<TColor>(this.Count);
this.destination = new Buffer<byte>(this.Count * 3);
}
[Cleanup]

8
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs

@ -13,9 +13,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class ToXyzw<TColor>
where TColor : struct, IPixel<TColor>
{
private PinnedBuffer<TColor> source;
private Buffer<TColor> source;
private PinnedBuffer<byte> destination;
private Buffer<byte> destination;
[Params(16, 128, 1024)]
public int Count { get; set; }
@ -23,8 +23,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup]
public void Setup()
{
this.source = new PinnedBuffer<TColor>(this.Count);
this.destination = new PinnedBuffer<byte>(this.Count * 4);
this.source = new Buffer<TColor>(this.Count);
this.destination = new Buffer<byte>(this.Count * 4);
}
[Cleanup]

6
tests/ImageSharp.Benchmarks/General/ClearBuffer.cs

@ -11,7 +11,7 @@ namespace ImageSharp.Benchmarks.General
public unsafe class ClearBuffer
{
private PinnedBuffer<Color> buffer;
private Buffer<Color> buffer;
[Params(32, 128, 512)]
public int Count { get; set; }
@ -19,7 +19,7 @@ namespace ImageSharp.Benchmarks.General
[Setup]
public void Setup()
{
this.buffer = new PinnedBuffer<ImageSharp.Color>(this.Count);
this.buffer = new Buffer<ImageSharp.Color>(this.Count);
}
[Cleanup]
@ -37,7 +37,7 @@ namespace ImageSharp.Benchmarks.General
[Benchmark]
public void Unsafe_InitBlock()
{
Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint));
Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count*sizeof(uint));
}
}
}

7
tests/ImageSharp.Benchmarks/General/IterateArray.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Benchmarks.General
public class IterateArray
{
// Usual pinned stuff
private PinnedBuffer<Vector4> buffer;
private Buffer<Vector4> buffer;
// An array that's not pinned by intent!
private Vector4[] array;
@ -19,7 +19,8 @@ namespace ImageSharp.Benchmarks.General
[Setup]
public void Setup()
{
this.buffer = new PinnedBuffer<Vector4>(this.Length);
this.buffer = new Buffer<Vector4>(this.Length);
this.buffer.Pin();
this.array = new Vector4[this.Length];
}
@ -41,7 +42,7 @@ namespace ImageSharp.Benchmarks.General
{
Vector4 sum = new Vector4();
Vector4* ptr = (Vector4*) this.buffer.Pointer;
Vector4* ptr = (Vector4*) this.buffer.Pin();
Vector4* end = ptr + this.Length;
for (; ptr < end; ptr++)

10
tests/ImageSharp.Benchmarks/General/PixelIndexing.cs

@ -29,9 +29,9 @@
private int width;
public Data(PinnedImageBuffer<Vector4> buffer)
public Data(Buffer2D<Vector4> buffer)
{
this.pointer = (Vector4*)buffer.Pointer;
this.pointer = (Vector4*)buffer.Pin();
this.pinnable = Unsafe.As<Pinnable<Vector4>>(buffer.Array);
this.array = buffer.Array;
this.width = buffer.Width;
@ -126,7 +126,7 @@
}
}
internal PinnedImageBuffer<Vector4> buffer;
internal Buffer2D<Vector4> buffer;
protected int width;
@ -147,8 +147,8 @@
public void Setup()
{
this.width = 2048;
this.buffer = new PinnedImageBuffer<Vector4>(2048, 2048);
this.pointer = (Vector4*)this.buffer.Pointer;
this.buffer = new Buffer2D<Vector4>(2048, 2048);
this.pointer = (Vector4*)this.buffer.Pin();
this.array = this.buffer.Array;
this.pinnable = Unsafe.As<Pinnable<Vector4>>(this.array);

16
tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs

@ -45,8 +45,8 @@ namespace ImageSharp.Tests.Colors
int times = 200000;
int count = 1024;
using (PinnedBuffer<ImageSharp.Color> source = new PinnedBuffer<ImageSharp.Color>(count))
using (PinnedBuffer<Vector4> dest = new PinnedBuffer<Vector4>(count))
using (Buffer<ImageSharp.Color> source = new Buffer<ImageSharp.Color>(count))
using (Buffer<Vector4> dest = new Buffer<Vector4>(count))
{
this.Measure(
times,
@ -310,18 +310,18 @@ namespace ImageSharp.Tests.Colors
where TSource : struct
where TDest : struct
{
public PinnedBuffer<TSource> SourceBuffer { get; }
public PinnedBuffer<TDest> ActualDestBuffer { get; }
public PinnedBuffer<TDest> ExpectedDestBuffer { get; }
public Buffer<TSource> SourceBuffer { get; }
public Buffer<TDest> ActualDestBuffer { get; }
public Buffer<TDest> ExpectedDestBuffer { get; }
public BufferSpan<TSource> Source => this.SourceBuffer;
public BufferSpan<TDest> ActualDest => this.ActualDestBuffer;
public TestBuffers(TSource[] source, TDest[] expectedDest)
{
this.SourceBuffer = new PinnedBuffer<TSource>(source);
this.ExpectedDestBuffer = new PinnedBuffer<TDest>(expectedDest);
this.ActualDestBuffer = new PinnedBuffer<TDest>(expectedDest.Length);
this.SourceBuffer = new Buffer<TSource>(source);
this.ExpectedDestBuffer = new Buffer<TDest>(expectedDest);
this.ActualDestBuffer = new Buffer<TDest>(expectedDest.Length);
}
public void Dispose()

27
tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs → tests/ImageSharp.Tests/Common/Buffer2DTests.cs

@ -8,19 +8,18 @@ namespace ImageSharp.Tests.Common
using static TestStructs;
public unsafe class PinnedImageBufferTests
public unsafe class Buffer2DTests
{
// ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert
{
public static void SpanPointsTo<T>(IntPtr ptr, BufferSpan<T> span)
public static void SpanPointsTo<T>(BufferSpan<T> span, Buffer<T> buffer, int bufferOffset = 0)
where T : struct
{
ref byte r = ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference());
ref T actual = ref span.DangerousGetPinnableReference();
ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset);
void* p = Unsafe.AsPointer(ref r);
Assert.Equal(ptr, (IntPtr)p);
Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position");
}
}
@ -29,7 +28,7 @@ namespace ImageSharp.Tests.Common
[InlineData(1025, 17)]
public void Construct(int width, int height)
{
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height))
using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{
Assert.Equal(width, buffer.Width);
Assert.Equal(height, buffer.Height);
@ -43,7 +42,7 @@ namespace ImageSharp.Tests.Common
public void Construct_FromExternalArray(int width, int height)
{
Foo[] array = new Foo[width * height + 10];
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(array, width, height))
using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(array, width, height))
{
Assert.Equal(width, buffer.Width);
Assert.Equal(height, buffer.Height);
@ -57,7 +56,7 @@ namespace ImageSharp.Tests.Common
{
for (int i = 0; i < 100; i++)
{
using (PinnedImageBuffer<int> buffer = PinnedImageBuffer<int>.CreateClean(42, 42))
using (Buffer2D<int> buffer = Buffer2D<int>.CreateClean(42, 42))
{
for (int j = 0; j < buffer.Length; j++)
{
@ -74,13 +73,13 @@ namespace ImageSharp.Tests.Common
[InlineData(17, 42, 41)]
public void GetRowSpanY(int width, int height, int y)
{
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height))
using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{
BufferSpan<Foo> span = buffer.GetRowSpan(y);
Assert.Equal(width * y, span.Start);
Assert.Equal(width, span.Length);
Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * width * y, span);
Assert.SpanPointsTo(span, buffer, width * y);
}
}
@ -90,13 +89,13 @@ namespace ImageSharp.Tests.Common
[InlineData(17, 42, 0, 41)]
public void GetRowSpanXY(int width, int height, int x, int y)
{
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height))
using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{
BufferSpan<Foo> span = buffer.GetRowSpan(x, y);
Assert.Equal(width * y + x, span.Start);
Assert.Equal(width - x, span.Length);
Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * (width * y + x), span);
Assert.SpanPointsTo(span, buffer, width * y + x);
}
}
@ -106,7 +105,7 @@ namespace ImageSharp.Tests.Common
[InlineData(99, 88, 98, 87)]
public void Indexer(int width, int height, int x, int y)
{
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height))
using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{
Foo[] array = buffer.Array;

6
tests/ImageSharp.Tests/Common/BufferSpanTests.cs

@ -28,7 +28,7 @@ namespace ImageSharp.Tests.Common
{
Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) };
using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz))
using (Buffer<Foo> colorBuf = new Buffer<Foo>(fooz))
{
BufferSpan<Foo> orig = colorBuf.Slice(1);
BufferSpan<byte> asBytes = orig.AsBytes();
@ -414,8 +414,8 @@ namespace ImageSharp.Tests.Common
{
Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), };
using (PinnedBuffer<Color> colorBuf = new PinnedBuffer<Color>(colors))
using (PinnedBuffer<byte> byteBuf = new PinnedBuffer<byte>(colors.Length * 4))
using (Buffer<Color> colorBuf = new Buffer<Color>(colors))
using (Buffer<byte> byteBuf = new Buffer<byte>(colors.Length * 4))
{
BufferSpan.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length*sizeof(Color));

93
tests/ImageSharp.Tests/Common/PinnedBufferTests.cs → tests/ImageSharp.Tests/Common/BufferTests.cs

@ -1,4 +1,5 @@
namespace ImageSharp.Tests.Common
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Common
{
using System;
using System.Runtime.CompilerServices;
@ -9,18 +10,23 @@
using static TestStructs;
public unsafe class PinnedBufferTests
public unsafe class BufferTests
{
// ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert
{
public static void SpanPointsTo<T>(IntPtr ptr, BufferSpan<T> span)
public static void SpanPointsTo<T>(BufferSpan<T> span, Buffer<T> buffer, int bufferOffset = 0)
where T : struct
{
ref byte r = ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference());
ref T actual = ref span.DangerousGetPinnableReference();
ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset);
void* p = Unsafe.AsPointer(ref r);
Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position");
}
Assert.Equal(ptr, (IntPtr)p);
public static void Equal(void* expected, void* actual)
{
Assert.Equal((IntPtr)expected, (IntPtr)actual);
}
}
@ -29,14 +35,12 @@
[InlineData(1111)]
public void ConstructWithOwnArray(int count)
{
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(count))
using (Buffer<Foo> buffer = new Buffer<Foo>(count))
{
Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.NotNull(buffer.Array);
Assert.Equal(count, buffer.Length);
Assert.True(buffer.Array.Length >= count);
VerifyPointer(buffer);
}
}
@ -46,13 +50,11 @@
public void ConstructWithExistingArray(int count)
{
Foo[] array = new Foo[count];
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(array))
using (Buffer<Foo> buffer = new Buffer<Foo>(array))
{
Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.Equal(array, buffer.Array);
Assert.Equal(count, buffer.Length);
VerifyPointer(buffer);
}
}
@ -62,7 +64,7 @@
public void Clear(int count)
{
Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } };
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a))
using (Buffer<Foo> buffer = new Buffer<Foo>(a))
{
buffer.Clear();
@ -76,7 +78,7 @@
{
for (int i = 0; i < 100; i++)
{
using (PinnedBuffer<int> buffer = PinnedBuffer<int>.CreateClean(42))
using (Buffer<int> buffer = Buffer<int>.CreateClean(42))
{
for (int j = 0; j < buffer.Length; j++)
{
@ -103,7 +105,7 @@
{
Foo[] a = Foo.CreateArray(length);
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a))
using (Buffer<Foo> buffer = new Buffer<Foo>(a))
{
Foo element = buffer[index];
@ -117,7 +119,7 @@
{
Foo[] a = Foo.CreateArray(length);
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a))
using (Buffer<Foo> buffer = new Buffer<Foo>(a))
{
buffer[index] = new Foo(666, 666);
@ -129,7 +131,7 @@
[Fact]
public void Dispose()
{
PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(42);
Buffer<Foo> buffer = new Buffer<Foo>(42);
buffer.Dispose();
Assert.True(buffer.IsDisposedOrLostArrayOwnership);
@ -140,13 +142,13 @@
[InlineData(123)]
public void CastToSpan(int bufferLength)
{
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(bufferLength))
using (Buffer<Foo> buffer = new Buffer<Foo>(bufferLength))
{
BufferSpan<Foo> span = buffer;
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(0, span.Start);
Assert.SpanPointsTo(buffer.Pointer, span);
Assert.SpanPointsTo(span, buffer);
Assert.Equal(span.Length, bufferLength);
}
}
@ -154,13 +156,13 @@
[Fact]
public void Span()
{
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(42))
using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{
BufferSpan<Foo> span = buffer.Span;
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(0, span.Start);
Assert.SpanPointsTo(buffer.Pointer, span);
Assert.SpanPointsTo(span, buffer);
Assert.Equal(span.Length, 42);
}
}
@ -173,13 +175,13 @@
[InlineData(123, 17)]
public void WithStartOnly(int bufferLength, int start)
{
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(bufferLength))
using (Buffer<Foo> buffer = new Buffer<Foo>(bufferLength))
{
BufferSpan<Foo> span = buffer.Slice(start);
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(start, span.Start);
Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span);
Assert.SpanPointsTo(span, buffer, start);
Assert.Equal(span.Length, bufferLength - start);
}
}
@ -189,13 +191,13 @@
[InlineData(123, 17, 42)]
public void WithStartAndLength(int bufferLength, int start, int spanLength)
{
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(bufferLength))
using (Buffer<Foo> buffer = new Buffer<Foo>(bufferLength))
{
BufferSpan<Foo> span = buffer.Slice(start, spanLength);
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(start, span.Start);
Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span);
Assert.SpanPointsTo(span, buffer, start);
Assert.Equal(span.Length, spanLength);
}
}
@ -205,9 +207,9 @@
public void UnPinAndTakeArrayOwnership()
{
Foo[] data = null;
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(42))
using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{
data = buffer.UnPinAndTakeArrayOwnership();
data = buffer.TakeArrayOwnership();
Assert.True(buffer.IsDisposedOrLostArrayOwnership);
}
@ -215,10 +217,41 @@
Assert.True(data.Length >= 42);
}
private static void VerifyPointer(PinnedBuffer<Foo> buffer)
public class Pin
{
IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]);
Assert.Equal(ptr, buffer.Pointer);
[Fact]
public void ReturnsPinnedPointerToTheBeginningOfArray()
{
using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{
Foo* actual = (Foo*)buffer.Pin();
fixed (Foo* expected = buffer.Array)
{
Assert.Equal(expected, actual);
}
}
}
[Fact]
public void SecondCallReturnsTheSamePointer()
{
using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{
IntPtr ptr1 = buffer.Pin();
IntPtr ptr2 = buffer.Pin();
Assert.Equal(ptr1, ptr2);
}
}
[Fact]
public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException()
{
Buffer<Foo> buffer = new Buffer<Foo>(42);
buffer.Dispose();
Assert.Throws<InvalidOperationException>(() => buffer.Pin());
}
}
}
}
Loading…
Cancel
Save