<PackageReference Include="System.Text.Json" Version="10.0.0-preview.6.25358.103" />

JsonReaderHelper

static class JsonReaderHelper
using System.Buffers; using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.Unicode; namespace System.Text.Json { internal static class JsonReaderHelper { private static readonly SearchValues<char> s_specialCharacters = SearchValues.Create(". '/\"[]()\t\n\r \b\\…

".AsSpan()); public static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(false, true); private static readonly SearchValues<byte> s_controlQuoteBackslash = SearchValues.Create(new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.AF588FFF8363545AB79B72C6BC89DA690C48DC7F5ED52015A88E121C552F86E1, 34)); public static bool ContainsSpecialCharacters(this ReadOnlySpan<char> text) { return text.ContainsAny(s_specialCharacters); } public static (int, int) CountNewLines(ReadOnlySpan<byte> data) { int num = data.LastIndexOf<byte>(10); int item = 0; if (num >= 0) { item = 1; data = data.Slice(0, num); item += data.Count<byte>(10); } return (item, num); } 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); } public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTime value) { if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length)) { value = default(DateTime); return false; } if (isEscaped) return TryGetEscapedDateTime(segment, out value); if (JsonHelpers.TryParseAsISO(segment, out DateTime value2)) { value = value2; return true; } value = default(DateTime); return false; } public unsafe static bool TryGetEscapedDateTime(ReadOnlySpan<byte> source, out DateTime value) { Span<byte> span = new Span<byte>(stackalloc byte[252], 252); Unescape(source, span, out int written); span = span.Slice(0, written); if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(span.Length) && JsonHelpers.TryParseAsISO(span, out DateTime value2)) { value = value2; return true; } value = default(DateTime); return false; } public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTimeOffset value) { if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length)) { value = default(DateTimeOffset); return false; } if (isEscaped) return TryGetEscapedDateTimeOffset(segment, out value); if (JsonHelpers.TryParseAsISO(segment, out DateTimeOffset value2)) { value = value2; return true; } value = default(DateTimeOffset); return false; } public unsafe static bool TryGetEscapedDateTimeOffset(ReadOnlySpan<byte> source, out DateTimeOffset value) { Span<byte> span = new Span<byte>(stackalloc byte[252], 252); Unescape(source, span, out int written); span = span.Slice(0, written); if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(span.Length) && JsonHelpers.TryParseAsISO(span, 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); Unescape(source, span, out int written); span = span.Slice(0, written); if (span.Length == 36 && Utf8Parser.TryParse(span, out Guid value2, out int _, 'D')) { value = value2; return true; } value = default(Guid); return false; } public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out Half value) { if (span.Length == 3) { if (span.SequenceEqual(JsonConstants.NaNValue)) { value = Half.NaN; return true; } } else if (span.Length == 8) { if (span.SequenceEqual(JsonConstants.PositiveInfinityValue)) { value = Half.PositiveInfinity; return true; } } else if (span.Length == 9 && span.SequenceEqual(JsonConstants.NegativeInfinityValue)) { value = Half.NegativeInfinity; return true; } value = default(Half); 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, [NotNullWhen(true)] out byte[] bytes) { byte[] array = null; Span<byte> span = (utf8Source.Length > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(utf8Source.Length))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> span2 = span; Unescape(utf8Source, span2, out int written); span2 = span2.Slice(0, written); bool result = TryDecodeBase64InPlace(span2, out bytes); if (array != null) { span2.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> span = (length > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(length))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> span2 = span; Unescape(utf8Source, span2, out int written); span2 = span2.Slice(0, written); string result = TranscodeHelper(span2); if (array != null) { span2.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public unsafe static byte[] GetUnescaped(ReadOnlySpan<byte> utf8Source) { int length = utf8Source.Length; byte[] array = null; Span<byte> span = (length > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(length))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> destination = span; Unescape(utf8Source, destination, out int written); span = destination.Slice(0, written); 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> span = (utf8Source.Length > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(utf8Source.Length))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> span2 = span; Unescape(utf8Source, span2, 0, out int written); span2 = span2.Slice(0, written); bool result = other.SequenceEqual(span2); if (array != null) { span2.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> span = (num > 256) ? ((Span<byte>)(array2 = ArrayPool<byte>.Shared.Rent(num))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> span2 = span; span = ((num > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(num))) : new Span<byte>(stackalloc byte[256], 256)); Span<byte> span3 = span; ref utf8Source.CopyTo(span3); span3 = span3.Slice(0, num); Unescape(span3, span2, 0, out int written); span2 = span2.Slice(0, written); bool result = other.SequenceEqual(span2); if (array2 != null) { span2.Clear(); ArrayPool<byte>.Shared.Return(array2, false); span3.Clear(); ArrayPool<byte>.Shared.Return(array, false); } return result; } public unsafe static bool UnescapeAndCompareBothInputs(ReadOnlySpan<byte> utf8Source1, ReadOnlySpan<byte> utf8Source2) { int idx = utf8Source1.IndexOf<byte>(92); int idx2 = utf8Source2.IndexOf<byte>(92); byte[] array = null; byte[] array2 = null; Span<byte> span = (utf8Source1.Length > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(utf8Source1.Length))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> span2 = span; span = ((utf8Source2.Length > 256) ? ((Span<byte>)(array2 = ArrayPool<byte>.Shared.Rent(utf8Source2.Length))) : new Span<byte>(stackalloc byte[256], 256)); Span<byte> span3 = span; Unescape(utf8Source1, span2, idx, out int written); span2 = span2.Slice(0, written); Unescape(utf8Source2, span3, idx2, out written); span3 = span3.Slice(0, written); bool result = span2.SequenceEqual(span3); if (array != null) { span2.Clear(); ArrayPool<byte>.Shared.Return(array, false); } if (array2 != null) { span3.Clear(); ArrayPool<byte>.Shared.Return(array2, false); } return result; } public static bool TryDecodeBase64InPlace(Span<byte> utf8Unescaped, [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, [NotNullWhen(true)] out byte[] bytes) { byte[] array = null; Span<byte> span = (utf8Unescaped.Length > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(utf8Unescaped.Length))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> bytes2 = span; 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 static string TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped) { try { return s_utf8Encoding.GetString(utf8Unescaped); } catch (DecoderFallbackException innerException) { throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException); } } public static int TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped, Span<char> destination) { try { return s_utf8Encoding.GetChars(utf8Unescaped, destination); } catch (DecoderFallbackException innerException) { throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException); } catch (ArgumentException) { destination.Clear(); throw; } } public static void ValidateUtf8(ReadOnlySpan<byte> utf8Buffer) { if (!Utf8.IsValid(utf8Buffer)) throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(null); } internal static int GetUtf8ByteCount(ReadOnlySpan<char> text) { try { return s_utf8Encoding.GetByteCount(text); } catch (EncoderFallbackException innerException) { throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(innerException); } } internal static int GetUtf8FromText(ReadOnlySpan<char> text, Span<byte> dest) { try { return s_utf8Encoding.GetBytes(text, dest); } catch (EncoderFallbackException innerException) { throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(innerException); } } internal static string GetTextFromUtf8(ReadOnlySpan<byte> utf8Text) { return s_utf8Encoding.GetString(utf8Text); } internal static void Unescape(ReadOnlySpan<byte> source, Span<byte> destination, out int written) { int idx = source.IndexOf<byte>(92); TryUnescape(source, destination, idx, out written); } internal static void Unescape(ReadOnlySpan<byte> source, Span<byte> destination, int idx, out int written) { 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_0260; case 110: destination[written++] = 10; goto IL_0260; case 114: destination[written++] = 13; goto IL_0260; case 92: destination[written++] = 92; goto IL_0260; case 47: destination[written++] = 47; goto IL_0260; case 116: destination[written++] = 9; goto IL_0260; case 98: destination[written++] = 8; goto IL_0260; case 102: destination[written++] = 12; goto IL_0260; default: { 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(); 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 (!new Rune(value).TryEncodeToUtf8(destination.Slice(written), out int bytesWritten)) break; written += bytesWritten; goto IL_0260; } IL_0260: 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_03dd; } continue; } goto IL_03dd; IL_03dd: return true; } } break; } } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span) { return span.IndexOfAny(s_controlQuoteBackslash); } } }