diff --git a/src/ImageSharp/Common/Helpers/ThrowHelper.cs b/src/ImageSharp/Common/Helpers/ThrowHelper.cs
new file mode 100644
index 0000000000..a832645334
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/ThrowHelper.cs
@@ -0,0 +1,17 @@
+namespace ImageSharp
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Helps removing exception throwing code from hot path by providing non-inlined exception thrower methods.
+ ///
+ internal static class ThrowHelper
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowArgumentNullException(string paramName)
+ {
+ throw new ArgumentNullException(nameof(paramName));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs
new file mode 100644
index 0000000000..c3fc32234a
--- /dev/null
+++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs
@@ -0,0 +1,57 @@
+namespace ImageSharp
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// 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.
+ ///
+ internal unsafe struct ArrayPointer
+ 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()*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 Slice(int offset)
+ {
+ ArrayPointer result = default(ArrayPointer);
+ result.Array = this.Array;
+ result.Offset = this.Offset + offset;
+ result.PointerAtOffset = this.PointerAtOffset + Unsafe.SizeOf() * offset;
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs b/src/ImageSharp/Common/Memory/Fast2DArray{T}.cs
similarity index 98%
rename from src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs
rename to src/ImageSharp/Common/Memory/Fast2DArray{T}.cs
index 26ec816ce3..88a9797572 100644
--- a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs
+++ b/src/ImageSharp/Common/Memory/Fast2DArray{T}.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs
new file mode 100644
index 0000000000..1d229f86ab
--- /dev/null
+++ b/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(
+ () =>
+ {
+ new ArrayPointer(null, (void*)0);
+ });
+
+ Assert.Throws(
+ () =>
+ {
+ new ArrayPointer(null, (void*)0);
+ });
+ }
+
+ [Fact]
+ public void ConstructWithoutOffset()
+ {
+ Foo[] array = Foo.CreateArray(3);
+ fixed (Foo* p = array)
+ {
+ // Act:
+ ArrayPointer ap = new ArrayPointer(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 ap = new ArrayPointer(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 ap = new ArrayPointer(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);
+ }
+ }
+ }
+}
\ No newline at end of file