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.
|
// Copyright (c) James Jackson-South and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
// </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