From a52f6bf1b5065db0d88680c8e6c8a5ace1e6a037 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 2 Mar 2017 01:50:24 +0100 Subject: [PATCH] BulkPixelOperations skeleton --- src/ImageSharp/Colors/Color.cs | 3 + src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 3 + src/ImageSharp/Colors/PackedPixel/Argb.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 3 + .../Colors/PackedPixel/BulkPixelOperations.cs | 56 ++++++++++ src/ImageSharp/Colors/PackedPixel/Byte4.cs | 3 + .../Colors/PackedPixel/HalfSingle.cs | 3 + .../Colors/PackedPixel/HalfVector2.cs | 3 + .../Colors/PackedPixel/HalfVector4.cs | 3 + src/ImageSharp/Colors/PackedPixel/IPixel.cs | 4 + .../Colors/PackedPixel/NormalizedByte2.cs | 3 + .../Colors/PackedPixel/NormalizedByte4.cs | 3 + .../Colors/PackedPixel/NormalizedShort2.cs | 3 + .../Colors/PackedPixel/NormalizedShort4.cs | 3 + src/ImageSharp/Colors/PackedPixel/Rg32.cs | 3 + .../Colors/PackedPixel/Rgba1010102.cs | 3 + src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 3 + src/ImageSharp/Colors/PackedPixel/Short2.cs | 3 + src/ImageSharp/Colors/PackedPixel/Short4.cs | 3 + src/ImageSharp/Common/Memory/ArrayPointer.cs | 50 +++++++++ .../General/ArrayCopy.cs | 25 ++++- .../Colors/BulkPixelOperationsTests.cs | 104 ++++++++++++++++++ .../Common/ArrayPointerTests.cs | 94 +++++++++++++++- 25 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs create mode 100644 src/ImageSharp/Common/Memory/ArrayPointer.cs create mode 100644 tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 469774b34..8a869935c 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -112,6 +112,9 @@ namespace ImageSharp this.packedValue = packed; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Gets or sets the red component. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 485725d71..1181eb9e4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -26,6 +26,9 @@ namespace ImageSharp /// public byte PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index bef986fb9..1b97d2337 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -109,6 +109,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Gets or sets the red component. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index ebe8d2533..41b2bdc2e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -39,6 +39,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index ccd6ab1f3..99659a36b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -38,6 +38,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index a7a2e899a..86864fd48 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -40,6 +40,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs new file mode 100644 index 000000000..c914b3921 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -0,0 +1,56 @@ +namespace ImageSharp +{ + using System.Numerics; + + public unsafe class BulkPixelOperations + where TColor : struct, IPixel + { + public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + internal virtual void PackFromVector4( + ArrayPointer sourceVectors, + ArrayPointer destColors, + int count) + { + } + + internal virtual void PackToVector4( + ArrayPointer sourceColors, + ArrayPointer destVectors, + int count) + { + } + + internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromXyzBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 9d5eb9be8..5712027d9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -41,6 +41,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index acfa639b7..0bc82c3a6 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -36,6 +36,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index e02c226dd..778f86e0f 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -45,6 +45,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index 7c7f640e4..c24553d3f 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -49,6 +49,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs index 1c3e20a7e..c17fe86cc 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -15,6 +15,10 @@ namespace ImageSharp public interface IPixel : IPixel, IEquatable where TSelf : struct, IPixel { + /// + /// Gets the instance for this pixel type. + /// + BulkPixelOperations BulkOperations { get; } } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index 116a68172..d425806c2 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 7aaa30c52..cba3f0e86 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -52,6 +52,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 2f4ef89d6..4bc7f9427 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 60c5c9805..c848b7236 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -53,6 +53,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 9e5e5a711..9eb2247c9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -36,6 +36,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 95a8d3b97..4f99feb6e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -39,6 +39,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index 679a55c4e..a23e57ec3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -38,6 +38,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index 1c1cb28c3..f26e82578 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 2c11a1f8b..6dc7545e1 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -53,6 +53,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs new file mode 100644 index 000000000..c864d31fd --- /dev/null +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -0,0 +1,50 @@ +namespace ImageSharp +{ + using System.Runtime.CompilerServices; + + /// + /// Utility methods to + /// + internal static class ArrayPointer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest)); + } + + /// + /// Gets the size of `count` elements in bytes. + /// + /// The count of the elements + /// The size in bytes as int + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf(int count) + where T : struct => Unsafe.SizeOf() * count; + + /// + /// Gets the size of `count` elements in bytes as UInt32 + /// + /// The count of the elements + /// The size in bytes as UInt32 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint USizeOf(int count) + where T : struct + => (uint)SizeOf(count); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index dddd83e42..72fd6dc24 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -2,22 +2,23 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; + [Config(typeof(Config.Short))] public class ArrayCopy { - [Params(100, 1000, 10000)] + [Params(10, 100, 1000, 10000)] public int Count { get; set; } - private byte[] source; + byte[] source; - private byte[] destination; + byte[] destination; [Setup] public void SetUp() @@ -42,6 +43,12 @@ namespace ImageSharp.Benchmarks.General } } + [Benchmark(Description = "Copy using Buffer.BlockCopy()")] + public void CopyUsingBufferBlockCopy() + { + Buffer.BlockCopy(this.source, 0, this.destination, 0, this.Count); + } + [Benchmark(Description = "Copy using Buffer.MemoryCopy")] public unsafe void CopyUsingBufferMemoryCopy() { @@ -51,5 +58,15 @@ namespace ImageSharp.Benchmarks.General Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); } } + + + [Benchmark(Description = "Copy using Marshal.Copy")] + public unsafe void CopyUsingMarshalCopy() + { + fixed (byte* pinnedDestination = this.destination) + { + Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); + } + } } } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs new file mode 100644 index 000000000..413bd9451 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -0,0 +1,104 @@ +namespace ImageSharp.Tests.Colors +{ + using System; + + using Xunit; + + public class BulkPixelOperationsTests + { + public class TypeParam + { + } + + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromVector4(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToVector4(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToXyzBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromXyzBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToXyzwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromXyzwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToZyxBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromZyxBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToZyxwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromZyxwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs index 076e2512c..916a10947 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs @@ -3,6 +3,7 @@ namespace ImageSharp.Tests.Common { using System; + using System.Runtime.CompilerServices; using Xunit; @@ -10,18 +11,16 @@ namespace ImageSharp.Tests.Common { public struct Foo { -#pragma warning disable CS0414 - private int a; + public int A; - private double b; -#pragma warning restore CS0414 + public 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 }; + result[i] = new Foo() { A = i, B = i }; } return result; } @@ -79,5 +78,90 @@ namespace ImageSharp.Tests.Common Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset); } } + + public class Copy + { + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void GenericToOwnType(int count) + { + Foo[] source = Foo.CreateArray(count + 2); + Foo[] dest = new Foo[count + 5]; + + fixed (Foo* pSource = source) + fixed (Foo* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.Equal(source[0], dest[0]); + Assert.Equal(source[count-1], dest[count-1]); + Assert.NotEqual(source[count], dest[count]); + } + + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void GenericToBytes(int count) + { + int destCount = count * sizeof(Foo); + Foo[] source = Foo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(Foo) + 1]; + + fixed (Foo* pSource = source) + fixed (byte* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.True(ElementsAreEqual(source, dest, 0)); + Assert.True(ElementsAreEqual(source, dest, count - 1)); + Assert.False(ElementsAreEqual(source, dest, count)); + } + + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void BytesToGeneric(int count) + { + int destCount = count * sizeof(Foo); + byte[] source = new byte[destCount + sizeof(Foo) + 1]; + Foo[] dest = Foo.CreateArray(count + 2); + + fixed(byte* pSource = source) + fixed (Foo* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.True(ElementsAreEqual(dest, source, 0)); + Assert.True(ElementsAreEqual(dest, source, count - 1)); + Assert.False(ElementsAreEqual(dest, source, count)); + } + + private static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) + { + fixed (Foo* pArray = array) + fixed (byte* pRaw = rawArray) + { + Foo* pCasted = (Foo*)pRaw; + + Foo val1 = pArray[index]; + Foo val2 = pCasted[index]; + + return val1.Equals(val2); + } + } + } } } \ No newline at end of file