mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 172 additions and 1 deletions
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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<T> 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; |
|||
} |
|||
} |
|||
} |
|||
@ -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>
|
|||
@ -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…
Reference in new issue