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

JsonHelpers

static class JsonHelpers
using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Encodings.Web; namespace System.Text.Json { internal static class JsonHelpers { private struct DateTimeParseData { public int Year; public int Month; public int Day; public int Hour; public int Minute; public int Second; public int Fraction; public int OffsetHours; public int OffsetMinutes; public byte OffsetToken; public bool OffsetNegative => OffsetToken == 45; } private static readonly int[] s_daysToMonth365 = new int[13] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; private static readonly int[] s_daysToMonth366 = new int[13] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, [In] [System.Runtime.CompilerServices.IsReadOnly] ref TKey key, [In] [System.Runtime.CompilerServices.IsReadOnly] ref TValue value) { if (!dictionary.ContainsKey(key)) { dictionary[key] = value; return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<byte> GetSpan(ref Utf8JsonReader reader) { if (!reader.HasValueSequence) return reader.ValueSpan; ReadOnlySequence<byte> sequence = reader.ValueSequence; return ref sequence.ToArray(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidUnicodeScalar(uint value) { return IsInRangeInclusive(value ^ 55296, 2048, 1114111); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInRangeInclusive(uint value, uint lowerBound, uint upperBound) { return value - lowerBound <= upperBound - lowerBound; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound) { return (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInRangeInclusive(long value, long lowerBound, long upperBound) { return (ulong)(value - lowerBound) <= (ulong)(upperBound - lowerBound); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInRangeInclusive(JsonTokenType value, JsonTokenType lowerBound, JsonTokenType upperBound) { return value - lowerBound <= upperBound - lowerBound; } public static bool IsDigit(byte value) { return (uint)(value - 48) <= 9; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadWithVerify(ref Utf8JsonReader reader) { bool flag = reader.Read(); } public static string Utf8GetString(ReadOnlySpan<byte> bytes) { return Encoding.UTF8.GetString(bytes.ToArray()); } public static Dictionary<TKey, TValue> CreateDictionaryFromCollection<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) { Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(comparer); foreach (KeyValuePair<TKey, TValue> item in collection) { dictionary.Add(item.Key, item.Value); } return dictionary; } public static bool IsFinite(double value) { if (!double.IsNaN(value)) return !double.IsInfinity(value); return false; } public static bool IsFinite(float value) { if (!float.IsNaN(value)) return !float.IsInfinity(value); return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ValidateInt32MaxArrayLength(uint length) { if (length > 2146435071) ThrowHelper.ThrowOutOfMemoryException(length); } public unsafe static string FormatDateTimeOffset(DateTimeOffset value) { Span<byte> span = new Span<byte>(stackalloc byte[33], 33); Span<byte> buffer = span; JsonWriterHelper.WriteDateTimeOffsetTrimmed(buffer, value, out int bytesWritten); return JsonReaderHelper.GetTextFromUtf8(buffer.Slice(0, bytesWritten)); } public unsafe static string FormatDateTime(DateTime value) { Span<byte> span = new Span<byte>(stackalloc byte[33], 33); Span<byte> buffer = span; JsonWriterHelper.WriteDateTimeTrimmed(buffer, value, out int bytesWritten); return JsonReaderHelper.GetTextFromUtf8(buffer.Slice(0, bytesWritten)); } public unsafe static bool TryParseAsISO(ReadOnlySpan<char> source, out DateTime value) { if (!IsValidDateTimeOffsetParseLength(source.Length)) { value = default(DateTime); return false; } int num = checked(source.Length * 3); Span<byte> span2; if (num <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = new byte[num]; Span<byte> span3 = span2; int utf8FromText = JsonReaderHelper.GetUtf8FromText(source, span3); span3 = span3.Slice(0, utf8FromText); if (span3.IndexOf<byte>(92) != -1) return JsonReaderHelper.TryGetEscapedDateTime(span3, out value); if (TryParseAsISO(span3, out DateTime value2)) { value = value2; return true; } value = default(DateTime); return false; } public unsafe static bool TryParseAsISO(ReadOnlySpan<char> source, out DateTimeOffset value) { if (!IsValidDateTimeOffsetParseLength(source.Length)) { value = default(DateTimeOffset); return false; } int num = checked(source.Length * 3); Span<byte> span2; if (num <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = new byte[num]; Span<byte> span3 = span2; int utf8FromText = JsonReaderHelper.GetUtf8FromText(source, span3); span3 = span3.Slice(0, utf8FromText); if (span3.IndexOf<byte>(92) != -1) return JsonReaderHelper.TryGetEscapedDateTimeOffset(span3, out value); if (TryParseAsISO(span3, out DateTimeOffset value2)) { value = value2; return true; } value = default(DateTimeOffset); return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidDateTimeOffsetParseLength(int length) { return IsInRangeInclusive(length, 10, 252); } public static bool TryParseAsISO(ReadOnlySpan<byte> source, out DateTime value) { if (!TryParseDateTimeOffset(source, out DateTimeParseData parseData)) { value = default(DateTime); return false; } if (parseData.OffsetToken == 90) return TryCreateDateTime(parseData, DateTimeKind.Utc, out value); if (parseData.OffsetToken == 43 || parseData.OffsetToken == 45) { if (!TryCreateDateTimeOffset(ref parseData, out DateTimeOffset value2)) { value = default(DateTime); return false; } value = value2.LocalDateTime; return true; } return TryCreateDateTime(parseData, DateTimeKind.Unspecified, out value); } public static bool TryParseAsISO(ReadOnlySpan<byte> source, out DateTimeOffset value) { if (!TryParseDateTimeOffset(source, out DateTimeParseData parseData)) { value = default(DateTimeOffset); return false; } if (parseData.OffsetToken == 90 || parseData.OffsetToken == 43 || parseData.OffsetToken == 45) return TryCreateDateTimeOffset(ref parseData, out value); return TryCreateDateTimeOffsetInterpretingDataAsLocalTime(parseData, out value); } private static bool TryParseDateTimeOffset(ReadOnlySpan<byte> source, out DateTimeParseData parseData) { parseData = default(DateTimeParseData); uint num = (uint)(source[0] - 48); uint num2 = (uint)(source[1] - 48); uint num3 = (uint)(source[2] - 48); uint num4 = (uint)(source[3] - 48); if (num > 9 || num2 > 9 || num3 > 9 || num4 > 9) return false; parseData.Year = (int)(num * 1000 + num2 * 100 + num3 * 10 + num4); if (source[4] != 45 || !TryGetNextTwoDigits(source.Slice(5, 2), ref parseData.Month) || source[7] != 45 || !TryGetNextTwoDigits(source.Slice(8, 2), ref parseData.Day)) return false; if (source.Length == 10) return true; if (source.Length < 16) return false; if (source[10] != 84 || source[13] != 58 || !TryGetNextTwoDigits(source.Slice(11, 2), ref parseData.Hour) || !TryGetNextTwoDigits(source.Slice(14, 2), ref parseData.Minute)) return false; if (source.Length != 16) { byte b = source[16]; int num5 = 17; switch (b) { case 90: parseData.OffsetToken = 90; return num5 == source.Length; case 43: case 45: parseData.OffsetToken = b; return <TryParseDateTimeOffset>g__ParseOffset|22_0(ref parseData, source.Slice(num5)); default: return false; case 58: if (source.Length < 19 || !TryGetNextTwoDigits(source.Slice(17, 2), ref parseData.Second)) return false; if (source.Length != 19) { b = source[19]; num5 = 20; switch (b) { case 90: parseData.OffsetToken = 90; return num5 == source.Length; case 43: case 45: parseData.OffsetToken = b; return <TryParseDateTimeOffset>g__ParseOffset|22_0(ref parseData, source.Slice(num5)); default: return false; case 46: { if (source.Length < 21) return false; int i = 0; for (int num6 = Math.Min(num5 + 16, source.Length); num5 < num6; num5++) { if (!IsDigit(b = source[num5])) break; if (i < 7) { parseData.Fraction = parseData.Fraction * 10 + (b - 48); i++; } } if (parseData.Fraction != 0) { for (; i < 7; i++) { parseData.Fraction *= 10; } } if (num5 != source.Length) { b = source[num5++]; switch (b) { case 90: parseData.OffsetToken = 90; return num5 == source.Length; case 43: case 45: parseData.OffsetToken = b; return <TryParseDateTimeOffset>g__ParseOffset|22_0(ref parseData, source.Slice(num5)); default: return false; } } return true; } } } return true; } } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryGetNextTwoDigits(ReadOnlySpan<byte> source, ref int value) { uint num = (uint)(source[0] - 48); uint num2 = (uint)(source[1] - 48); if (num > 9 || num2 > 9) { value = 0; return false; } value = (int)(num * 10 + num2); return true; } private static bool TryCreateDateTimeOffset(DateTime dateTime, ref DateTimeParseData parseData, out DateTimeOffset value) { if ((uint)parseData.OffsetHours > 14) { value = default(DateTimeOffset); return false; } if ((uint)parseData.OffsetMinutes > 59) { value = default(DateTimeOffset); return false; } if (parseData.OffsetHours == 14 && parseData.OffsetMinutes != 0) { value = default(DateTimeOffset); return false; } long num = ((long)parseData.OffsetHours * 3600 + (long)parseData.OffsetMinutes * 60) * 10000000; if (parseData.OffsetNegative) num = -num; try { value = new DateTimeOffset(dateTime.Ticks, new TimeSpan(num)); } catch (ArgumentOutOfRangeException) { value = default(DateTimeOffset); return false; } return true; } private static bool TryCreateDateTimeOffset(ref DateTimeParseData parseData, out DateTimeOffset value) { if (!TryCreateDateTime(parseData, DateTimeKind.Unspecified, out DateTime value2)) { value = default(DateTimeOffset); return false; } if (!TryCreateDateTimeOffset(value2, ref parseData, out value)) { value = default(DateTimeOffset); return false; } return true; } private static bool TryCreateDateTimeOffsetInterpretingDataAsLocalTime(DateTimeParseData parseData, out DateTimeOffset value) { if (!TryCreateDateTime(parseData, DateTimeKind.Local, out DateTime value2)) { value = default(DateTimeOffset); return false; } try { value = new DateTimeOffset(value2); } catch (ArgumentOutOfRangeException) { value = default(DateTimeOffset); return false; } return true; } private static bool TryCreateDateTime(DateTimeParseData parseData, DateTimeKind kind, out DateTime value) { if (parseData.Year == 0) { value = default(DateTime); return false; } if ((uint)(parseData.Month - 1) >= 12) { value = default(DateTime); return false; } uint num = (uint)(parseData.Day - 1); if (num >= 28 && num >= DateTime.DaysInMonth(parseData.Year, parseData.Month)) { value = default(DateTime); return false; } if ((uint)parseData.Hour > 23) { value = default(DateTime); return false; } if ((uint)parseData.Minute > 59) { value = default(DateTime); return false; } if ((uint)parseData.Second > 59) { value = default(DateTime); return false; } int[] array = DateTime.IsLeapYear(parseData.Year) ? s_daysToMonth366 : s_daysToMonth365; int num2 = parseData.Year - 1; int num3 = num2 * 365 + num2 / 4 - num2 / 100 + num2 / 400 + array[parseData.Month - 1] + parseData.Day - 1; long num4 = num3 * 864000000000; int num5 = parseData.Hour * 3600 + parseData.Minute * 60 + parseData.Second; num4 += (long)num5 * 10000000; num4 += parseData.Fraction; value = new DateTime(num4, kind); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] GetEscapedPropertyNameSection(ReadOnlySpan<byte> utf8Value, JavaScriptEncoder encoder) { int num = JsonWriterHelper.NeedsEscaping(utf8Value, encoder); if (num != -1) return GetEscapedPropertyNameSection(utf8Value, num, encoder); return GetPropertyNameSection(utf8Value); } public unsafe static byte[] EscapeValue(ReadOnlySpan<byte> utf8Value, int firstEscapeIndexVal, JavaScriptEncoder encoder) { byte[] array = null; int maxEscapedLength = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); Span<byte> span; Span<byte> span2; if (maxEscapedLength <= 256) { span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(maxEscapedLength)); Span<byte> destination = span2; JsonWriterHelper.EscapeString(utf8Value, destination, firstEscapeIndexVal, encoder, out int written); span = destination.Slice(0, written); byte[] result = span.ToArray(); if (array != null) ArrayPool<byte>.Shared.Return(array, false); return result; } private unsafe static byte[] GetEscapedPropertyNameSection(ReadOnlySpan<byte> utf8Value, int firstEscapeIndexVal, JavaScriptEncoder encoder) { byte[] array = null; int maxEscapedLength = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal); Span<byte> span2; if (maxEscapedLength <= 256) { Span<byte> span = new Span<byte>(stackalloc byte[256], 256); span2 = span; } else span2 = (array = ArrayPool<byte>.Shared.Rent(maxEscapedLength)); Span<byte> destination = span2; JsonWriterHelper.EscapeString(utf8Value, destination, firstEscapeIndexVal, encoder, out int written); byte[] propertyNameSection = GetPropertyNameSection(destination.Slice(0, written)); if (array != null) ArrayPool<byte>.Shared.Return(array, false); return propertyNameSection; } private static byte[] GetPropertyNameSection(ReadOnlySpan<byte> utf8Value) { int length = utf8Value.Length; byte[] array = new byte[length + 3]; array[0] = 34; utf8Value.CopyTo(array.AsSpan(1, length)); array[++length] = 34; array[++length] = 58; return array; } } }