mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 23:58:48 +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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
using X10D.Collections;
|
using X10D.Collections;
|
||||||
|
|
||||||
namespace X10D.Tests.Collections;
|
namespace X10D.Tests.Collections;
|
||||||
@ -7,7 +8,7 @@ namespace X10D.Tests.Collections;
|
|||||||
public class ByteTests
|
public class ByteTests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldUnpackToArrayCorrectly()
|
public void Unpack_ShouldUnpackToArrayCorrectly()
|
||||||
{
|
{
|
||||||
bool[] bits = ((byte)0b11010100).Unpack();
|
bool[] bits = ((byte)0b11010100).Unpack();
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ public class ByteTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldUnpackToSpanCorrectly()
|
public void Unpack_ShouldUnpackToSpanCorrectly()
|
||||||
{
|
{
|
||||||
Span<bool> bits = stackalloc bool[8];
|
Span<bool> bits = stackalloc bool[8];
|
||||||
((byte)0b11010100).Unpack(bits);
|
((byte)0b11010100).Unpack(bits);
|
||||||
@ -39,14 +40,35 @@ public class ByteTests
|
|||||||
Assert.IsTrue(bits[7]);
|
Assert.IsTrue(bits[7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
[TestMethod]
|
[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());
|
Assert.AreEqual(0b11010100, ((byte)0b11010100).Unpack().PackByte());
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldThrow_GivenTooSmallSpan()
|
public void Unpack_ShouldThrow_GivenTooSmallSpan()
|
||||||
{
|
{
|
||||||
Assert.ThrowsException<ArgumentException>(() =>
|
Assert.ThrowsException<ArgumentException>(() =>
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
using X10D.Collections;
|
using X10D.Collections;
|
||||||
|
|
||||||
namespace X10D.Tests.Collections;
|
namespace X10D.Tests.Collections;
|
||||||
@ -7,7 +8,7 @@ namespace X10D.Tests.Collections;
|
|||||||
public class Int16Tests
|
public class Int16Tests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldUnpackToArrayCorrectly()
|
public void Unpack_ShouldUnpackToArrayCorrectly()
|
||||||
{
|
{
|
||||||
bool[] bits = ((short)0b11010100).Unpack();
|
bool[] bits = ((short)0b11010100).Unpack();
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ public class Int16Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldUnpackToSpanCorrectly()
|
public void Unpack_ShouldUnpackToSpanCorrectly()
|
||||||
{
|
{
|
||||||
Span<bool> bits = stackalloc bool[16];
|
Span<bool> bits = stackalloc bool[16];
|
||||||
((short)0b11010100).Unpack(bits);
|
((short)0b11010100).Unpack(bits);
|
||||||
@ -49,14 +50,40 @@ public class Int16Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
[TestMethod]
|
[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());
|
Assert.AreEqual(0b11010100, ((short)0b11010100).Unpack().PackInt16());
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldThrow_GivenTooSmallSpan()
|
public void Unpack_ShouldThrow_GivenTooSmallSpan()
|
||||||
{
|
{
|
||||||
Assert.ThrowsException<ArgumentException>(() =>
|
Assert.ThrowsException<ArgumentException>(() =>
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
using X10D.Collections;
|
using X10D.Collections;
|
||||||
|
|
||||||
namespace X10D.Tests.Collections;
|
namespace X10D.Tests.Collections;
|
||||||
@ -7,7 +8,7 @@ namespace X10D.Tests.Collections;
|
|||||||
public class Int32Tests
|
public class Int32Tests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldUnpackToArrayCorrectly()
|
public void Unpack_ShouldUnpackToArrayCorrectly()
|
||||||
{
|
{
|
||||||
bool[] bits = 0b11010100.Unpack();
|
bool[] bits = 0b11010100.Unpack();
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ public class Int32Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldUnpackToSpanCorrectly()
|
public void Unpack_ShouldUnpackToSpanCorrectly()
|
||||||
{
|
{
|
||||||
Span<bool> bits = stackalloc bool[32];
|
Span<bool> bits = stackalloc bool[32];
|
||||||
0b11010100.Unpack(bits);
|
0b11010100.Unpack(bits);
|
||||||
@ -49,14 +50,69 @@ public class Int32Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
[TestMethod]
|
[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());
|
Assert.AreEqual(0b11010100, 0b11010100.Unpack().PackInt32());
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void UnpackBits_ShouldThrow_GivenTooSmallSpan()
|
public void Unpack_ShouldThrow_GivenTooSmallSpan()
|
||||||
{
|
{
|
||||||
Assert.ThrowsException<ArgumentException>(() =>
|
Assert.ThrowsException<ArgumentException>(() =>
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
#if NETCOREAPP3_0_OR_GREATER
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
@ -33,50 +34,68 @@ public static class ByteExtensions
|
|||||||
/// <param name="value">The value to unpack.</param>
|
/// <param name="value">The value to unpack.</param>
|
||||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</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>
|
/// <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)
|
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)
|
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 NETCOREAPP3_0_OR_GREATER
|
||||||
if (Ssse3.IsSupported)
|
ssse3SupportProvider ??= new SystemSsse3SupportProvider();
|
||||||
|
|
||||||
|
if (ssse3SupportProvider.IsSupported)
|
||||||
{
|
{
|
||||||
Ssse3Implementation(value, destination);
|
UnpackInternal_Ssse3(value, destination);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FallbackImplementation(value, destination);
|
UnpackInternal_Fallback(value, destination);
|
||||||
|
}
|
||||||
|
|
||||||
#if NETCOREAPP3_0_OR_GREATER
|
private static void UnpackInternal_Fallback(byte value, Span<bool> destination)
|
||||||
unsafe static void Ssse3Implementation(byte value, Span<bool> destination)
|
{
|
||||||
|
for (var index = 0; index < Size; index++)
|
||||||
{
|
{
|
||||||
fixed (bool* pDestination = destination)
|
destination[index] = (value & (1 << index)) != 0;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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>
|
/// <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>
|
/// <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)
|
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)
|
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 NETCOREAPP3_0_OR_GREATER
|
||||||
if (Ssse3.IsSupported)
|
ssse3SupportProvider ??= new SystemSsse3SupportProvider();
|
||||||
|
|
||||||
|
if (ssse3SupportProvider.IsSupported)
|
||||||
{
|
{
|
||||||
Ssse3Implementation(value, destination);
|
UnpackInternal_Ssse3(value, destination);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FallbackImplementation(value, destination);
|
UnpackInternal_Fallback(value, destination);
|
||||||
|
}
|
||||||
|
|
||||||
#if NETCOREAPP3_0_OR_GREATER
|
private static void UnpackInternal_Fallback(short value, Span<bool> destination)
|
||||||
unsafe static void Ssse3Implementation(short value, Span<bool> destination)
|
{
|
||||||
|
for (var index = 0; index < Size; index++)
|
||||||
{
|
{
|
||||||
fixed (bool* pDestination = destination)
|
destination[index] = (value & (1 << index)) != 0;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
@ -33,96 +34,114 @@ public static class Int32Extensions
|
|||||||
/// <param name="value">The value to unpack.</param>
|
/// <param name="value">The value to unpack.</param>
|
||||||
/// <param name="destination">When this method returns, contains the unpacked booleans from <paramref name="value" />.</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>
|
/// <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)
|
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)
|
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 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 (avx2SupportProvider.IsSupported)
|
||||||
if (Avx2.IsSupported)
|
|
||||||
{
|
{
|
||||||
Avx2Implementation(value, destination);
|
UnpackInternal_Avx2(value, destination);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Ssse3.IsSupported)
|
if (ssse3SupportProvider.IsSupported)
|
||||||
{
|
{
|
||||||
Ssse3Implementation(value, destination);
|
UnpackInternal_Ssse3(value, destination);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FallbackImplementation(value, destination);
|
UnpackInternal_Fallback(value, destination);
|
||||||
|
}
|
||||||
|
|
||||||
#if NETCOREAPP3_0_OR_GREATER
|
private static void UnpackInternal_Fallback(int value, Span<bool> destination)
|
||||||
unsafe static void Avx2Implementation(int value, Span<bool> destination)
|
{
|
||||||
|
for (var index = 0; index < Size; index++)
|
||||||
{
|
{
|
||||||
fixed (bool* pDestination = destination)
|
destination[index] = (value & (1 << index)) != 0;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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)
|
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++)
|
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>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// 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
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// 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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to The end index must be less than the list count..
|
/// Looks up a localized string similar to The end index must be less than the list count..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -123,6 +123,9 @@
|
|||||||
<data name="CountMustBeInRange" xml:space="preserve">
|
<data name="CountMustBeInRange" xml:space="preserve">
|
||||||
<value>Count must be positive and count must refer to a location within the string/array/collection.</value>
|
<value>Count must be positive and count must refer to a location within the string/array/collection.</value>
|
||||||
</data>
|
</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">
|
<data name="EndIndexLessThanStartIndex" xml:space="preserve">
|
||||||
<value>The end index must be greater than or equal to the start index.</value>
|
<value>The end index must be greater than or equal to the start index.</value>
|
||||||
</data>
|
</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