BinaryTransformations
Provides various binary transformations.
using DotNext.Runtime;
using System;
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Runtime.Versioning;
namespace DotNext.Buffers.Binary
{
public static class BinaryTransformations
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
[RequiresPreviewFeatures]
private readonly struct BitwiseAndTransformation : IBinaryTransformation<UIntPtr>, IBinaryTransformation<Vector<byte>>
{
static Vector<byte> IBinaryTransformation<Vector<byte>>.Transform(Vector<byte> x, Vector<byte> y)
{
return Vector.BitwiseAnd(x, y);
}
[return: System.Runtime.CompilerServices.NativeInteger]
static unsafe UIntPtr IBinaryTransformation<UIntPtr>.Transform([System.Runtime.CompilerServices.NativeInteger] UIntPtr x, [System.Runtime.CompilerServices.NativeInteger] UIntPtr y)
{
return (UIntPtr)(void*)((long)(ulong)x & (long)(ulong)y);
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
[RequiresPreviewFeatures]
private readonly struct BitwiseOrTransformation : IBinaryTransformation<UIntPtr>, IBinaryTransformation<Vector<byte>>
{
static Vector<byte> IBinaryTransformation<Vector<byte>>.Transform(Vector<byte> x, Vector<byte> y)
{
return Vector.BitwiseOr(x, y);
}
[return: System.Runtime.CompilerServices.NativeInteger]
static unsafe UIntPtr IBinaryTransformation<UIntPtr>.Transform([System.Runtime.CompilerServices.NativeInteger] UIntPtr x, [System.Runtime.CompilerServices.NativeInteger] UIntPtr y)
{
return (UIntPtr)(void*)((long)(ulong)x | (long)(ulong)y);
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
[RequiresPreviewFeatures]
private readonly struct BitwiseXorTransformation : IBinaryTransformation<UIntPtr>, IBinaryTransformation<Vector<byte>>
{
static Vector<byte> IBinaryTransformation<Vector<byte>>.Transform(Vector<byte> x, Vector<byte> y)
{
return Vector.Xor(x, y);
}
[return: System.Runtime.CompilerServices.NativeInteger]
static unsafe UIntPtr IBinaryTransformation<UIntPtr>.Transform([System.Runtime.CompilerServices.NativeInteger] UIntPtr x, [System.Runtime.CompilerServices.NativeInteger] UIntPtr y)
{
return (UIntPtr)(void*)((long)(ulong)x ^ (long)(ulong)y);
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
[RequiresPreviewFeatures]
private readonly struct BitwiseAndNotTransformation : IBinaryTransformation<UIntPtr>, IBinaryTransformation<Vector<byte>>
{
static Vector<byte> IBinaryTransformation<Vector<byte>>.Transform(Vector<byte> x, Vector<byte> y)
{
return Vector.AndNot(x, y);
}
[return: System.Runtime.CompilerServices.NativeInteger]
static unsafe UIntPtr IBinaryTransformation<UIntPtr>.Transform([System.Runtime.CompilerServices.NativeInteger] UIntPtr x, [System.Runtime.CompilerServices.NativeInteger] UIntPtr y)
{
return (UIntPtr)(void*)((long)(ulong)x & (long)(IntPtr)(void*)(~(ulong)y));
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
[RequiresPreviewFeatures]
private readonly struct OnesComplementTransformation : IUnaryTransformation<UIntPtr>, IUnaryTransformation<Vector<byte>>
{
static Vector<byte> IUnaryTransformation<Vector<byte>>.Transform(Vector<byte> value)
{
return Vector.OnesComplement(value);
}
[return: System.Runtime.CompilerServices.NativeInteger]
static unsafe UIntPtr IUnaryTransformation<UIntPtr>.Transform([System.Runtime.CompilerServices.NativeInteger] UIntPtr value)
{
return (UIntPtr)(void*)(~(ulong)value);
}
}
[RequiresPreviewFeatures]
private interface IUnaryTransformation<[System.Runtime.CompilerServices.IsUnmanaged] T> where T : struct
{
T Transform(T value);
}
[RequiresPreviewFeatures]
private interface IBinaryTransformation<[System.Runtime.CompilerServices.IsUnmanaged] T> where T : struct
{
T Transform(T x, T y);
}
[RequiresPreviewFeatures]
private interface IEndiannessTransformation<[System.Runtime.CompilerServices.IsUnmanaged] T> : IUnaryTransformation<T> where T : struct
{
Vector128<byte> ReorderMask { get; }
}
[StructLayout(LayoutKind.Auto)]
[RequiresPreviewFeatures]
private readonly struct UInt16Transformation : IEndiannessTransformation<ushort>, IUnaryTransformation<ushort>
{
public Vector128<byte> ReorderMask { get; }
public UInt16Transformation()
{
ReorderMask = Vector128.Create((byte)1, (byte)0, (byte)3, (byte)2, (byte)5, (byte)4, (byte)7, (byte)6, (byte)9, (byte)8, (byte)11, (byte)10, (byte)13, (byte)12, (byte)15, (byte)14);
}
static ushort IUnaryTransformation<ushort>.Transform(ushort value)
{
return BinaryPrimitives.ReverseEndianness(value);
}
}
[StructLayout(LayoutKind.Auto)]
[RequiresPreviewFeatures]
private readonly struct UInt32Transformation : IEndiannessTransformation<uint>, IUnaryTransformation<uint>
{
public Vector128<byte> ReorderMask { get; }
public UInt32Transformation()
{
ReorderMask = Vector128.Create((byte)3, (byte)2, (byte)1, (byte)0, (byte)7, (byte)6, (byte)5, (byte)4, (byte)11, (byte)10, (byte)9, (byte)8, (byte)15, (byte)14, (byte)13, (byte)12);
}
static uint IUnaryTransformation<uint>.Transform(uint value)
{
return BinaryPrimitives.ReverseEndianness(value);
}
}
[StructLayout(LayoutKind.Auto)]
[RequiresPreviewFeatures]
private readonly struct UInt64Transformation : IEndiannessTransformation<ulong>, IUnaryTransformation<ulong>
{
public Vector128<byte> ReorderMask { get; }
public UInt64Transformation()
{
ReorderMask = Vector128.Create((byte)7, (byte)6, (byte)5, (byte)4, (byte)3, (byte)2, (byte)1, (byte)0, (byte)15, (byte)14, (byte)13, (byte)12, (byte)11, (byte)10, (byte)9, (byte)8);
}
static ulong IUnaryTransformation<ulong>.Transform(ulong value)
{
return BinaryPrimitives.ReverseEndianness(value);
}
}
public static void BitwiseAnd<[System.Runtime.CompilerServices.IsUnmanaged] T>(this ReadOnlySpan<T> x, Span<T> y) where T : struct
{
if (x.Length != y.Length)
throw new ArgumentOutOfRangeException("x");
Transform<T, BitwiseAndTransformation>(x, y);
}
public static void AndNot<[System.Runtime.CompilerServices.IsUnmanaged] T>(this ReadOnlySpan<T> x, Span<T> y) where T : struct
{
if (x.Length != y.Length)
throw new ArgumentOutOfRangeException("x");
Transform<T, BitwiseAndNotTransformation>(x, y);
}
public static void BitwiseOr<[System.Runtime.CompilerServices.IsUnmanaged] T>(this ReadOnlySpan<T> x, Span<T> y) where T : struct
{
if (x.Length != y.Length)
throw new ArgumentOutOfRangeException("x");
Transform<T, BitwiseOrTransformation>(x, y);
}
public static void BitwiseXor<[System.Runtime.CompilerServices.IsUnmanaged] T>(this ReadOnlySpan<T> x, Span<T> y) where T : struct
{
if (x.Length != y.Length)
throw new ArgumentOutOfRangeException("x");
Transform<T, BitwiseXorTransformation>(x, y);
}
public static void OnesComplement<[System.Runtime.CompilerServices.IsUnmanaged] T>(this Span<T> values) where T : struct
{
Transform<T, OnesComplementTransformation>(values);
}
[RequiresPreviewFeatures]
private unsafe static void Transform<TTransformation>(ref byte x, int length) where TTransformation : struct, IUnaryTransformation<UIntPtr>, IUnaryTransformation<Vector<byte>>
{
if (Vector.IsHardwareAccelerated) {
while (length >= Vector<byte>.Count) {
Unsafe.WriteUnaligned<Vector<byte>>(value: ((IUnaryTransformation<Vector<byte>>)x).Transform(Unsafe.ReadUnaligned<Vector<byte>>(ref x)), destination: ref *(byte*));
ref x = ref Unsafe.Add(ref x, Vector<byte>.Count);
length -= Vector<byte>.Count;
}
}
while (length >= UIntPtr.Size) {
Unsafe.WriteUnaligned<UIntPtr>(value: ((IUnaryTransformation<UIntPtr>)x).Transform(Unsafe.ReadUnaligned<UIntPtr>(ref x)), destination: ref *(byte*));
ref x = ref Unsafe.Add(ref x, UIntPtr.Size);
length -= UIntPtr.Size;
}
while (length > 0) {
byte num = (byte)(ulong)((IUnaryTransformation<UIntPtr>)x).Transform((UIntPtr)(void*)x);
*(sbyte*)(long)(IntPtr)(void*) = (sbyte)num;
ref x = ref Unsafe.Add(ref x, 1);
length--;
}
}
[RequiresPreviewFeatures]
private unsafe static void Transform<[System.Runtime.CompilerServices.IsUnmanaged] T, TTransformation>(Span<T> values) where T : struct where TTransformation : struct, IUnaryTransformation<UIntPtr>, IUnaryTransformation<Vector<byte>>
{
int val = Array.get_MaxLength() / sizeof(T);
while (!values.IsEmpty) {
int num = Math.Min(val, values.Length);
Transform<TTransformation>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)), num * sizeof(T));
values = values.Slice(num);
}
}
[RequiresPreviewFeatures]
private unsafe static void Transform<TTransformation>([In] ref byte x, ref byte y, int length) where TTransformation : struct, IBinaryTransformation<UIntPtr>, IBinaryTransformation<Vector<byte>>
{
if (Vector.IsHardwareAccelerated) {
while (length >= Vector<byte>.Count) {
Unsafe.WriteUnaligned<Vector<byte>>(value: ((IBinaryTransformation<Vector<byte>>)y).Transform(Unsafe.ReadUnaligned<Vector<byte>>(ref x), Unsafe.ReadUnaligned<Vector<byte>>(ref y)), destination: ref *(byte*));
ref x = ref Unsafe.Add(ref x, Vector<byte>.Count);
ref y = ref Unsafe.Add(ref y, Vector<byte>.Count);
length -= Vector<byte>.Count;
}
}
while (length >= UIntPtr.Size) {
Unsafe.WriteUnaligned<UIntPtr>(value: ((IBinaryTransformation<UIntPtr>)y).Transform(Unsafe.ReadUnaligned<UIntPtr>(ref x), Unsafe.ReadUnaligned<UIntPtr>(ref y)), destination: ref *(byte*));
ref x = ref Unsafe.Add(ref x, UIntPtr.Size);
ref y = ref Unsafe.Add(ref y, UIntPtr.Size);
length -= UIntPtr.Size;
}
while (length > 0) {
byte num = (byte)(ulong)((IBinaryTransformation<UIntPtr>)y).Transform((UIntPtr)(void*)x, (UIntPtr)(void*)y);
*(sbyte*)(long)(IntPtr)(void*) = (sbyte)num;
ref x = ref Unsafe.Add(ref x, 1);
ref y = ref Unsafe.Add(ref y, 1);
length--;
}
}
[RequiresPreviewFeatures]
private unsafe static void Transform<[System.Runtime.CompilerServices.IsUnmanaged] T, TTransformation>(ReadOnlySpan<T> x, Span<T> y) where T : struct where TTransformation : struct, IBinaryTransformation<UIntPtr>, IBinaryTransformation<Vector<byte>>
{
int val = Array.get_MaxLength() / sizeof(T);
while (!x.IsEmpty) {
int num = Math.Min(val, x.Length);
Transform<TTransformation>(ref Unsafe.As<T, byte>(ref Unsafe.AsRef(ref MemoryMarshal.GetReference(x))), ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(y)), num * sizeof(T));
x = x.Slice(num);
y = y.Slice(num);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<T> LoadVector128<[System.Runtime.CompilerServices.IsUnmanaged] T>(ref T input) where T : struct
{
return Unsafe.ReadUnaligned<Vector128<T>>(ref Unsafe.As<T, byte>(ref input));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<T> LoadVector128<[System.Runtime.CompilerServices.IsUnmanaged] T>(ReadOnlySpan<T> input) where T : struct
{
return LoadVector128(ref MemoryMarshal.GetReference(input));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<T> LoadVector256<[System.Runtime.CompilerServices.IsUnmanaged] T>(ref T input) where T : struct
{
return Unsafe.ReadUnaligned<Vector256<T>>(ref Unsafe.As<T, byte>(ref input));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<T> LoadVector256<[System.Runtime.CompilerServices.IsUnmanaged] T>(ReadOnlySpan<T> input) where T : struct
{
return LoadVector256(ref MemoryMarshal.GetReference(input));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void StoreVector128<[System.Runtime.CompilerServices.IsUnmanaged] T>(Vector128<T> input, ref T output) where T : struct
{
Unsafe.WriteUnaligned(ref Unsafe.As<T, byte>(ref output), input);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void StoreVector128<[System.Runtime.CompilerServices.IsUnmanaged] T>(Vector128<T> input, Span<T> output) where T : struct
{
StoreVector128(input, ref MemoryMarshal.GetReference(output));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void StoreVector256<[System.Runtime.CompilerServices.IsUnmanaged] T>(Vector256<T> input, ref T output) where T : struct
{
Unsafe.WriteUnaligned(ref Unsafe.As<T, byte>(ref output), input);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void StoreVector256<[System.Runtime.CompilerServices.IsUnmanaged] T>(Vector256<T> input, Span<T> output) where T : struct
{
StoreVector256(input, ref MemoryMarshal.GetReference(output));
}
[RequiresPreviewFeatures]
private unsafe static void ReverseEndianness<[System.Runtime.CompilerServices.IsUnmanaged] T, TTransformation>(Span<T> buffer, TTransformation transformation) where T : struct where TTransformation : struct, IEndiannessTransformation<T>
{
if (Ssse3.IsSupported) {
if (Avx2.IsSupported) {
Vector256<byte> mask = Vector256.Create(((IEndiannessTransformation<T>)transformation).ReorderMask, ((IEndiannessTransformation<T>)transformation).ReorderMask);
while (buffer.Length >= Vector256<T>.Count) {
StoreVector256(Avx2.Shuffle(LoadVector256<T>(buffer).AsByte(), mask).As<byte, T>(), buffer);
buffer = buffer.Slice(Vector256<T>.Count);
}
}
while (buffer.Length >= Vector128<T>.Count) {
StoreVector128(Ssse3.Shuffle(LoadVector128<T>(buffer).AsByte(), ((IEndiannessTransformation<T>)transformation).ReorderMask).As<byte, T>(), buffer);
buffer = buffer.Slice(Vector128<T>.Count);
}
}
Span<T> span = buffer;
for (int i = 0; i < span.Length; i++) {
ref T reference = ref span[i];
T val = ((IUnaryTransformation<T>)reference).Transform(reference);
*(T*)(long)(IntPtr)(void*) = val;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[RequiresPreviewFeatures]
private unsafe static void ReverseEndianness<[System.Runtime.CompilerServices.IsUnmanaged] T, TTransformation>(Span<T> buffer) where T : struct where TTransformation : struct, IEndiannessTransformation<T>
{
switch (buffer.Length) {
case 0:
break;
case 1: {
ref T reference = ref buffer[0];
T val = ((IUnaryTransformation<T>)reference).Transform(reference);
*(T*)(long)(IntPtr)(void*) = val;
break;
}
default:
ReverseEndianness(buffer, new TTransformation());
break;
}
}
public static void ReverseEndianness(this Span<short> buffer)
{
ReverseEndianness<ushort, UInt16Transformation>(Intrinsics.ReinterpretCast<short, ushort>(buffer));
}
[CLSCompliant(false)]
public static void ReverseEndianness(this Span<ushort> buffer)
{
ReverseEndianness<ushort, UInt16Transformation>(buffer);
}
public static void ReverseEndianness(this Span<int> buffer)
{
ReverseEndianness<uint, UInt32Transformation>(Intrinsics.ReinterpretCast<int, uint>(buffer));
}
[CLSCompliant(false)]
public static void ReverseEndianness(this Span<uint> buffer)
{
ReverseEndianness<uint, UInt32Transformation>(buffer);
}
public static void ReverseEndianness(this Span<long> buffer)
{
ReverseEndianness<ulong, UInt64Transformation>(Intrinsics.ReinterpretCast<long, ulong>(buffer));
}
[CLSCompliant(false)]
public static void ReverseEndianness(this Span<ulong> buffer)
{
ReverseEndianness<ulong, UInt64Transformation>(buffer);
}
}
}