Hex
Provides conversion to/from hexadecimal representation.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace DotNext.Buffers.Text
{
public static class Hex
{
private const byte NibbleMaxValue = 15;
private static readonly char[] NibbleToUtf16CharLookupTable = new char[32] {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'A',
'B',
'C',
'D',
'E',
'F'
};
private unsafe static ReadOnlySpan<byte> CharToNibbleLookupTable => new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.3119C902A2D30870A3FC3661C8D3CC542815988CC258DFA0A4B9396E04855905, 103);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<byte> NibbleToUtf8CharLookupTable(bool lowercased)
{
if (!lowercased)
return Vector128.Create((byte)48, (byte)49, (byte)50, (byte)51, (byte)52, (byte)53, (byte)54, (byte)55, (byte)56, (byte)57, (byte)65, (byte)66, (byte)67, (byte)68, (byte)69, (byte)70);
return Vector128.Create((byte)48, (byte)49, (byte)50, (byte)51, (byte)52, (byte)53, (byte)54, (byte)55, (byte)56, (byte)57, (byte)97, (byte)98, (byte)99, (byte)100, (byte)101, (byte)102);
}
private static byte ToNibble(int ch)
{
ReadOnlySpan<byte> charToNibbleLookupTable = CharToNibbleLookupTable;
byte result = default(byte);
if ((uint)ch >= (uint)charToNibbleLookupTable.Length || (result = charToNibbleLookupTable[ch]) > 15)
<ToNibble>g__ThrowFormatException|5_0(ch);
return result;
}
public unsafe static int EncodeToUtf16([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(false, true)] Span<char> output, bool lowercased = false)
{
if (bytes.IsEmpty || output.IsEmpty)
return 0;
int num = Math.Min(bytes.Length, output.Length >> 1);
ref byte reference = ref MemoryMarshal.GetReference(bytes);
ref char reference2 = ref MemoryMarshal.GetReference(output);
int num2;
if (Ssse3.IsSupported) {
num2 = num;
Vector128<byte> vector = NibbleToUtf8CharLookupTable(lowercased);
if (Avx2.IsSupported && num2 >= 8) {
Vector256<byte> value = Vector256.Create(vector, vector);
Vector256<byte> right = Vector256.Create((byte)15);
Vector256<byte> mask = Vector256.Create(0, byte.MaxValue, 1, byte.MaxValue, 2, byte.MaxValue, 3, byte.MaxValue, 4, byte.MaxValue, 5, byte.MaxValue, 6, byte.MaxValue, 7, byte.MaxValue, 0, byte.MaxValue, 1, byte.MaxValue, 2, byte.MaxValue, 3, byte.MaxValue, 4, byte.MaxValue, 5, byte.MaxValue, 6, byte.MaxValue, 7, byte.MaxValue);
do {
Vector256<byte> vector2 = Vector256.Create(Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned<uint>(ref reference)), Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref reference, 4)))).AsByte();
Vector256<byte> left = Avx2.UnpackLow(Avx2.ShiftRightLogical(vector2.AsUInt32(), 4).AsByte(), vector2);
left = Avx2.And(left, right);
left = Avx2.Shuffle(value, left);
left = Avx2.Shuffle(left, mask);
fixed (char* address = &reference2) {
Avx.Store((byte*)address, left);
}
ref reference = ref Unsafe.Add(ref reference, 8);
ref reference2 = ref Unsafe.Add(ref reference2, 16);
num2 -= 8;
} while (num2 >= 8);
}
if (num2 >= 4) {
Vector128<byte> right2 = Vector128.Create((byte)15);
Vector128<byte> mask2 = Vector128.Create(0, byte.MaxValue, 1, byte.MaxValue, 2, byte.MaxValue, 3, byte.MaxValue, 4, byte.MaxValue, 5, byte.MaxValue, 6, byte.MaxValue, 7, byte.MaxValue);
do {
Vector128<byte> vector3 = Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned<uint>(ref reference)).AsByte();
Vector128<byte> left2 = Sse2.UnpackLow(Sse2.ShiftRightLogical(vector3.AsUInt32(), 4).AsByte(), vector3);
left2 = Sse2.And(left2, right2);
left2 = Ssse3.Shuffle(vector, left2);
left2 = Ssse3.Shuffle(left2, mask2);
fixed (char* address = &reference2) {
Sse2.Store((byte*)address, left2);
}
ref reference = ref Unsafe.Add(ref reference, 4);
ref reference2 = ref Unsafe.Add(ref reference2, 8);
num2 -= 4;
} while (num2 >= 4);
}
num2 = num - num2;
} else
num2 = 0;
ref char source = ref MemoryMarshal.GetArrayDataReference(NibbleToUtf16CharLookupTable);
if (!lowercased)
ref source = ref Unsafe.Add(ref source, 16);
while (num2 < num) {
byte b = reference;
reference2 = Unsafe.Add(ref source, b >> 4);
ref reference2 = ref Unsafe.Add(ref reference2, 1);
reference2 = Unsafe.Add(ref source, b & 15);
num2++;
ref reference2 = ref Unsafe.Add(ref reference2, 1);
ref reference = ref Unsafe.Add(ref reference, 1);
}
return num << 1;
}
[return: System.Runtime.CompilerServices.Nullable(1)]
public static string EncodeToUtf16([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, bool lowercased = false)
{
int num = bytes.Length << 1;
string text;
if (num == 0)
text = string.Empty;
else {
text = new string(' ', num);
EncodeToUtf16(bytes, MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference<char>(text), num), lowercased);
}
return text;
}
public static int DecodeFromUtf16([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [LifetimeAnnotation(false, true)] Span<byte> output)
{
if (chars.IsEmpty || output.IsEmpty)
return 0;
int num = Math.Min(chars.Length, output.Length << 1);
num &= -2;
ref byte reference = ref MemoryMarshal.GetReference(output);
int num2 = 0;
while (num2 < num) {
char ch = Unsafe.Add(ref MemoryMarshal.GetReference(chars), num2);
char ch2 = Unsafe.Add(ref MemoryMarshal.GetReference(chars), num2 + 1);
reference = (byte)(ToNibble(ch2) | (ToNibble(ch) << 4));
num2 += 2;
ref reference = ref Unsafe.Add(ref reference, 1);
}
return num >> 1;
}
public unsafe static int EncodeToUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(false, true)] Span<byte> output, bool lowercased = false)
{
if (bytes.IsEmpty || output.IsEmpty)
return 0;
int num = Math.Min(bytes.Length, output.Length >> 1);
ref byte reference = ref MemoryMarshal.GetReference(bytes);
ref byte reference2 = ref MemoryMarshal.GetReference(output);
int num2;
if (Ssse3.IsSupported) {
num2 = num;
Vector128<byte> vector = NibbleToUtf8CharLookupTable(lowercased);
if (Avx2.IsSupported && num2 >= 16) {
Vector256<byte> value = Vector256.Create(vector, vector);
Vector256<byte> right = Vector256.Create((byte)15);
do {
Vector256<byte> vector2 = Vector256.Create(Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned<ulong>(ref reference)), Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref reference, 8)))).AsByte();
Vector256<byte> left = Avx2.UnpackLow(Avx2.ShiftRightLogical(vector2.AsUInt32(), 4).AsByte(), vector2);
left = Avx2.And(left, right);
left = Avx2.Shuffle(value, left);
fixed (byte* address = &reference2) {
Avx.Store(address, left);
}
ref reference = ref Unsafe.Add(ref reference, 16);
ref reference2 = ref Unsafe.Add(ref reference2, 32);
num2 -= 16;
} while (num2 >= 16);
}
if (num2 >= 8) {
Vector128<byte> right2 = Vector128.Create((byte)15);
do {
Vector128<byte> vector3 = Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned<ulong>(ref reference)).AsByte();
Vector128<byte> left2 = Sse2.UnpackLow(Sse2.ShiftRightLogical(vector3.AsUInt64(), 4).AsByte(), vector3);
left2 = Sse2.And(left2, right2);
left2 = Ssse3.Shuffle(vector, left2);
fixed (byte* address = &reference2) {
Sse2.Store(address, left2);
}
ref reference = ref Unsafe.Add(ref reference, 8);
ref reference2 = ref Unsafe.Add(ref reference2, 16);
num2 -= 8;
} while (num2 >= 8);
}
num2 = num - num2;
} else
num2 = 0;
ref char source = ref MemoryMarshal.GetArrayDataReference(NibbleToUtf16CharLookupTable);
if (!lowercased)
ref source = ref Unsafe.Add(ref source, 16);
while (num2 < num) {
byte b = reference;
reference2 = (byte)Unsafe.Add(ref source, b >> 4);
ref reference2 = ref Unsafe.Add(ref reference2, 1);
reference2 = (byte)Unsafe.Add(ref source, b & 15);
num2++;
ref reference2 = ref Unsafe.Add(ref reference2, 1);
ref reference = ref Unsafe.Add(ref reference, 1);
}
return num << 1;
}
[return: System.Runtime.CompilerServices.Nullable(1)]
public static byte[] EncodeToUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, bool lowercased = false)
{
int num = bytes.Length << 1;
byte[] array;
if (num == 0)
array = Array.Empty<byte>();
else {
array = GC.AllocateUninitializedArray<byte>(num, false);
num = EncodeToUtf8(bytes, array, lowercased);
}
return array;
}
public static int DecodeFromUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> chars, [LifetimeAnnotation(false, true)] Span<byte> output)
{
if (chars.IsEmpty || output.IsEmpty)
return 0;
int num = Math.Min(chars.Length, output.Length << 1);
num &= -2;
ref byte reference = ref MemoryMarshal.GetReference(output);
int num2 = 0;
while (num2 < num) {
byte ch = Unsafe.Add(ref MemoryMarshal.GetReference(chars), num2);
byte ch2 = Unsafe.Add(ref MemoryMarshal.GetReference(chars), num2 + 1);
reference = (byte)(ToNibble(ch2) | (ToNibble(ch) << 4));
num2 += 2;
ref reference = ref Unsafe.Add(ref reference, 1);
}
return num >> 1;
}
}
}