mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-09 22:55:42 +00:00
test: bring coverage to 100% for integer Unpack (#70)
This commit is contained in:
parent
5f21a2102d
commit
87b6dbdd56
@ -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<bool> 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<ISsse3SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
|
||||
Span<bool> 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<ArgumentException>(() =>
|
||||
{
|
||||
|
@ -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<bool> 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<ISsse3SupportProvider>();
|
||||
mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
|
||||
Span<bool> 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<ArgumentException>(() =>
|
||||
{
|
||||
|
@ -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<bool> 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<ISsse3SupportProvider>();
|
||||
var avx2Mock = new Mock<IAvx2SupportProvider>();
|
||||
avx2Mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
ssse3Mock.Setup(provider => provider.IsSupported).Returns(true);
|
||||
|
||||
Span<bool> 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<ISsse3SupportProvider>();
|
||||
var avx2Mock = new Mock<IAvx2SupportProvider>();
|
||||
ssse3Mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
avx2Mock.Setup(provider => provider.IsSupported).Returns(false);
|
||||
|
||||
Span<bool> 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<ArgumentException>(() =>
|
||||
{
|
||||
|
@ -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
|
||||
/// <param name="value">The value to unpack.</param>
|
||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static void Unpack(this byte value, Span<bool> 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<bool> destination, ISsse3SupportProvider? ssse3SupportProvider)
|
||||
#else
|
||||
internal static void UnpackInternal(this byte value, Span<bool> 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<bool> destination)
|
||||
private static void UnpackInternal_Fallback(byte value, Span<bool> 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<byte> vec = Vector128.Create(value).AsByte();
|
||||
Vector128<byte> shuffle = Ssse3.Shuffle(vec, mask1);
|
||||
Vector128<byte> and = Sse2.AndNot(shuffle, mask2);
|
||||
Vector128<byte> cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero);
|
||||
Vector128<byte> correctness = Sse2.And(cmp, Vector128.Create((byte)0x01));
|
||||
|
||||
Sse2.StoreScalar((long*)pDestination, correctness.AsInt64());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void FallbackImplementation(byte value, Span<bool> 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<bool> 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<byte> vec = Vector128.Create(value).AsByte();
|
||||
Vector128<byte> shuffle = Ssse3.Shuffle(vec, mask1);
|
||||
Vector128<byte> and = Sse2.AndNot(shuffle, mask2);
|
||||
Vector128<byte> cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero);
|
||||
Vector128<byte> correctness = Sse2.And(cmp, Vector128.Create((byte)0x01));
|
||||
|
||||
Sse2.StoreScalar((long*)pDestination, correctness.AsInt64());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -34,51 +34,76 @@ public static class Int16Extensions
|
||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||
public static void Unpack(this short value, Span<bool> 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<bool> destination, ISsse3SupportProvider? ssse3SupportProvider)
|
||||
#else
|
||||
internal static void UnpackInternal(this short value, Span<bool> 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<bool> destination)
|
||||
private static void UnpackInternal_Fallback(short value, Span<bool> 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<byte>.Zero);
|
||||
var correctness = Sse2.And(cmp, one);
|
||||
|
||||
Sse2.Store((byte*)pDestination, correctness);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void FallbackImplementation(short value, Span<bool> 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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
get => Sse3.IsSupported;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void UnpackInternal_Ssse3(short value, Span<bool> 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<byte> vec = Vector128.Create(value).AsByte();
|
||||
Vector128<byte> shuffle = Ssse3.Shuffle(vec, mask1Lo);
|
||||
Vector128<byte> and = Sse2.AndNot(shuffle, mask2);
|
||||
Vector128<byte> cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero);
|
||||
Vector128<byte> correctness = Sse2.And(cmp, one);
|
||||
|
||||
Sse2.Store((byte*)pDestination, correctness);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -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
|
||||
/// <param name="value">The value to unpack.</param>
|
||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="destination" /> is not large enough to contain the result.</exception>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static void Unpack(this int value, Span<bool> 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<bool> 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<bool> destination)
|
||||
private static void UnpackInternal_Fallback(int value, Span<bool> 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<byte>.Zero);
|
||||
var correctness = Avx2.And(cmp, Vector256.Create((byte)0x01));
|
||||
|
||||
Avx.Store((byte*)pDestination, correctness);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe static void Ssse3Implementation(int value, Span<bool> 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<byte>.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<byte>.Zero);
|
||||
correctness = Sse2.And(cmp, one);
|
||||
|
||||
Sse2.Store((byte*)pDestination + 16, correctness);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void FallbackImplementation(int value, Span<bool> 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<bool> 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<byte> vec = Vector128.Create(value).AsByte();
|
||||
Vector128<byte> shuffle = Ssse3.Shuffle(vec, mask1Lo);
|
||||
Vector128<byte> and = Sse2.AndNot(shuffle, mask2);
|
||||
Vector128<byte> cmp = Sse2.CompareEqual(and, Vector128<byte>.Zero);
|
||||
Vector128<byte> 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<byte>.Zero);
|
||||
correctness = Sse2.And(cmp, one);
|
||||
|
||||
Sse2.Store((byte*)pDestination + 16, correctness);
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void UnpackInternal_Avx2(int value, Span<bool> 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<byte> vec = Vector256.Create(value).AsByte();
|
||||
Vector256<byte> shuffle = Avx2.Shuffle(vec, mask1);
|
||||
Vector256<byte> and = Avx2.AndNot(shuffle, mask2);
|
||||
Vector256<byte> cmp = Avx2.CompareEqual(and, Vector256<byte>.Zero);
|
||||
Vector256<byte> correctness = Avx2.And(cmp, Vector256.Create((byte)0x01));
|
||||
|
||||
Avx.Store((byte*)pDestination, correctness);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -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++)
|
||||
|
10
X10D/src/ExceptionMessages.Designer.cs
generated
10
X10D/src/ExceptionMessages.Designer.cs
generated
@ -1,7 +1,6 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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 {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The destination span is too short to contain the data..
|
||||
/// </summary>
|
||||
internal static string DestinationSpanLengthTooShort {
|
||||
get {
|
||||
return ResourceManager.GetString("DestinationSpanLengthTooShort", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The end index must be less than the list count..
|
||||
/// </summary>
|
||||
|
@ -123,6 +123,9 @@
|
||||
<data name="CountMustBeInRange" xml:space="preserve">
|
||||
<value>Count must be positive and count must refer to a location within the string/array/collection.</value>
|
||||
</data>
|
||||
<data name="DestinationSpanLengthTooShort" xml:space="preserve">
|
||||
<value>The destination span is too short to contain the data.</value>
|
||||
</data>
|
||||
<data name="EndIndexLessThanStartIndex" xml:space="preserve">
|
||||
<value>The end index must be greater than or equal to the start index.</value>
|
||||
</data>
|
||||
|
13
X10D/src/IAvx2SupportProvider.cs
Normal file
13
X10D/src/IAvx2SupportProvider.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object which provides the status of the support of AVX2 instructions.
|
||||
/// </summary>
|
||||
public interface IAvx2SupportProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether AVX2 instructions are supported on this platform.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if AVX2 instructions are supported; otherwise, <see langword="false" />.</value>
|
||||
bool IsSupported { get; }
|
||||
}
|
13
X10D/src/ISsse3SupportProvider.cs
Normal file
13
X10D/src/ISsse3SupportProvider.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object which provides the status of the support of SSSE3 instructions.
|
||||
/// </summary>
|
||||
public interface ISsse3SupportProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether SSSE3 instructions are supported on this platform.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if SSSE3 instructions are supported; otherwise, <see langword="false" />.</value>
|
||||
bool IsSupported { get; }
|
||||
}
|
18
X10D/src/SystemAvx2SupportProvider.cs
Normal file
18
X10D/src/SystemAvx2SupportProvider.cs
Normal file
@ -0,0 +1,18 @@
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace X10D;
|
||||
|
||||
internal struct SystemAvx2SupportProvider : IAvx2SupportProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
get => Avx2.IsSupported;
|
||||
#else
|
||||
get => false;
|
||||
#endif
|
||||
}
|
||||
}
|
18
X10D/src/SystemSsse3SupportProvider.cs
Normal file
18
X10D/src/SystemSsse3SupportProvider.cs
Normal file
@ -0,0 +1,18 @@
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace X10D;
|
||||
|
||||
internal struct SystemSsse3SupportProvider : ISsse3SupportProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsSupported
|
||||
{
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
get => Sse3.IsSupported;
|
||||
#else
|
||||
get => false;
|
||||
#endif
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user