11 internal static class HexConverter
14 public enum Casing : uint
66 [MethodImpl(MethodImplOptions.AggressiveInlining)]
67 public static void ToBytesBuffer(
byte value, Span<byte> buffer,
int startingIndex = 0, Casing casing = Casing.Upper)
69 uint difference = ((value & 0xF0U) << 4) + (value & 0x0FU) - 0x8989U;
70 uint packedResult = (((uint)-(int)difference & 0x7070U) >> 4) + difference + 0xB9B9U | (uint)casing;
72 buffer[startingIndex + 1] = (byte)packedResult;
73 buffer[startingIndex] = (byte)(packedResult >> 8);
76 [MethodImpl(MethodImplOptions.AggressiveInlining)]
77 public static void ToCharsBuffer(
byte value, Span<char> buffer,
int startingIndex = 0, Casing casing = Casing.Upper)
79 uint difference = ((value & 0xF0U) << 4) + (value & 0x0FU) - 0x8989U;
80 uint packedResult = (((uint)-(int)difference & 0x7070U) >> 4) + difference + 0xB9B9U | (uint)casing;
82 buffer[startingIndex + 1] = (char)(packedResult & 0xFF);
83 buffer[startingIndex] = (char)(packedResult >> 8);
86#if SYSTEM_PRIVATE_CORELIB
88 [MethodImpl(MethodImplOptions.AggressiveInlining)]
89 [CompExactlyDependsOn(typeof(Ssse3))]
90 [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
91 internal static (Vector128<byte>, Vector128<byte>) AsciiToHexVector128(Vector128<byte> src, Vector128<byte> hexMap)
93 Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported);
97 Vector128<byte> shiftedSrc = Vector128.ShiftRightLogical(src.AsUInt64(), 4).AsByte();
98 Vector128<byte> lowNibbles = Vector128.UnpackLow(shiftedSrc, src);
99 Vector128<byte> highNibbles = Vector128.UnpackHigh(shiftedSrc, src);
101 return (Vector128.ShuffleUnsafe(hexMap, lowNibbles & Vector128.Create((
byte)0xF)),
102 Vector128.ShuffleUnsafe(hexMap, highNibbles & Vector128.Create((
byte)0xF)));
105 [CompExactlyDependsOn(typeof(Ssse3))]
106 [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
107 private static void EncodeToUtf16_Vector128(ReadOnlySpan<byte> bytes, Span<char> chars, Casing casing)
109 Debug.Assert(bytes.Length >= Vector128<int>.Count);
111 ref byte srcRef =
ref MemoryMarshal.GetReference(bytes);
112 ref ushort destRef =
ref Unsafe.As<char, ushort>(
ref MemoryMarshal.GetReference(chars));
114 Vector128<byte> hexMap = casing == Casing.Upper ?
115 Vector128.Create((
byte)
'0', (
byte)
'1', (
byte)
'2', (
byte)
'3',
116 (
byte)
'4', (
byte)
'5', (
byte)
'6', (
byte)
'7',
117 (
byte)
'8', (
byte)
'9', (
byte)
'A', (
byte)
'B',
118 (
byte)
'C', (
byte)
'D', (
byte)
'E', (
byte)
'F') :
119 Vector128.Create((
byte)
'0', (
byte)
'1', (
byte)
'2', (
byte)
'3',
120 (
byte)
'4', (
byte)
'5', (
byte)
'6', (
byte)
'7',
121 (
byte)
'8', (
byte)
'9', (
byte)
'a', (
byte)
'b',
122 (
byte)
'c', (
byte)
'd', (
byte)
'e', (
byte)
'f');
125 nuint lengthSubVector128 = (nuint)bytes.Length - (nuint)Vector128<int>.Count;
133 uint i32 = Unsafe.ReadUnaligned<uint>(
ref Unsafe.Add(
ref srcRef, pos));
134 Vector128<byte> vec = Vector128.CreateScalar(i32).AsByte();
137 (Vector128<byte> hexLow, _) = AsciiToHexVector128(vec, hexMap);
138 (Vector128<ushort> v0, _) = Vector128.Widen(hexLow);
140 v0.StoreUnsafe(
ref destRef, pos * 2);
142 pos += (nuint)Vector128<int>.Count;
143 if (pos == (nuint)bytes.Length)
149 if (pos > lengthSubVector128)
151 pos = lengthSubVector128;
158 public static void EncodeToUtf16(ReadOnlySpan<byte> bytes, Span<char> chars, Casing casing = Casing.Upper)
160 Debug.Assert(chars.Length >= bytes.Length * 2);
162 for (
int pos = 0; pos < bytes.Length; pos++)
164 ToCharsBuffer(bytes[pos], chars, pos * 2, casing);
173 static readonly unsafe SpanAction<char, (nint ptr,
int len, Casing cas)> EncodeToUtf16Action = (Span<char> chars, (nint ptr,
int len, Casing cas) args) =>
175 EncodeToUtf16(
new ReadOnlySpan<byte>((
void*)args.ptr, args.len), chars, args.cas);
180 public static unsafe
string ToString(ReadOnlySpan<byte> bytes, Casing casing = Casing.Upper)
182#if NETFRAMEWORK || NETSTANDARD2_0
183 var result = bytes.Length > 16 ?
new char[bytes.Length * 2].AsSpan() : stackalloc
char[bytes.Length * 2];
186 foreach (
byte b
in bytes)
188 ToCharsBuffer(b, result, pos, casing);
192 return result.ToString();
194 fixed (
byte* b = bytes)
195 return string.Create(bytes.Length * 2, ((nint)b, bytes.Length, casing), EncodeToUtf16Action);
199 [MethodImpl(MethodImplOptions.AggressiveInlining)]
200 public static char ToCharUpper(
int value)
206 value +=
'A' - (
'9' + 1);
211 [MethodImpl(MethodImplOptions.AggressiveInlining)]
212 public static char ToCharLower(
int value)
218 value +=
'a' - (
'9' + 1);
223 public static bool TryDecodeFromUtf16(ReadOnlySpan<char> chars, Span<byte> bytes, out
int charsProcessed)
225 return TryDecodeFromUtf16_Scalar(chars, bytes, out charsProcessed);
228 static bool TryDecodeFromUtf16_Scalar(ReadOnlySpan<char> chars, Span<byte> bytes, out
int charsProcessed)
230 Debug.Assert(chars.Length % 2 == 0,
"Un-even number of characters provided");
231 Debug.Assert(chars.Length / 2 == bytes.Length,
"Target buffer not right-sized for provided characters");
237 while (j < bytes.Length)
239 byteLo = FromChar(chars[i + 1]);
240 byteHi = FromChar(chars[i]);
244 if ((byteLo | byteHi) == 0xFF)
247 bytes[j++] = (byte)(byteHi << 4 | byteLo);
255 return (byteLo | byteHi) != 0xFF;
258 [MethodImpl(MethodImplOptions.AggressiveInlining)]
259 public static int FromChar(
int c)
261 return c >= CharToHexLookup.Length ? 0xFF : CharToHexLookup[c];
264 [MethodImpl(MethodImplOptions.AggressiveInlining)]
265 public static int FromUpperChar(
int c)
267 return c > 71 ? 0xFF : CharToHexLookup[c];
270 [MethodImpl(MethodImplOptions.AggressiveInlining)]
271 public static int FromLowerChar(
int c)
273 if ((uint)(c -
'0') <=
'9' -
'0')
276 if ((uint)(c -
'a') <=
'f' -
'a')
282 [MethodImpl(MethodImplOptions.AggressiveInlining)]
283 public static bool IsHexChar(
int c)
285 if (IntPtr.Size == 8)
303 ulong i = (uint)c -
'0';
304 ulong shift = 18428868213665201664UL << (int)i;
307 return (
long)(shift & mask) < 0 ?
true :
false;
310 return FromChar(c) != 0xFF;
313 [MethodImpl(MethodImplOptions.AggressiveInlining)]
314 public static bool IsHexUpperChar(
int c)
316 return (uint)(c -
'0') <= 9 || (uint)(c -
'A') <=
'F' -
'A';
319 [MethodImpl(MethodImplOptions.AggressiveInlining)]
320 public static bool IsHexLowerChar(
int c)
322 return (uint)(c -
'0') <= 9 || (uint)(c -
'a') <=
'f' -
'a';
326 public static ReadOnlySpan<byte> CharToHexLookup =>
328 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
329 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
330 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
331 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
332 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
333 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
334 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
335 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
336 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
337 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
338 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
339 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
340 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
341 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
342 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
343 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF