diff --git a/src/ImageSharp/Common/Memory/BufferPointer.cs b/src/ImageSharp/Common/Memory/BufferPointer.cs index cc544341e..10a5c2db2 100644 --- a/src/ImageSharp/Common/Memory/BufferPointer.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer.cs @@ -5,6 +5,7 @@ namespace ImageSharp { + using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -29,19 +30,17 @@ namespace ImageSharp public static unsafe void Copy(BufferPointer source, BufferPointer destination, int count) where T : struct { - int elementSize = Unsafe.SizeOf(); - uint byteCount = (uint) (count * elementSize); + uint byteCount = USizeOf(count); - if (byteCount > ByteCountThreshold && elementSize == sizeof(int)) + if (byteCount > ByteCountThreshold) { - // TODO: Add the optimized path for non int-compatible types - int[] srcArray = Unsafe.As(source.Array); - Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); - } - else - { - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); + if (TryMarshalCopy(source, destination, count)) + { + return; + } } + + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); } /// @@ -55,19 +54,17 @@ namespace ImageSharp public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInSource) where T : struct { - int elementSize = Unsafe.SizeOf(); - uint byteCount = (uint)(countInSource * elementSize); + uint byteCount = USizeOf(countInSource); - if (byteCount > ByteCountThreshold && elementSize == sizeof(int)) - { - // TODO: Add the optimized path for non int-compatible types - int[] srcArray = Unsafe.As(source.Array); - Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, countInSource); - } - else + if (byteCount > ByteCountThreshold) { - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); + if (TryMarshalCopy(source, destination, countInSource)) + { + return; + } } + + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); } /// @@ -113,5 +110,63 @@ namespace ImageSharp public static uint USizeOf(int count) where T : struct => (uint)SizeOf(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryMarshalCopy(BufferPointer source, BufferPointer destination, int count) + where TSource : struct + where TDest : struct + { + // Pattern Based On: + // https://github.com/dotnet/corefx/blob/master/src/System.Numerics.Vectors/src/System/Numerics/Vector.cs#L12 + // + // Note: The following patterns are used throughout the code here and are described here + // + // PATTERN: + // if (typeof(T) == typeof(Int32)) { ... } + // else if (typeof(T) == typeof(Single)) { ... } + // EXPLANATION: + // At runtime, each instantiation of BufferPointer will be type-specific, and each of these typeof blocks will be eliminated, + // as typeof(T) is a (JIT) compile-time constant for each instantiation. This design was chosen to eliminate any overhead from + // delegates and other patterns. + + if (typeof(TSource) == typeof(long)) + { + long[] srcArray = Unsafe.As(source.Array); + Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); + return true; + } + else if (typeof(TSource) == typeof(int)) + { + int[] srcArray = Unsafe.As(source.Array); + Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); + return true; + } + else if (typeof(TSource) == typeof(uint)) + { + int[] srcArray = Unsafe.As(source.Array); + Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); + return true; + } + else if (typeof(TSource) == typeof(short)) + { + short[] srcArray = Unsafe.As(source.Array); + Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); + return true; + } + else if (typeof(TSource) == typeof(ushort)) + { + short[] srcArray = Unsafe.As(source.Array); + Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); + return true; + } + else if (typeof(TSource) == typeof(byte)) + { + byte[] srcArray = Unsafe.As(source.Array); + Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count); + return true; + } + + return false; + } } } \ No newline at end of file