DotNext by .NET Foundation and Contributors

<PackageReference Include="DotNext" Version="4.8.1" />

 Hex

public static class 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; } } }