<PackageReference Include="System.Text.Json" Version="7.0.4" />

JsonReaderHelper

static class JsonReaderHelper
using System.Buffers; using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Text.Json { internal static class JsonReaderHelper { private const ulong XorPowerOfTwoToHighByte = 283686952306184; public static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(false, true); public static (int, int) CountNewLines(ReadOnlySpan<byte> data) { int item = -1; int num = 0; for (int i = 0; i < data.Length; i++) { if (data[i] == 10) { item = i; num++; } } return (num, item); } internal static JsonValueKind ToValueKind(this JsonTokenType tokenType) { switch (tokenType) { case JsonTokenType.None: return JsonValueKind.Undefined; case JsonTokenType.StartArray: return JsonValueKind.Array; case JsonTokenType.StartObject: return JsonValueKind.Object; case JsonTokenType.String: case JsonTokenType.Number: case JsonTokenType.True: case JsonTokenType.False: case JsonTokenType.Null: return (JsonValueKind)(tokenType - 4); default: return JsonValueKind.Undefined; } } public static bool IsTokenTypePrimitive(JsonTokenType tokenType) { return (int)(tokenType - 7) <= 4; } public static bool IsHexDigit(byte nextByte) { return System.HexConverter.IsHexChar(nextByte); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span) { return IndexOfOrLessThan(ref MemoryMarshal.GetReference(span), 34, 92, 32, span.Length); } private unsafe static int IndexOfOrLessThan(ref byte searchSpace, byte value0, byte value1, byte lessThan, int length) { IntPtr intPtr = (IntPtr)0; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1); intPtr2 = (IntPtr)((Vector<byte>.Count - num) & (Vector<byte>.Count - 1)); } while (true) { uint num2; if ((ulong)(void*)intPtr2 < 8) { if ((ulong)(void*)intPtr2 >= 4) { intPtr2 -= 4; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_0393; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_039b; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_03a9; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_03b7; intPtr += 4; } while ((void*)intPtr2 != null) { intPtr2 -= 1; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2 && lessThan <= num2) { intPtr += 1; continue; } goto IL_0393; } if (Vector.IsHardwareAccelerated && (int)(void*)intPtr < length) { intPtr2 = (IntPtr)((length - (int)(void*)intPtr) & ~(Vector<byte>.Count - 1)); Vector<byte> right = new Vector<byte>(value0); Vector<byte> right2 = new Vector<byte>(value1); Vector<byte> right3 = new Vector<byte>(lessThan); while ((void*)intPtr2 > (void*)intPtr) { Vector<byte> left = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr)); Vector<byte> vector = Vector.BitwiseOr(Vector.BitwiseOr(Vector.Equals(left, right), Vector.Equals(left, right2)), Vector.LessThan(left, right3)); if (!Vector<byte>.Zero.Equals(vector)) return (int)(void*)intPtr + LocateFirstFoundByte(vector); intPtr += Vector<byte>.Count; } if ((int)(void*)intPtr < length) { intPtr2 = (IntPtr)(length - (int)(void*)intPtr); continue; } } return -1; } intPtr2 -= 8; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_0393; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_039b; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2 || lessThan > num2) goto IL_03a9; num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 != num2 && value1 != num2 && lessThan <= num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 4); if (value0 == num2 || value1 == num2 || lessThan > num2) return (int)(void*)(intPtr + 4); num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 5); if (value0 == num2 || value1 == num2 || lessThan > num2) return (int)(void*)(intPtr + 5); num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 6); if (value0 == num2 || value1 == num2 || lessThan > num2) return (int)(void*)(intPtr + 6); num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 7); if (value0 == num2 || value1 == num2 || lessThan > num2) break; intPtr += 8; continue; } goto IL_03b7; IL_0393: return (int)(void*)intPtr; IL_039b: return (int)(void*)(intPtr + 1); IL_03b7: return (int)(void*)(intPtr + 3); IL_03a9: return (int)(void*)(intPtr + 2); } return (int)(void*)(intPtr + 7); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(Vector<byte> match) { Vector<ulong> vector = Vector.AsVectorUInt64(match); ulong num = 0; int i; for (i = 0; i < Vector<ulong>.Count; i++) { num = vector[i]; if (num != 0) break; } return i * 8 + LocateFirstFoundByte(num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(ulong match) { ulong num = match ^ (match - 1); return (int)(num * 283686952306184 >> 57); } public unsafe static bool TryGetEscapedDateTime(ReadOnlySpan<byte> source, out DateTime value) { Span<byte> span = new Span<byte>(stackalloc byte[252], 252); Span<byte> span2 = span; Unescape(source, span2, out int written); span2 = span2.Slice(0, written); if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(span2.Length) && JsonHelpers.TryParseAsISO(span2, out DateTime value2)) { value = value2; return true; } value = default(DateTime); return false; } public unsafe static bool TryGetEscapedDateTimeOffset(ReadOnlySpan<byte> source, out DateTimeOffset value) { Span<byte> span = new Span<byte>(stackalloc byte[252], 252); Span<byte> span2 = span; Unescape(source, span2, out int written); span2 = span2.Slice(0, written); if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(span2.Length) && JsonHelpers.TryParseAsISO(span2, out DateTimeOffset value2)) { value = value2; return true; } value = default(DateTimeOffset); return false; } public unsafe static bool TryGetEscapedGuid(ReadOnlySpan<byte> source, out Guid value) { Span<byte> span = new Span<byte>(stackalloc byte[216], 216); Span<byte> span2 = span; Unescape(source, span2, out int written); span2 = span2.Slice(0, written); if (span2.Length == 36 && Utf8Parser.TryParse(span2, out Guid value2, out int _, 'D')) { value = value2; return true; } value = default(Guid); return false; } public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out float value) { if (span.Length == 3) { if (span.SequenceEqual(JsonConstants.NaNValue)) { value = NaN; return true; } } else if (span.Length == 8) { if (span.SequenceEqual(JsonConstants.PositiveInfinityValue)) { value = Infinity; return true; } } else if (span.Length == 9 && span.SequenceEqual(JsonConstants.NegativeInfinityValue)) { value = -Infinity; return true; } value = 0; return false; } public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out double value) { if (span.Length == 3) { if (span.SequenceEqual(JsonConstants.NaNValue)) { value = NaN; return true; } } else if (span.Length == 8) { if (span.SequenceEqual(JsonConstants.PositiveInfinityValue)) { value = Infinity; return true; } } else if (span.Length == 9 && span.SequenceEqual(JsonConstants.NegativeInfinityValue)) { value = -Infinity; return true; } value = 0; return false; } public unsafe static bool TryGetUnescapedBase64Bytes(ReadOnlySpan<byte> utf8Source, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out byte[] bytes) { byte[] array = null; Span<byte> span2; if (utf8Source.Length <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(utf8Source.Length)); Span<byte> span3 = span2; Unescape(utf8Source, span3, out int written); span3 = span3.Slice(0, written); bool result = TryDecodeBase64InPlace(span3, out bytes); if (array != null) { span3.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public unsafe static string GetUnescapedString(ReadOnlySpan<byte> utf8Source) { int length = utf8Source.Length; byte[] array = null; Span<byte> span2; if (length <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(length)); Span<byte> span3 = span2; Unescape(utf8Source, span3, out int written); span3 = span3.Slice(0, written); string result = TranscodeHelper(span3); if (array != null) { span3.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public unsafe static ReadOnlySpan<byte> GetUnescapedSpan(ReadOnlySpan<byte> utf8Source) { int length = utf8Source.Length; byte[] array = null; Span<byte> span; Span<byte> span2; if (length <= 256) { span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(length)); Span<byte> destination = span2; Unescape(utf8Source, destination, out int written); span = destination.Slice(0, written); ReadOnlySpan<byte> result = span.ToArray(); if (array != null) { span = new Span<byte>(array, 0, written); span.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public unsafe static bool UnescapeAndCompare(ReadOnlySpan<byte> utf8Source, ReadOnlySpan<byte> other) { byte[] array = null; Span<byte> span2; if (utf8Source.Length <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(utf8Source.Length)); Span<byte> span3 = span2; Unescape(utf8Source, span3, 0, out int written); span3 = span3.Slice(0, written); bool result = other.SequenceEqual(span3); if (array != null) { span3.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public unsafe static bool UnescapeAndCompare(ReadOnlySequence<byte> utf8Source, ReadOnlySpan<byte> other) { byte[] array = null; byte[] array2 = null; int num = checked((int)utf8Source.Length); Span<byte> span2; if (num <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array2 = ArrayPool<byte>.Shared.Rent(num)); Span<byte> span3 = span2; Span<byte> span4; if (num <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span4 = span; } else span4 = (array = ArrayPool<byte>.Shared.Rent(num)); Span<byte> span5 = span4; ref utf8Source.CopyTo(span5); span5 = span5.Slice(0, num); Unescape(span5, span3, 0, out int written); span3 = span3.Slice(0, written); bool result = other.SequenceEqual(span3); if (array2 != null) { span3.Clear(); ArrayPool<byte>.Shared.Return(array2, false); span5.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public static bool TryDecodeBase64InPlace(Span<byte> utf8Unescaped, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out byte[] bytes) { if (Base64.DecodeFromUtf8InPlace(utf8Unescaped, out int bytesWritten) != 0) { bytes = null; return false; } bytes = utf8Unescaped.Slice(0, bytesWritten).ToArray(); return true; } public unsafe static bool TryDecodeBase64(ReadOnlySpan<byte> utf8Unescaped, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out byte[] bytes) { byte[] array = null; Span<byte> span; Span<byte> span2; if (utf8Unescaped.Length <= 256) { span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(utf8Unescaped.Length)); Span<byte> bytes2 = span2; if (Base64.DecodeFromUtf8(utf8Unescaped, bytes2, out int _, out int bytesWritten, true) != 0) { bytes = null; if (array != null) { bytes2.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return false; } span = bytes2.Slice(0, bytesWritten); bytes = span.ToArray(); if (array != null) { bytes2.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return true; } public unsafe static string TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped) { try { if (!utf8Unescaped.IsEmpty) try { fixed (byte* bytes = &utf8Unescaped.GetPinnableReference()) { return s_utf8Encoding.GetString(bytes, utf8Unescaped.Length); } } finally { } return string.Empty; } catch (DecoderFallbackException innerException) { throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException); } } public unsafe static int TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped, Span<char> destination) { try { if (!utf8Unescaped.IsEmpty) try { fixed (byte* bytes = &utf8Unescaped.GetPinnableReference()) { try { fixed (char* chars = &destination.GetPinnableReference()) { return s_utf8Encoding.GetChars(bytes, utf8Unescaped.Length, chars, destination.Length); } } finally { } } } finally { } return 0; } catch (DecoderFallbackException innerException) { throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException); } catch (ArgumentException) { destination.Clear(); throw; } } public unsafe static void ValidateUtf8(ReadOnlySpan<byte> utf8Buffer) { try { if (!utf8Buffer.IsEmpty) try { fixed (byte* bytes = &utf8Buffer.GetPinnableReference()) { s_utf8Encoding.GetCharCount(bytes, utf8Buffer.Length); } } finally { } } catch (DecoderFallbackException innerException) { throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException); } } internal unsafe static int GetUtf8ByteCount(ReadOnlySpan<char> text) { try { if (!text.IsEmpty) try { fixed (char* chars = &text.GetPinnableReference()) { return s_utf8Encoding.GetByteCount(chars, text.Length); } } finally { } return 0; } catch (EncoderFallbackException innerException) { throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(innerException); } } internal unsafe static int GetUtf8FromText(ReadOnlySpan<char> text, Span<byte> dest) { try { if (!text.IsEmpty) try { fixed (char* chars = &text.GetPinnableReference()) { try { fixed (byte* bytes = &dest.GetPinnableReference()) { return s_utf8Encoding.GetBytes(chars, text.Length, bytes, dest.Length); } } finally { } } } finally { } return 0; } catch (EncoderFallbackException innerException) { throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(innerException); } } internal unsafe static string GetTextFromUtf8(ReadOnlySpan<byte> utf8Text) { if (!utf8Text.IsEmpty) { fixed (byte* bytes = &utf8Text.GetPinnableReference()) { return s_utf8Encoding.GetString(bytes, utf8Text.Length); } } return string.Empty; } internal static void Unescape(ReadOnlySpan<byte> source, Span<byte> destination, out int written) { int idx = source.IndexOf<byte>(92); bool flag = TryUnescape(source, destination, idx, out written); } internal static void Unescape(ReadOnlySpan<byte> source, Span<byte> destination, int idx, out int written) { bool flag = TryUnescape(source, destination, idx, out written); } internal static bool TryUnescape(ReadOnlySpan<byte> source, Span<byte> destination, out int written) { int idx = source.IndexOf<byte>(92); return TryUnescape(source, destination, idx, out written); } private static bool TryUnescape(ReadOnlySpan<byte> source, Span<byte> destination, int idx, out int written) { ReadOnlySpan<byte> readOnlySpan = source.Slice(0, idx); if (!readOnlySpan.TryCopyTo(destination)) written = 0; else { written = idx; while (true) { if (written != destination.Length) { switch (source[++idx]) { case 34: destination[written++] = 34; goto IL_025b; case 110: destination[written++] = 10; goto IL_025b; case 114: destination[written++] = 13; goto IL_025b; case 92: destination[written++] = 92; goto IL_025b; case 47: destination[written++] = 47; goto IL_025b; case 116: destination[written++] = 9; goto IL_025b; case 98: destination[written++] = 8; goto IL_025b; case 102: destination[written++] = 12; goto IL_025b; default: { bool flag = Utf8Parser.TryParse(source.Slice(idx + 1, 4), out int value, out int bytesConsumed, 'x'); idx += 4; if (JsonHelpers.IsInRangeInclusive((uint)value, 55296, 57343)) { if (value >= 56320) ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16(value); if (source.Length < idx + 7 || source[idx + 1] != 92 || source[idx + 2] != 117) ThrowHelper.ThrowInvalidOperationException_ReadIncompleteUTF16(); flag = Utf8Parser.TryParse(source.Slice(idx + 3, 4), out int value2, out bytesConsumed, 'x'); idx += 6; if (!JsonHelpers.IsInRangeInclusive((uint)value2, 56320, 57343)) ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16(value2); value = 1024 * (value - 55296) + (value2 - 56320) + 65536; } if (!TryEncodeToUtf8Bytes((uint)value, destination.Slice(written), out int bytesWritten)) break; written += bytesWritten; goto IL_025b; } IL_025b: if (++idx != source.Length) { if (source[idx] != 92) { ReadOnlySpan<byte> span = source.Slice(idx); int num = span.IndexOf<byte>(92); if (num < 0) num = span.Length; if ((uint)(written + num) >= (uint)destination.Length) break; switch (num) { case 1: destination[written++] = source[idx++]; break; case 2: destination[written++] = source[idx++]; destination[written++] = source[idx++]; break; case 3: destination[written++] = source[idx++]; destination[written++] = source[idx++]; destination[written++] = source[idx++]; break; default: readOnlySpan = span.Slice(0, num); readOnlySpan.CopyTo(destination.Slice(written)); written += num; idx += num; break; } if (idx == source.Length) goto IL_03d8; } continue; } goto IL_03d8; IL_03d8: return true; } } break; } } return false; } private static bool TryEncodeToUtf8Bytes(uint scalar, Span<byte> utf8Destination, out int bytesWritten) { if (scalar < 128) { if ((uint)utf8Destination.Length < 1) { bytesWritten = 0; return false; } utf8Destination[0] = (byte)scalar; bytesWritten = 1; } else if (scalar < 2048) { if ((uint)utf8Destination.Length < 2) { bytesWritten = 0; return false; } utf8Destination[0] = (byte)(192 | (scalar >> 6)); utf8Destination[1] = (byte)(128 | (scalar & 63)); bytesWritten = 2; } else if (scalar < 65536) { if ((uint)utf8Destination.Length < 3) { bytesWritten = 0; return false; } utf8Destination[0] = (byte)(224 | (scalar >> 12)); utf8Destination[1] = (byte)(128 | ((scalar >> 6) & 63)); utf8Destination[2] = (byte)(128 | (scalar & 63)); bytesWritten = 3; } else { if ((uint)utf8Destination.Length < 4) { bytesWritten = 0; return false; } utf8Destination[0] = (byte)(240 | (scalar >> 18)); utf8Destination[1] = (byte)(128 | ((scalar >> 12) & 63)); utf8Destination[2] = (byte)(128 | ((scalar >> 6) & 63)); utf8Destination[3] = (byte)(128 | (scalar & 63)); bytesWritten = 4; } return true; } } }