From 87b6dbdd56386de457be27b579a249ce76e8c94c Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sat, 1 Apr 2023 21:56:45 +0100 Subject: [PATCH] test: bring coverage to 100% for integer Unpack (#70) --- X10D.Tests/src/Collections/ByteTests.cs | 30 ++++- X10D.Tests/src/Collections/Int16Tests.cs | 35 ++++- X10D.Tests/src/Collections/Int32Tests.cs | 64 ++++++++- X10D/src/Collections/ByteExtensions.cs | 81 ++++++----- X10D/src/Collections/Int16Extensions.cs | 89 ++++++++----- X10D/src/Collections/Int32Extensions.cs | 163 +++++++++++++---------- X10D/src/Collections/Int64Extensions.cs | 2 +- X10D/src/ExceptionMessages.Designer.cs | 10 +- X10D/src/ExceptionMessages.resx | 3 + X10D/src/IAvx2SupportProvider.cs | 13 ++ X10D/src/ISsse3SupportProvider.cs | 13 ++ X10D/src/SystemAvx2SupportProvider.cs | 18 +++ X10D/src/SystemSsse3SupportProvider.cs | 18 +++ 13 files changed, 390 insertions(+), 149 deletions(-) create mode 100644 X10D/src/IAvx2SupportProvider.cs create mode 100644 X10D/src/ISsse3SupportProvider.cs create mode 100644 X10D/src/SystemAvx2SupportProvider.cs create mode 100644 X10D/src/SystemSsse3SupportProvider.cs diff --git a/X10D.Tests/src/Collections/ByteTests.cs b/X10D.Tests/src/Collections/ByteTests.cs index 15c28a8..e2b59fd 100644 --- a/X10D.Tests/src/Collections/ByteTests.cs +++ b/X10D.Tests/src/Collections/ByteTests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using X10D.Collections; namespace X10D.Tests.Collections; @@ -7,7 +8,7 @@ namespace X10D.Tests.Collections; public class ByteTests { [TestMethod] - public void UnpackBits_ShouldUnpackToArrayCorrectly() + public void Unpack_ShouldUnpackToArrayCorrectly() { bool[] bits = ((byte)0b11010100).Unpack(); @@ -24,7 +25,7 @@ public class ByteTests } [TestMethod] - public void UnpackBits_ShouldUnpackToSpanCorrectly() + public void Unpack_ShouldUnpackToSpanCorrectly() { Span bits = stackalloc bool[8]; ((byte)0b11010100).Unpack(bits); @@ -39,14 +40,35 @@ public class ByteTests Assert.IsTrue(bits[7]); } +#if NET5_0_OR_GREATER [TestMethod] - public void UnpackBits_ShouldRepackEqually() + public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation() + { + var mock = new Mock(); + mock.Setup(provider => provider.IsSupported).Returns(false); + + Span bits = stackalloc bool[8]; + ((byte)0b11010100).UnpackInternal(bits, mock.Object); + + Assert.IsFalse(bits[0]); + Assert.IsFalse(bits[1]); + Assert.IsTrue(bits[2]); + Assert.IsFalse(bits[3]); + Assert.IsTrue(bits[4]); + Assert.IsFalse(bits[5]); + Assert.IsTrue(bits[6]); + Assert.IsTrue(bits[7]); + } +#endif + + [TestMethod] + public void Unpack_ShouldRepackEqually() { Assert.AreEqual(0b11010100, ((byte)0b11010100).Unpack().PackByte()); } [TestMethod] - public void UnpackBits_ShouldThrow_GivenTooSmallSpan() + public void Unpack_ShouldThrow_GivenTooSmallSpan() { Assert.ThrowsException(() => { diff --git a/X10D.Tests/src/Collections/Int16Tests.cs b/X10D.Tests/src/Collections/Int16Tests.cs index c97f34d..17474f3 100644 --- a/X10D.Tests/src/Collections/Int16Tests.cs +++ b/X10D.Tests/src/Collections/Int16Tests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using X10D.Collections; namespace X10D.Tests.Collections; @@ -7,7 +8,7 @@ namespace X10D.Tests.Collections; public class Int16Tests { [TestMethod] - public void UnpackBits_ShouldUnpackToArrayCorrectly() + public void Unpack_ShouldUnpackToArrayCorrectly() { bool[] bits = ((short)0b11010100).Unpack(); @@ -29,7 +30,7 @@ public class Int16Tests } [TestMethod] - public void UnpackBits_ShouldUnpackToSpanCorrectly() + public void Unpack_ShouldUnpackToSpanCorrectly() { Span bits = stackalloc bool[16]; ((short)0b11010100).Unpack(bits); @@ -49,14 +50,40 @@ public class Int16Tests } } +#if NET5_0_OR_GREATER [TestMethod] - public void UnpackBits_ShouldRepackEqually() + public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackImplementation() + { + var mock = new Mock(); + mock.Setup(provider => provider.IsSupported).Returns(false); + + Span bits = stackalloc bool[16]; + ((short)0b11010100).UnpackInternal(bits, mock.Object); + + Assert.IsFalse(bits[0]); + Assert.IsFalse(bits[1]); + Assert.IsTrue(bits[2]); + Assert.IsFalse(bits[3]); + Assert.IsTrue(bits[4]); + Assert.IsFalse(bits[5]); + Assert.IsTrue(bits[6]); + Assert.IsTrue(bits[7]); + + for (var index = 8; index < 16; index++) + { + Assert.IsFalse(bits[index]); + } + } +#endif + + [TestMethod] + public void Unpack_ShouldRepackEqually() { Assert.AreEqual(0b11010100, ((short)0b11010100).Unpack().PackInt16()); } [TestMethod] - public void UnpackBits_ShouldThrow_GivenTooSmallSpan() + public void Unpack_ShouldThrow_GivenTooSmallSpan() { Assert.ThrowsException(() => { diff --git a/X10D.Tests/src/Collections/Int32Tests.cs b/X10D.Tests/src/Collections/Int32Tests.cs index e29b72f..f4c1fdf 100644 --- a/X10D.Tests/src/Collections/Int32Tests.cs +++ b/X10D.Tests/src/Collections/Int32Tests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using X10D.Collections; namespace X10D.Tests.Collections; @@ -7,7 +8,7 @@ namespace X10D.Tests.Collections; public class Int32Tests { [TestMethod] - public void UnpackBits_ShouldUnpackToArrayCorrectly() + public void Unpack_ShouldUnpackToArrayCorrectly() { bool[] bits = 0b11010100.Unpack(); @@ -29,7 +30,7 @@ public class Int32Tests } [TestMethod] - public void UnpackBits_ShouldUnpackToSpanCorrectly() + public void Unpack_ShouldUnpackToSpanCorrectly() { Span bits = stackalloc bool[32]; 0b11010100.Unpack(bits); @@ -49,14 +50,69 @@ public class Int32Tests } } +#if NET5_0_OR_GREATER [TestMethod] - public void UnpackBits_ShouldRepackEqually() + public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallbackFromAvx2() + { + var ssse3Mock = new Mock(); + var avx2Mock = new Mock(); + avx2Mock.Setup(provider => provider.IsSupported).Returns(false); + ssse3Mock.Setup(provider => provider.IsSupported).Returns(true); + + Span bits = stackalloc bool[32]; + 0b11010100.UnpackInternal(bits, ssse3Mock.Object, avx2Mock.Object); + + Assert.IsFalse(bits[0]); + Assert.IsFalse(bits[1]); + Assert.IsTrue(bits[2]); + Assert.IsFalse(bits[3]); + Assert.IsTrue(bits[4]); + Assert.IsFalse(bits[5]); + Assert.IsTrue(bits[6]); + Assert.IsTrue(bits[7]); + + for (var index = 8; index < 32; index++) + { + Assert.IsFalse(bits[index]); + } + } + + [TestMethod] + public void Unpack_ShouldUnpackToSpanCorrectly_GivenFallback() + { + var ssse3Mock = new Mock(); + var avx2Mock = new Mock(); + ssse3Mock.Setup(provider => provider.IsSupported).Returns(false); + avx2Mock.Setup(provider => provider.IsSupported).Returns(false); + + Span bits = stackalloc bool[32]; + 0b11010100.UnpackInternal(bits, ssse3Mock.Object, avx2Mock.Object); + + Assert.IsFalse(bits[0]); + Assert.IsFalse(bits[1]); + Assert.IsTrue(bits[2]); + Assert.IsFalse(bits[3]); + Assert.IsTrue(bits[4]); + Assert.IsFalse(bits[5]); + Assert.IsTrue(bits[6]); + Assert.IsTrue(bits[7]); + + for (var index = 8; index < 32; index++) + { + Assert.IsFalse(bits[index]); + } + } + +#endif + + [TestMethod] + public void Unpack_ShouldRepackEqually() { Assert.AreEqual(0b11010100, 0b11010100.Unpack().PackInt32()); } [TestMethod] - public void UnpackBits_ShouldThrow_GivenTooSmallSpan() + public void Unpack_ShouldThrow_GivenTooSmallSpan() { Assert.ThrowsException(() => { diff --git a/X10D/src/Collections/ByteExtensions.cs b/X10D/src/Collections/ByteExtensions.cs index 96bf23c..9094775 100644 --- a/X10D/src/Collections/ByteExtensions.cs +++ b/X10D/src/Collections/ByteExtensions.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.Contracts; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; #if NETCOREAPP3_0_OR_GREATER using System.Runtime.Intrinsics; @@ -33,50 +34,68 @@ public static class ByteExtensions /// The value to unpack. /// When this method returns, contains the unpacked booleans from . /// is not large enough to contain the result. + [ExcludeFromCodeCoverage] public static void Unpack(this byte value, Span destination) + { +#if NETCOREAPP3_0_OR_GREATER + UnpackInternal(value, destination, new SystemSsse3SupportProvider()); +#else + UnpackInternal(value, destination); +#endif + } + +#if NETCOREAPP3_0_OR_GREATER + internal static void UnpackInternal(this byte value, Span destination, ISsse3SupportProvider? ssse3SupportProvider) +#else + internal static void UnpackInternal(this byte value, Span destination) +#endif { if (destination.Length < Size) { - throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination)); + throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination)); } #if NETCOREAPP3_0_OR_GREATER - if (Ssse3.IsSupported) + ssse3SupportProvider ??= new SystemSsse3SupportProvider(); + + if (ssse3SupportProvider.IsSupported) { - Ssse3Implementation(value, destination); + UnpackInternal_Ssse3(value, destination); return; } #endif - FallbackImplementation(value, destination); + UnpackInternal_Fallback(value, destination); + } -#if NETCOREAPP3_0_OR_GREATER - unsafe static void Ssse3Implementation(byte value, Span destination) + private static void UnpackInternal_Fallback(byte value, Span destination) + { + for (var index = 0; index < Size; index++) { - fixed (bool* pDestination = destination) - { - var mask2 = Vector128.Create( - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 - ); - var mask1 = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); - - Vector128 vec = Vector128.Create(value).AsByte(); - Vector128 shuffle = Ssse3.Shuffle(vec, mask1); - Vector128 and = Sse2.AndNot(shuffle, mask2); - Vector128 cmp = Sse2.CompareEqual(and, Vector128.Zero); - Vector128 correctness = Sse2.And(cmp, Vector128.Create((byte)0x01)); - - Sse2.StoreScalar((long*)pDestination, correctness.AsInt64()); - } - } -#endif - static void FallbackImplementation(byte value, Span destination) - { - for (var index = 0; index < Size; index++) - { - destination[index] = (value & (1 << index)) != 0; - } + destination[index] = (value & (1 << index)) != 0; } } + +#if NETCOREAPP3_0_OR_GREATER + + private unsafe static void UnpackInternal_Ssse3(byte value, Span destination) + { + fixed (bool* pDestination = destination) + { + var mask2 = Vector128.Create( + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 + ); + var mask1 = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); + + Vector128 vec = Vector128.Create(value).AsByte(); + Vector128 shuffle = Ssse3.Shuffle(vec, mask1); + Vector128 and = Sse2.AndNot(shuffle, mask2); + Vector128 cmp = Sse2.CompareEqual(and, Vector128.Zero); + Vector128 correctness = Sse2.And(cmp, Vector128.Create((byte)0x01)); + + Sse2.StoreScalar((long*)pDestination, correctness.AsInt64()); + } + } +#endif } diff --git a/X10D/src/Collections/Int16Extensions.cs b/X10D/src/Collections/Int16Extensions.cs index 1bf6176..46d2ce1 100644 --- a/X10D/src/Collections/Int16Extensions.cs +++ b/X10D/src/Collections/Int16Extensions.cs @@ -34,51 +34,76 @@ public static class Int16Extensions /// When this method returns, contains the unpacked booleans from . /// is not large enough to contain the result. public static void Unpack(this short value, Span destination) + { +#if NETCOREAPP3_0_OR_GREATER + UnpackInternal(value, destination, new SystemSsse3SupportProvider()); +#else + UnpackInternal(value, destination); +#endif + } + +#if NETCOREAPP3_0_OR_GREATER + internal static void UnpackInternal(this short value, Span destination, ISsse3SupportProvider? ssse3SupportProvider) +#else + internal static void UnpackInternal(this short value, Span destination) +#endif { if (destination.Length < Size) { - throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination)); + throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination)); } #if NETCOREAPP3_0_OR_GREATER - if (Ssse3.IsSupported) + ssse3SupportProvider ??= new SystemSsse3SupportProvider(); + + if (ssse3SupportProvider.IsSupported) { - Ssse3Implementation(value, destination); + UnpackInternal_Ssse3(value, destination); return; } #endif - FallbackImplementation(value, destination); + UnpackInternal_Fallback(value, destination); + } -#if NETCOREAPP3_0_OR_GREATER - unsafe static void Ssse3Implementation(short value, Span destination) + private static void UnpackInternal_Fallback(short value, Span destination) + { + for (var index = 0; index < Size; index++) { - fixed (bool* pDestination = destination) - { - var mask2 = Vector128.Create( - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 - ); - var mask1Lo = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); - - var one = Vector128.Create((byte)0x01); - - var vec = Vector128.Create(value).AsByte(); - var shuffle = Ssse3.Shuffle(vec, mask1Lo); - var and = Sse2.AndNot(shuffle, mask2); - var cmp = Sse2.CompareEqual(and, Vector128.Zero); - var correctness = Sse2.And(cmp, one); - - Sse2.Store((byte*)pDestination, correctness); - } - } -#endif - static void FallbackImplementation(short value, Span destination) - { - for (var index = 0; index < Size; index++) - { - destination[index] = (value & (1 << index)) != 0; - } + destination[index] = (value & (1 << index)) != 0; } } + +#if NETCOREAPP3_0_OR_GREATER + private struct SystemSsse3SupportProvider : ISsse3SupportProvider + { + /// + public bool IsSupported + { + get => Sse3.IsSupported; + } + } + + private unsafe static void UnpackInternal_Ssse3(short value, Span destination) + { + fixed (bool* pDestination = destination) + { + var mask2 = Vector128.Create( + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 + ); + + var mask1Lo = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); + var one = Vector128.Create((byte)0x01); + + Vector128 vec = Vector128.Create(value).AsByte(); + Vector128 shuffle = Ssse3.Shuffle(vec, mask1Lo); + Vector128 and = Sse2.AndNot(shuffle, mask2); + Vector128 cmp = Sse2.CompareEqual(and, Vector128.Zero); + Vector128 correctness = Sse2.And(cmp, one); + + Sse2.Store((byte*)pDestination, correctness); + } + } +#endif } diff --git a/X10D/src/Collections/Int32Extensions.cs b/X10D/src/Collections/Int32Extensions.cs index 08cd006..38439e3 100644 --- a/X10D/src/Collections/Int32Extensions.cs +++ b/X10D/src/Collections/Int32Extensions.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.Contracts; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; #if NETCOREAPP3_0_OR_GREATER using System.Runtime.Intrinsics; @@ -33,96 +34,114 @@ public static class Int32Extensions /// The value to unpack. /// When this method returns, contains the unpacked booleans from . /// is not large enough to contain the result. + [ExcludeFromCodeCoverage] public static void Unpack(this int value, Span destination) + { +#if NETCOREAPP3_0_OR_GREATER + UnpackInternal(value, destination, new SystemSsse3SupportProvider(), new SystemAvx2SupportProvider()); +#else + UnpackInternal(value, destination); +#endif + } + + internal static void UnpackInternal(this int value, + Span destination +#if NETCOREAPP3_0_OR_GREATER + , + ISsse3SupportProvider? ssse3SupportProvider, + IAvx2SupportProvider? avx2SupportProvider +#endif + ) { if (destination.Length < Size) { - throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination)); + throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination)); } #if NETCOREAPP3_0_OR_GREATER - // TODO: AdvSimd support. + ssse3SupportProvider ??= new SystemSsse3SupportProvider(); + avx2SupportProvider ??= new SystemAvx2SupportProvider(); - // https://stackoverflow.com/questions/24225786/fastest-way-to-unpack-32-bits-to-a-32-byte-simd-vector - if (Avx2.IsSupported) + if (avx2SupportProvider.IsSupported) { - Avx2Implementation(value, destination); + UnpackInternal_Avx2(value, destination); return; } - if (Ssse3.IsSupported) + if (ssse3SupportProvider.IsSupported) { - Ssse3Implementation(value, destination); + UnpackInternal_Ssse3(value, destination); return; } #endif - FallbackImplementation(value, destination); + UnpackInternal_Fallback(value, destination); + } -#if NETCOREAPP3_0_OR_GREATER - unsafe static void Avx2Implementation(int value, Span destination) + private static void UnpackInternal_Fallback(int value, Span destination) + { + for (var index = 0; index < Size; index++) { - fixed (bool* pDestination = destination) - { - var mask1 = Vector256.Create( - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 - ).AsByte(); - var mask2 = Vector256.Create( - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 - ); - - var vec = Vector256.Create(value).AsByte(); - var shuffle = Avx2.Shuffle(vec, mask1); - var and = Avx2.AndNot(shuffle, mask2); - var cmp = Avx2.CompareEqual(and, Vector256.Zero); - var correctness = Avx2.And(cmp, Vector256.Create((byte)0x01)); - - Avx.Store((byte*)pDestination, correctness); - } - } - - unsafe static void Ssse3Implementation(int value, Span destination) - { - fixed (bool* pDestination = destination) - { - var mask2 = Vector128.Create( - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 - ); - var mask1Lo = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); - var mask1Hi = Vector128.Create((byte)2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3); - - var one = Vector128.Create((byte)0x01); - - var vec = Vector128.Create(value).AsByte(); - var shuffle = Ssse3.Shuffle(vec, mask1Lo); - var and = Sse2.AndNot(shuffle, mask2); - var cmp = Sse2.CompareEqual(and, Vector128.Zero); - var correctness = Sse2.And(cmp, one); - - Sse2.Store((byte*)pDestination, correctness); - - shuffle = Ssse3.Shuffle(vec, mask1Hi); - and = Sse2.AndNot(shuffle, mask2); - cmp = Sse2.CompareEqual(and, Vector128.Zero); - correctness = Sse2.And(cmp, one); - - Sse2.Store((byte*)pDestination + 16, correctness); - } - } -#endif - static void FallbackImplementation(int value, Span destination) - { - for (var index = 0; index < Size; index++) - { - destination[index] = (value & (1 << index)) != 0; - } + destination[index] = (value & (1 << index)) != 0; } } + +#if NETCOREAPP3_0_OR_GREATER + private static unsafe void UnpackInternal_Ssse3(int value, Span destination) + { + fixed (bool* pDestination = destination) + { + var mask2 = Vector128.Create( + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 + ); + var mask1Lo = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); + var mask1Hi = Vector128.Create((byte)2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3); + + var one = Vector128.Create((byte)0x01); + + Vector128 vec = Vector128.Create(value).AsByte(); + Vector128 shuffle = Ssse3.Shuffle(vec, mask1Lo); + Vector128 and = Sse2.AndNot(shuffle, mask2); + Vector128 cmp = Sse2.CompareEqual(and, Vector128.Zero); + Vector128 correctness = Sse2.And(cmp, one); + + Sse2.Store((byte*)pDestination, correctness); + + shuffle = Ssse3.Shuffle(vec, mask1Hi); + and = Sse2.AndNot(shuffle, mask2); + cmp = Sse2.CompareEqual(and, Vector128.Zero); + correctness = Sse2.And(cmp, one); + + Sse2.Store((byte*)pDestination + 16, correctness); + } + } + + private static unsafe void UnpackInternal_Avx2(int value, Span destination) + { + fixed (bool* pDestination = destination) + { + var mask1 = Vector256.Create( + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 + ).AsByte(); + var mask2 = Vector256.Create( + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 + ); + + Vector256 vec = Vector256.Create(value).AsByte(); + Vector256 shuffle = Avx2.Shuffle(vec, mask1); + Vector256 and = Avx2.AndNot(shuffle, mask2); + Vector256 cmp = Avx2.CompareEqual(and, Vector256.Zero); + Vector256 correctness = Avx2.And(cmp, Vector256.Create((byte)0x01)); + + Avx.Store((byte*)pDestination, correctness); + } + } +#endif } diff --git a/X10D/src/Collections/Int64Extensions.cs b/X10D/src/Collections/Int64Extensions.cs index 4903138..5b31a1d 100644 --- a/X10D/src/Collections/Int64Extensions.cs +++ b/X10D/src/Collections/Int64Extensions.cs @@ -32,7 +32,7 @@ public static class Int64Extensions { if (destination.Length < Size) { - throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination)); + throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination)); } for (var index = 0; index < Size; index++) diff --git a/X10D/src/ExceptionMessages.Designer.cs b/X10D/src/ExceptionMessages.Designer.cs index 033a87e..704181c 100644 --- a/X10D/src/ExceptionMessages.Designer.cs +++ b/X10D/src/ExceptionMessages.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -87,6 +86,15 @@ namespace X10D { } } + /// + /// Looks up a localized string similar to The destination span is too short to contain the data.. + /// + internal static string DestinationSpanLengthTooShort { + get { + return ResourceManager.GetString("DestinationSpanLengthTooShort", resourceCulture); + } + } + /// /// Looks up a localized string similar to The end index must be less than the list count.. /// diff --git a/X10D/src/ExceptionMessages.resx b/X10D/src/ExceptionMessages.resx index 0719397..480548d 100644 --- a/X10D/src/ExceptionMessages.resx +++ b/X10D/src/ExceptionMessages.resx @@ -123,6 +123,9 @@ Count must be positive and count must refer to a location within the string/array/collection. + + The destination span is too short to contain the data. + The end index must be greater than or equal to the start index. diff --git a/X10D/src/IAvx2SupportProvider.cs b/X10D/src/IAvx2SupportProvider.cs new file mode 100644 index 0000000..093ae29 --- /dev/null +++ b/X10D/src/IAvx2SupportProvider.cs @@ -0,0 +1,13 @@ +namespace X10D; + +/// +/// Represents an object which provides the status of the support of AVX2 instructions. +/// +public interface IAvx2SupportProvider +{ + /// + /// Gets a value indicating whether AVX2 instructions are supported on this platform. + /// + /// if AVX2 instructions are supported; otherwise, . + bool IsSupported { get; } +} diff --git a/X10D/src/ISsse3SupportProvider.cs b/X10D/src/ISsse3SupportProvider.cs new file mode 100644 index 0000000..1ed50d3 --- /dev/null +++ b/X10D/src/ISsse3SupportProvider.cs @@ -0,0 +1,13 @@ +namespace X10D; + +/// +/// Represents an object which provides the status of the support of SSSE3 instructions. +/// +public interface ISsse3SupportProvider +{ + /// + /// Gets a value indicating whether SSSE3 instructions are supported on this platform. + /// + /// if SSSE3 instructions are supported; otherwise, . + bool IsSupported { get; } +} diff --git a/X10D/src/SystemAvx2SupportProvider.cs b/X10D/src/SystemAvx2SupportProvider.cs new file mode 100644 index 0000000..976336e --- /dev/null +++ b/X10D/src/SystemAvx2SupportProvider.cs @@ -0,0 +1,18 @@ +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics.X86; +#endif + +namespace X10D; + +internal struct SystemAvx2SupportProvider : IAvx2SupportProvider +{ + /// + public bool IsSupported + { +#if NETCOREAPP3_0_OR_GREATER + get => Avx2.IsSupported; +#else + get => false; +#endif + } +} diff --git a/X10D/src/SystemSsse3SupportProvider.cs b/X10D/src/SystemSsse3SupportProvider.cs new file mode 100644 index 0000000..0369cf8 --- /dev/null +++ b/X10D/src/SystemSsse3SupportProvider.cs @@ -0,0 +1,18 @@ +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics.X86; +#endif + +namespace X10D; + +internal struct SystemSsse3SupportProvider : ISsse3SupportProvider +{ + /// + public bool IsSupported + { +#if NETCOREAPP3_0_OR_GREATER + get => Sse3.IsSupported; +#else + get => false; +#endif + } +}