Browse Source

PinnedBuffer<T>, better tests

pull/125/head
Anton Firszov 9 years ago
parent
commit
78f997cd06
  1. 7
      src/ImageSharp/Common/Memory/ArrayPointer.cs
  2. 109
      src/ImageSharp/Common/Memory/PinnedBuffer.cs
  3. 1
      src/ImageSharp/Image/PixelPool{TColor}.cs
  4. 114
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  5. 69
      tests/ImageSharp.Tests/Common/PinnedBufferTests.cs

7
src/ImageSharp/Common/Memory/ArrayPointer.cs

@ -7,6 +7,13 @@ namespace ImageSharp
/// </summary>
internal static class ArrayPointer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ArrayPointer<T> GetArrayPointer<T>(this PinnedBuffer<T> buffer)
where T : struct
{
return new ArrayPointer<T>(buffer.Array, (void*)buffer.Pointer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(ArrayPointer<T> source, ArrayPointer<T> destination, int count)
where T : struct

109
src/ImageSharp/Common/Memory/PinnedBuffer.cs

@ -0,0 +1,109 @@
namespace ImageSharp
{
using System;
using System.Buffers;
using System.Runtime.InteropServices;
/// <summary>
/// Manages a pinned buffer of 'T' as a Disposable resource.
/// The backing array is either pooled or comes from the outside.
/// TODO: Should replace the pinning/dispose logic in several classes like <see cref="PixelAccessor{TColor}"/> or <see cref="PixelArea{TColor}"/>!
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal class PinnedBuffer<T> : IDisposable
where T : struct
{
private GCHandle handle;
private bool isBufferRented;
private bool isDisposed;
/// <summary>
/// TODO: Consider reusing functionality of <see cref="PixelPool{TColor}"/>
/// </summary>
private static readonly ArrayPool<T> ArrayPool = ArrayPool<T>.Create();
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int count)
{
this.Count = count;
this.Array = ArrayPool.Rent(count);
this.isBufferRented = true;
this.Pin();
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="array">The array to pin.</param>
public PinnedBuffer(T[] array)
{
this.Count = array.Length;
this.Array = array;
this.Pin();
}
/// <summary>
/// The count of "relevant" elements. Usually be smaller than 'Array.Length' when <see cref="Array"/> is pooled.
/// </summary>
public int Count { get; private set; }
/// <summary>
/// The (pinned) array of elements.
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Pointer to the pinned <see cref="Array"/>.
/// </summary>
public IntPtr Pointer { get; private set; }
/// <summary>
/// Disposes the <see cref="PinnedBuffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
/// </summary>
public void Dispose()
{
if (this.isDisposed)
{
return;
}
this.isDisposed = true;
this.UnPin();
if (this.isBufferRented)
{
ArrayPool.Return(this.Array, true);
}
this.Array = null;
this.Count = 0;
GC.SuppressFinalize(this);
}
private void Pin()
{
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned);
this.Pointer = this.handle.AddrOfPinnedObject();
}
private void UnPin()
{
if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated)
{
return;
}
this.handle.Free();
this.Pointer = IntPtr.Zero;
}
~PinnedBuffer()
{
this.UnPin();
}
}
}

1
src/ImageSharp/Image/PixelPool{TColor}.cs

@ -8,6 +8,7 @@ namespace ImageSharp
using System;
using System.Buffers;
// TODO: Consider refactoring this into a more general ClearPool<T>, so we can use it in PinnedBuffer<T>!
/// <summary>
/// Provides a resource pool that enables reusing instances of type <see cref="T:TColor[]"/>.
/// </summary>

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

@ -1,104 +1,132 @@
namespace ImageSharp.Tests.Colors
{
using System;
using System.Numerics;
using Xunit;
public class BulkPixelOperationsTests
public abstract class BulkPixelOperationsTests<TColor>
where TColor : struct, IPixel<TColor>
{
public class TypeParam<TColor>
public class ColorPixels : BulkPixelOperationsTests<Color>
{
}
public class ArgbPixels : BulkPixelOperationsTests<Argb>
{
}
public static TheoryData<int> ArraySizesData = new TheoryData<int> { 7, 16, 1111 };
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackFromVector4<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackFromVector4(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackToVector4<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackToVector4(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackToXyzBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackToXyzBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackFromXyzBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackFromXyzBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackToXyzwBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackToXyzwBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackFromXyzwBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackFromXyzwBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackToZyxBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackToZyxBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackFromZyxBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackFromZyxBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackToZyxwBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackToZyxwBytes(int count)
{
throw new NotImplementedException();
}
[Theory]
[InlineData(default(TypeParam<Color>))]
[InlineData(default(TypeParam<Argb>))]
public virtual void PackFromZyxwBytes<TColor>(TypeParam<TColor> dummy)
where TColor : struct, IPixel<TColor>
[MemberData(nameof(ArraySizesData))]
public virtual void PackFromZyxwBytes(int count)
{
throw new NotImplementedException();
}
public class TestBuffers
{
internal static PinnedBuffer<Vector4> Vector4(int length)
{
Vector4[] result = new Vector4[length];
Random rnd = new Random(42); // Deterministic random values
for (int i = 0; i < result.Length; i++)
{
result[i] = GetVector(rnd);
}
return new PinnedBuffer<Vector4>(result);
}
internal static PinnedBuffer<TColor> Pixel(int length)
{
TColor[] result = new TColor[length];
Random rnd = new Random(42); // Deterministic random values
for (int i = 0; i < result.Length; i++)
{
Vector4 v = GetVector(rnd);
result[i].PackFromVector4(v);
}
return new PinnedBuffer<TColor>(result);
}
private static Vector4 GetVector(Random rnd)
{
return new Vector4(
(float)rnd.NextDouble(),
(float)rnd.NextDouble(),
(float)rnd.NextDouble(),
(float)rnd.NextDouble()
);
}
}
}
}

69
tests/ImageSharp.Tests/Common/PinnedBufferTests.cs

@ -0,0 +1,69 @@
namespace ImageSharp.Tests.Common
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Xunit;
public unsafe class PinnedBufferTests
{
public struct Foo
{
public int A;
public double B;
}
[Theory]
[InlineData(42)]
[InlineData(1111)]
public void ConstructWithOwnArray(int count)
{
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(count))
{
Assert.NotNull(buffer.Array);
Assert.Equal(count, buffer.Count);
Assert.True(buffer.Array.Length >= count);
VerifyPointer(buffer);
}
}
[Theory]
[InlineData(42)]
[InlineData(1111)]
public void ConstructWithExistingArray(int count)
{
Foo[] array = new Foo[count];
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(array))
{
Assert.Equal(array, buffer.Array);
Assert.Equal(count, buffer.Count);
VerifyPointer(buffer);
}
}
[Fact]
public void GetArrayPointer()
{
Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } };
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a))
{
var arrayPtr = buffer.GetArrayPointer();
Assert.Equal(a, arrayPtr.Array);
Assert.Equal(0, arrayPtr.Offset);
Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset);
}
}
private static void VerifyPointer(PinnedBuffer<Foo> buffer)
{
IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]);
Assert.Equal(ptr, buffer.Pointer);
}
}
}
Loading…
Cancel
Save