mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-14 04:15:41 +00:00
Initial commit with SIMD implementation and some other specialization methods
This commit is contained in:
parent
11c1ecb910
commit
1e98e6910f
@ -58,14 +58,8 @@ public static class ArrayExtensions
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int index = range.Start.Value;
|
(int offset, int length) = range.GetOffsetAndLength(array.Length);
|
||||||
int end = range.End.Value;
|
array.Clear(offset, length);
|
||||||
if (range.End.IsFromEnd)
|
|
||||||
{
|
|
||||||
end = array.Length - end;
|
|
||||||
}
|
|
||||||
|
|
||||||
array.Clear(index, end - index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace X10D.Collections;
|
namespace X10D.Collections;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,9 +40,43 @@ public static class ByteExtensions
|
|||||||
throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination));
|
throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var index = 0; index < Size; index++)
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
if (Ssse3.IsSupported)
|
||||||
{
|
{
|
||||||
destination[index] = (value & (1 << index)) != 0;
|
Ssse3Implementation(value, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FallbackImplementation(value, destination);
|
||||||
|
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
unsafe static void Ssse3Implementation(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 mask1Lo = Vector128.Create((byte)0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
|
||||||
|
|
||||||
|
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, 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
|
||||||
namespace X10D.Collections;
|
namespace X10D.Collections;
|
||||||
@ -8,6 +10,72 @@ namespace X10D.Collections;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class DictionaryExtensions
|
public static class DictionaryExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a key/value pair to the <see cref="Dictionary{TKey,TValue}" /> if the key does not already exist, or updates a
|
||||||
|
/// key/value pair in the <see cref="Dictionary{TKey,TValue}" /> by using the specified function if the key already
|
||||||
|
/// exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dictionary">The dictionary to update.</param>
|
||||||
|
/// <param name="key">The key to be added or whose value should be updated.</param>
|
||||||
|
/// <param name="addValue">The value to be added for an absent key.</param>
|
||||||
|
/// <param name="updateValueFactory">
|
||||||
|
/// The function used to generate a new value for an existing key based on the key's existing value.
|
||||||
|
/// </param>
|
||||||
|
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
|
||||||
|
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
|
||||||
|
/// <returns>
|
||||||
|
/// The new value for the key. This will be either be <paramref name="addValue" /> (if the key was absent) or the result
|
||||||
|
/// of <paramref name="updateValueFactory" /> (if the key was present).
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// <para><paramref name="dictionary" /> is <see langword="null" />.</para>
|
||||||
|
/// -or-
|
||||||
|
/// <para><paramref name="updateValueFactory" /> is <see langword="null" />.</para>
|
||||||
|
/// </exception>
|
||||||
|
public static TValue AddOrUpdate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue addValue,
|
||||||
|
Func<TKey, TValue, TValue> updateValueFactory)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(dictionary);
|
||||||
|
#else
|
||||||
|
if (dictionary is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(dictionary));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(updateValueFactory);
|
||||||
|
#else
|
||||||
|
if (updateValueFactory is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(updateValueFactory));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists);
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
value = updateValueFactory(key, value!);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
value = addValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
if (dictionary.TryGetValue(key, out var old))
|
||||||
|
{
|
||||||
|
dictionary[key] = updateValueFactory(key, old);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dictionary.Add(key, addValue);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a key/value pair to the <see cref="IDictionary{TKey,TValue}" /> if the key does not already exist, or updates a
|
/// Adds a key/value pair to the <see cref="IDictionary{TKey,TValue}" /> if the key does not already exist, or updates a
|
||||||
/// key/value pair in the <see cref="IDictionary{TKey,TValue}" /> by using the specified function if the key already
|
/// key/value pair in the <see cref="IDictionary{TKey,TValue}" /> by using the specified function if the key already
|
||||||
@ -51,9 +119,9 @@ public static class DictionaryExtensions
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (dictionary.ContainsKey(key))
|
if (dictionary.TryGetValue(key, out var old))
|
||||||
{
|
{
|
||||||
dictionary[key] = updateValueFactory(key, dictionary[key]);
|
dictionary[key] = updateValueFactory(key, old);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -63,6 +131,85 @@ public static class DictionaryExtensions
|
|||||||
return dictionary[key];
|
return dictionary[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses the specified functions to add a key/value pair to the <see cref="Dictionary{TKey,TValue}" /> if the key does
|
||||||
|
/// not already exist, or to update a key/value pair in the <see cref="Dictionary{TKey,TValue}" /> if the key already
|
||||||
|
/// exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dictionary">The dictionary to update.</param>
|
||||||
|
/// <param name="key">The key to be added or whose value should be updated.</param>
|
||||||
|
/// <param name="addValueFactory">The function used to generate a value for an absent key.</param>
|
||||||
|
/// <param name="updateValueFactory">
|
||||||
|
/// The function used to generate a new value for an existing key based on the key's existing value.
|
||||||
|
/// </param>
|
||||||
|
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
|
||||||
|
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
|
||||||
|
/// <returns>
|
||||||
|
/// The new value for the key. This will be either be the result of <paramref name="addValueFactory "/> (if the key was
|
||||||
|
/// absent) or the result of <paramref name="updateValueFactory" /> (if the key was present).
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// <para><paramref name="dictionary" /> is <see langword="null" />.</para>
|
||||||
|
/// -or-
|
||||||
|
/// <para><paramref name="addValueFactory" /> is <see langword="null" />.</para>
|
||||||
|
/// -or-
|
||||||
|
/// <para><paramref name="updateValueFactory" /> is <see langword="null" />.</para>
|
||||||
|
/// </exception>
|
||||||
|
public static TValue AddOrUpdate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
|
||||||
|
Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(dictionary);
|
||||||
|
#else
|
||||||
|
if (dictionary is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(dictionary));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(addValueFactory);
|
||||||
|
#else
|
||||||
|
if (addValueFactory is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(addValueFactory));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(updateValueFactory);
|
||||||
|
#else
|
||||||
|
if (updateValueFactory is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(updateValueFactory));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists);
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
value = updateValueFactory(key, value!);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = addValueFactory(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
if (dictionary.TryGetValue(key, out var old))
|
||||||
|
{
|
||||||
|
dictionary[key] = updateValueFactory(key, old);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dictionary.Add(key, addValueFactory(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary[key];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses the specified functions to add a key/value pair to the <see cref="IDictionary{TKey,TValue}" /> if the key does
|
/// Uses the specified functions to add a key/value pair to the <see cref="IDictionary{TKey,TValue}" /> if the key does
|
||||||
/// not already exist, or to update a key/value pair in the <see cref="IDictionary{TKey,TValue}" /> if the key already
|
/// not already exist, or to update a key/value pair in the <see cref="IDictionary{TKey,TValue}" /> if the key already
|
||||||
@ -116,9 +263,9 @@ public static class DictionaryExtensions
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (dictionary.ContainsKey(key))
|
if (dictionary.TryGetValue(key, out var old))
|
||||||
{
|
{
|
||||||
dictionary[key] = updateValueFactory(key, dictionary[key]);
|
dictionary[key] = updateValueFactory(key, old);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -128,6 +275,91 @@ public static class DictionaryExtensions
|
|||||||
return dictionary[key];
|
return dictionary[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses the specified functions and argument to add a key/value pair to the <see cref="Dictionary{TKey,TValue}" /> if
|
||||||
|
/// the key does not already exist, or to update a key/value pair in the <see cref="Dictionary{TKey,TValue}" /> if th
|
||||||
|
/// key already exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dictionary">The dictionary to update.</param>
|
||||||
|
/// <param name="key">The key to be added or whose value should be updated.</param>
|
||||||
|
/// <param name="addValueFactory">The function used to generate a value for an absent key.</param>
|
||||||
|
/// <param name="updateValueFactory">
|
||||||
|
/// The function used to generate a new value for an existing key based on the key's existing value.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="factoryArgument">
|
||||||
|
/// An argument to pass into <paramref name="addValueFactory" /> and <paramref name="updateValueFactory" />.
|
||||||
|
/// </param>
|
||||||
|
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
|
||||||
|
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
|
||||||
|
/// <typeparam name="TArg">
|
||||||
|
/// The type of an argument to pass into <paramref name="addValueFactory" /> and <paramref name="updateValueFactory" />.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <returns>
|
||||||
|
/// The new value for the key. This will be either be the result of <paramref name="addValueFactory "/> (if the key was
|
||||||
|
/// absent) or the result of <paramref name="updateValueFactory" /> (if the key was present).
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// <para><paramref name="dictionary" /> is <see langword="null" />.</para>
|
||||||
|
/// -or-
|
||||||
|
/// <para><paramref name="addValueFactory" /> is <see langword="null" />.</para>
|
||||||
|
/// -or-
|
||||||
|
/// <para><paramref name="updateValueFactory" /> is <see langword="null" />.</para>
|
||||||
|
/// </exception>
|
||||||
|
public static TValue AddOrUpdate<TKey, TValue, TArg>(this Dictionary<TKey, TValue> dictionary, TKey key,
|
||||||
|
Func<TKey, TArg, TValue> addValueFactory, Func<TKey, TValue, TArg, TValue> updateValueFactory, TArg factoryArgument)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(dictionary);
|
||||||
|
#else
|
||||||
|
if (dictionary is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(dictionary));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(addValueFactory);
|
||||||
|
#else
|
||||||
|
if (addValueFactory is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(addValueFactory));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ArgumentNullException.ThrowIfNull(updateValueFactory);
|
||||||
|
#else
|
||||||
|
if (updateValueFactory is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(updateValueFactory));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists);
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
value = updateValueFactory(key, value!, factoryArgument);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = addValueFactory(key, factoryArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
#else
|
||||||
|
if (dictionary.TryGetValue(key, out var old))
|
||||||
|
{
|
||||||
|
dictionary[key] = updateValueFactory(key, old, factoryArgument);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dictionary.Add(key, addValueFactory(key, factoryArgument));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary[key];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses the specified functions and argument to add a key/value pair to the <see cref="IDictionary{TKey,TValue}" /> if
|
/// Uses the specified functions and argument to add a key/value pair to the <see cref="IDictionary{TKey,TValue}" /> if
|
||||||
/// the key does not already exist, or to update a key/value pair in the <see cref="IDictionary{TKey,TValue}" /> if th
|
/// the key does not already exist, or to update a key/value pair in the <see cref="IDictionary{TKey,TValue}" /> if th
|
||||||
@ -187,9 +419,9 @@ public static class DictionaryExtensions
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (dictionary.ContainsKey(key))
|
if (dictionary.TryGetValue(key, out var old))
|
||||||
{
|
{
|
||||||
dictionary[key] = updateValueFactory(key, dictionary[key], factoryArgument);
|
dictionary[key] = updateValueFactory(key, old, factoryArgument);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -227,7 +459,7 @@ public static class DictionaryExtensions
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Contains(' ') ? $"\"{value}\"" : value;
|
return value.Contains(' ', StringComparison.InvariantCulture) ? $"\"{value}\"" : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string GetQueryParameter(KeyValuePair<TKey, TValue> pair)
|
static string GetQueryParameter(KeyValuePair<TKey, TValue> pair)
|
||||||
@ -282,7 +514,7 @@ public static class DictionaryExtensions
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Contains(' ') ? $"\"{value}\"" : value;
|
return value.Contains(' ', StringComparison.InvariantCulture) ? $"\"{value}\"" : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
string GetQueryParameter(KeyValuePair<TKey, TValue> pair)
|
string GetQueryParameter(KeyValuePair<TKey, TValue> pair)
|
||||||
@ -351,7 +583,7 @@ public static class DictionaryExtensions
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Contains(' ') ? $"\"{value}\"" : value;
|
return value.Contains(' ', StringComparison.InvariantCulture) ? $"\"{value}\"" : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
string GetQueryParameter(KeyValuePair<TKey, TValue> pair)
|
string GetQueryParameter(KeyValuePair<TKey, TValue> pair)
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace X10D.Collections;
|
namespace X10D.Collections;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -17,9 +22,9 @@ public static class Int16Extensions
|
|||||||
[Pure]
|
[Pure]
|
||||||
public static bool[] Unpack(this short value)
|
public static bool[] Unpack(this short value)
|
||||||
{
|
{
|
||||||
Span<bool> buffer = stackalloc bool[Size];
|
bool[] ret = new bool[Size];
|
||||||
value.Unpack(buffer);
|
value.Unpack(ret);
|
||||||
return buffer.ToArray();
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,9 +40,45 @@ public static class Int16Extensions
|
|||||||
throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination));
|
throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var index = 0; index < Size; index++)
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
if (Ssse3.IsSupported)
|
||||||
{
|
{
|
||||||
destination[index] = (value & (1 << index)) != 0;
|
Ssse3Implementation(value, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FallbackImplementation(value, destination);
|
||||||
|
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
unsafe static void Ssse3Implementation(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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
|
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace X10D.Collections;
|
namespace X10D.Collections;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -17,9 +22,9 @@ public static class Int32Extensions
|
|||||||
[Pure]
|
[Pure]
|
||||||
public static bool[] Unpack(this int value)
|
public static bool[] Unpack(this int value)
|
||||||
{
|
{
|
||||||
Span<bool> buffer = stackalloc bool[Size];
|
bool[] ret = new bool[Size];
|
||||||
value.Unpack(buffer);
|
value.Unpack(ret);
|
||||||
return buffer.ToArray();
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,9 +40,86 @@ public static class Int32Extensions
|
|||||||
throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination));
|
throw new ArgumentException($"Destination must be at least {Size} in length.", nameof(destination));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var index = 0; index < Size; index++)
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
// TODO: AdvSimd support.
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/24225786/fastest-way-to-unpack-32-bits-to-a-32-byte-simd-vector
|
||||||
|
if (Avx2.IsSupported)
|
||||||
{
|
{
|
||||||
destination[index] = (value & (1 << index)) != 0;
|
Avx2Implementation(value, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Ssse3.IsSupported)
|
||||||
|
{
|
||||||
|
Ssse3Implementation(value, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FallbackImplementation(value, destination);
|
||||||
|
|
||||||
|
#if NETCOREAPP3_0_OR_GREATER
|
||||||
|
unsafe static void Avx2Implementation(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
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@ public static class Int64Extensions
|
|||||||
[Pure]
|
[Pure]
|
||||||
public static bool[] Unpack(this long value)
|
public static bool[] Unpack(this long value)
|
||||||
{
|
{
|
||||||
Span<bool> buffer = stackalloc bool[Size];
|
bool[] ret = new bool[Size];
|
||||||
value.Unpack(buffer);
|
value.Unpack(ret);
|
||||||
return buffer.ToArray();
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -9,7 +9,9 @@ namespace X10D.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RandomExtensions
|
public static class RandomExtensions
|
||||||
{
|
{
|
||||||
|
#if !NET6_0_OR_GREATER
|
||||||
private static readonly Random Shared = new();
|
private static readonly Random Shared = new();
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a random value that defined in a specified enum.
|
/// Returns a random value that defined in a specified enum.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using X10D.Collections;
|
||||||
|
|
||||||
namespace X10D.Drawing;
|
namespace X10D.Drawing;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ public static class MemberInfoExtensions
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return member.HasCustomAttribute(typeof(T));
|
return Attribute.IsDefined(member, typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -82,7 +82,7 @@ public static class MemberInfoExtensions
|
|||||||
attribute, typeof(Attribute)), nameof(attribute));
|
attribute, typeof(Attribute)), nameof(attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
return member.GetCustomAttribute(attribute) is not null;
|
return Attribute.IsDefined(member, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user