Browse Source

ArrayPointer<T>

pull/109/head
Anton Firszov 9 years ago
parent
commit
44f5538c71
  1. 17
      src/ImageSharp/Common/Helpers/ThrowHelper.cs
  2. 57
      src/ImageSharp/Common/Memory/ArrayPointer{T}.cs
  3. 2
      src/ImageSharp/Common/Memory/Fast2DArray{T}.cs
  4. 97
      tests/ImageSharp.Tests/Common/ArrayPointerTests.cs

17
src/ImageSharp/Common/Helpers/ThrowHelper.cs

@ -0,0 +1,17 @@
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Helps removing exception throwing code from hot path by providing non-inlined exception thrower methods.
/// </summary>
internal static class ThrowHelper
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowArgumentNullException(string paramName)
{
throw new ArgumentNullException(nameof(paramName));
}
}
}

57
src/ImageSharp/Common/Memory/ArrayPointer{T}.cs

@ -0,0 +1,57 @@
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Provides access to elements in an array from a given position.
/// This struct shares many similarities with corefx System.Span&lt;T&gt; but there are differences in functionalities and semantics:
/// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays
/// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.)
/// - Currently the arrays provided to ArrayPointer need to be pinned. This behaviour could be changed using C#7 features.
/// </summary>
internal unsafe struct ArrayPointer<T>
where T : struct
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayPointer(T[] array, void* pointerToArray, int offset)
{
// TODO: Use Guard.NotNull() here after optimizing it with ThrowHelper!
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(nameof(array));
}
this.Array = array;
this.Offset = offset;
this.PointerAtOffset = (IntPtr)pointerToArray + Unsafe.SizeOf<T>()*offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArrayPointer(T[] array, void* pointerToArray)
{
// TODO: Use Guard.NotNull() here after optimizing it with ThrowHelper!
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(nameof(array));
}
this.Array = array;
this.Offset = 0;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
public T[] Array { get; private set; }
public int Offset { get; private set; }
public IntPtr PointerAtOffset { get; private set; }
public ArrayPointer<T> Slice(int offset)
{
ArrayPointer<T> result = default(ArrayPointer<T>);
result.Array = this.Array;
result.Offset = this.Offset + offset;
result.PointerAtOffset = this.PointerAtOffset + Unsafe.SizeOf<T>() * offset;
return result;
}
}
}

2
src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs → src/ImageSharp/Common/Memory/Fast2DArray{T}.cs

@ -1,4 +1,4 @@
// <copyright file="Fast2DArray{T}.cs" company="James Jackson-South">
// <copyright file="Fast2DArray{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>

97
tests/ImageSharp.Tests/Common/ArrayPointerTests.cs

@ -0,0 +1,97 @@
// ReSharper disable ObjectCreationAsStatement
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Common
{
using System;
using Xunit;
public unsafe class ArrayPointerTests
{
public struct Foo
{
private int a;
private double b;
internal static Foo[] CreateArray(int size)
{
Foo[] result = new Foo[size];
for (int i = 0; i < size; i++)
{
result[i] = new Foo() { a = i, b = i };
}
return result;
}
}
[Fact]
public void ConstructWithNullArray_Throws()
{
Assert.Throws<ArgumentNullException>(
() =>
{
new ArrayPointer<int>(null, (void*)0);
});
Assert.Throws<ArgumentNullException>(
() =>
{
new ArrayPointer<int>(null, (void*)0);
});
}
[Fact]
public void ConstructWithoutOffset()
{
Foo[] array = Foo.CreateArray(3);
fixed (Foo* p = array)
{
// Act:
ArrayPointer<Foo> ap = new ArrayPointer<Foo>(array, p);
// Assert:
Assert.Equal(array, ap.Array);
Assert.Equal((IntPtr)p, ap.PointerAtOffset);
}
}
[Fact]
public void ConstructWithOffset()
{
Foo[] array = Foo.CreateArray(3);
int offset = 2;
fixed (Foo* p = array)
{
// Act:
ArrayPointer<Foo> ap = new ArrayPointer<Foo>(array, p, offset);
// Assert:
Assert.Equal(array, ap.Array);
Assert.Equal(offset, ap.Offset);
Assert.Equal((IntPtr)(p+offset), ap.PointerAtOffset);
}
}
[Fact]
public void Slice()
{
Foo[] array = Foo.CreateArray(5);
int offset0 = 2;
int offset1 = 2;
int totalOffset = offset0 + offset1;
fixed (Foo* p = array)
{
ArrayPointer<Foo> ap = new ArrayPointer<Foo>(array, p, offset0);
// Act:
ap = ap.Slice(offset1);
// Assert:
Assert.Equal(array, ap.Array);
Assert.Equal(totalOffset, ap.Offset);
Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset);
}
}
}
}
Loading…
Cancel
Save