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

JsonHelpers

static class JsonHelpers
using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Encodings.Web; using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace System.Text.Json { internal static class JsonHelpers { [StructLayout(LayoutKind.Auto)] private struct DateTimeParseData { public int Year; public int Month; public int Day; public bool IsCalendarDateOnly; 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; } public static readonly Regex IntegerRegex = CreateIntegerRegex(); private const string IntegerRegexPattern = "^\\s*(?:\\+|\\-)?[0-9]+\\s*$"; private const int IntegerRegexTimeoutMs = 200; private static ReadOnlySpan<int> DaysToMonth365 { get { object obj = global::<PrivateImplementationDetails>.5857EE4CE98BFABBD62B385C1098507DD0052FF3951043AAD6A1DABD495F18AA_A6; if (obj == null) { obj = new int[13] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; global::<PrivateImplementationDetails>.5857EE4CE98BFABBD62B385C1098507DD0052FF3951043AAD6A1DABD495F18AA_A6 = (int[])obj; } return new ReadOnlySpan<int>((int[])obj); } } private static ReadOnlySpan<int> DaysToMonth366 { get { object obj = global::<PrivateImplementationDetails>.FADB218011E7702BB9575D0C32A685DA10B5C72EB809BD9A955DB1C76E4D8315_A6; if (obj == null) { obj = new int[13] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; global::<PrivateImplementationDetails>.FADB218011E7702BB9575D0C32A685DA10B5C72EB809BD9A955DB1C76E4D8315_A6 = (int[])obj; } return new ReadOnlySpan<int>((int[])obj); } } public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value) { if (!dictionary.ContainsKey(key)) { dictionary[key] = value; return true; } return false; } public static bool TryDequeue<T>(this Queue<T> queue, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T result) { if (queue.Count > 0) { result = queue.Dequeue(); return true; } result = default(T); return false; } internal static bool RequiresSpecialNumberHandlingOnWrite(JsonNumberHandling? handling) { if (!handling.HasValue) return false; return (handling.Value & (JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals)) != JsonNumberHandling.Strict; } internal static void StableSortByKey<T, [System.Runtime.CompilerServices.IsUnmanaged] TKey>(this List<T> items, Func<T, TKey> keySelector) where TKey : struct, IComparable<TKey> { T[] array = items.ToArray(); (TKey, int)[] array2 = new(TKey, int)[array.Length]; for (int i = 0; i < array2.Length; i++) { array2[i] = (keySelector(array[i]), i); } Array.Sort(array2, array); items.Clear(); items.AddRange((IEnumerable<T>)array); } public static T[] TraverseGraphWithTopologicalSort<T>(T entryNode, Func<T, ICollection<T>> getChildren, IEqualityComparer<T> comparer = null) { if (comparer == null) comparer = EqualityComparer<T>.Default; List<T> list = new List<T> { entryNode }; Dictionary<T, int> dictionary = new Dictionary<T, int>(comparer) { [entryNode] = 0 }; List<bool[]> list2 = new List<bool[]>(); Queue<int> queue = new Queue<int>(); for (int i = 0; i < list.Count; i++) { T arg = list[i]; ICollection<T> collection = getChildren(arg); int count = collection.Count; if (count == 0) { list2.Add((bool[])null); queue.Enqueue(i); } else { bool[] array = new bool[Math.Max(list.Count, count)]; foreach (T item in (IEnumerable<T>)collection) { if (!dictionary.TryGetValue(item, out int value)) { value = list.Count; dictionary.Add(item, value); list.Add(item); } if (value >= array.Length) Array.Resize(ref array, value + 1); array[value] = true; } list2.Add(array); } } T[] array2 = new T[list.Count]; int num = array2.Length; do { int num2 = queue.Dequeue(); array2[--num] = list[num2]; for (int j = 0; j < list2.Count; j++) { bool[] array3 = list2[j]; if (array3 != null && num2 < array3.Length && array3[num2]) { array3[num2] = false; if (array3.AsSpan().IndexOf(true) == -1) queue.Enqueue(j); } } } while (queue.Count > 0); return array2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<byte> GetUnescapedSpan([System.Runtime.CompilerServices.ScopedRef] ref Utf8JsonReader reader) { ReadOnlySpan<byte> readOnlySpan; if (!reader.HasValueSequence) readOnlySpan = reader.ValueSpan; else { ReadOnlySequence<byte> sequence = reader.ValueSequence; readOnlySpan = ref sequence.ToArray(); } ReadOnlySpan<byte> readOnlySpan2 = readOnlySpan; if (!reader.ValueIsEscaped) return readOnlySpan2; return JsonReaderHelper.GetUnescapedSpan(readOnlySpan2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryAdvanceWithOptionalReadAhead([System.Runtime.CompilerServices.ScopedRef] ref Utf8JsonReader reader, bool requiresReadAhead) { if (!requiresReadAhead || reader.IsFinalBlock) return reader.Read(); return TryAdvanceWithReadAhead(ref reader); } public static bool TryAdvanceToNextRootLevelValueWithOptionalReadAhead([System.Runtime.CompilerServices.ScopedRef] ref Utf8JsonReader reader, bool requiresReadAhead, out bool isAtEndOfStream) { Utf8JsonReader utf8JsonReader = reader; if (!reader.Read()) { isAtEndOfStream = reader.IsFinalBlock; reader = utf8JsonReader; return false; } isAtEndOfStream = false; if (requiresReadAhead && !reader.IsFinalBlock) { reader = utf8JsonReader; return TryAdvanceWithReadAhead(ref reader); } return true; } private static bool TryAdvanceWithReadAhead([System.Runtime.CompilerServices.ScopedRef] ref Utf8JsonReader reader) { Utf8JsonReader utf8JsonReader = reader; if (!reader.Read()) return false; JsonTokenType tokenType = reader.TokenType; if ((tokenType == JsonTokenType.StartObject || tokenType == JsonTokenType.StartArray) ? true : false) { bool num = ref reader.TrySkipPartial(); reader = utf8JsonReader; if (!num) return false; ref reader.ReadWithVerify(); } return true; } [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) { reader.Read(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SkipWithVerify(ref Utf8JsonReader reader) { reader.TrySkipPartial(reader.CurrentDepth); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TrySkipPartial(ref Utf8JsonReader reader) { return reader.TrySkipPartial(reader.CurrentDepth); } public unsafe static string Utf8GetString(ReadOnlySpan<byte> bytes) { if (bytes.Length != 0) { fixed (byte* bytes2 = &bytes.GetPinnableReference()) { return Encoding.UTF8.GetString(bytes2, bytes.Length); } } return string.Empty; } public static bool TryLookupUtf8Key<TValue>(this Dictionary<string, TValue> dictionary, ReadOnlySpan<byte> utf8Key, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TValue result) { string key = Utf8GetString(utf8Key); return dictionary.TryGetValue(key, out result); } 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 static bool HasAllSet(this BitArray bitArray) { for (int i = 0; i < bitArray.Count; i++) { if (!bitArray[i]) return false; } return true; } private static Regex CreateIntegerRegex() { return new Regex("^\\s*(?:\\+|\\-)?[0-9]+\\s*$", RegexOptions.Compiled, TimeSpan.FromMilliseconds(200)); } public static bool AreEqualJsonNumbers(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right) { <AreEqualJsonNumbers>g__ParseNumber|29_0(left, out bool isNegative, out ReadOnlySpan<byte> integral, out ReadOnlySpan<byte> fractional, out int exponent); <AreEqualJsonNumbers>g__ParseNumber|29_0(right, out bool isNegative2, out ReadOnlySpan<byte> integral2, out ReadOnlySpan<byte> fractional2, out int exponent2); if (isNegative != isNegative2 || exponent != exponent2 || integral.Length + fractional.Length != integral2.Length + fractional2.Length) return false; int num = integral.Length - integral2.Length; ReadOnlySpan<byte> span; ReadOnlySpan<byte> span2; ReadOnlySpan<byte> span3; ReadOnlySpan<byte> other; ReadOnlySpan<byte> other2; ReadOnlySpan<byte> other3; if (num >= 0) { if (num == 0) { span = integral; span2 = default(ReadOnlySpan<byte>); span3 = fractional; other = integral2; other2 = default(ReadOnlySpan<byte>); other3 = fractional2; } else { int num2 = integral.Length - num; span = integral.Slice(0, num2); span2 = integral.Slice(num2); span3 = fractional; other = integral2; other2 = fractional2.Slice(0, num); other3 = fractional2.Slice(num); } } else { span = integral; span2 = fractional.Slice(0, -num); span3 = fractional.Slice(-num); int num3 = integral2.Length + num; other = integral2.Slice(0, num3); other2 = integral2.Slice(num3); other3 = fractional2; } if (span.SequenceEqual(other) && span2.SequenceEqual(other2)) return span3.SequenceEqual(other3); return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidDateTimeOffsetParseLength(int length) { return IsInRangeInclusive(length, 10, 252); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidUnescapedDateTimeOffsetParseLength(int length) { return IsInRangeInclusive(length, 10, 42); } 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) { parseData.IsCalendarDateOnly = true; 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|35_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|35_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|35_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; } ReadOnlySpan<int> readOnlySpan = DateTime.IsLeapYear(parseData.Year) ? DaysToMonth366 : DaysToMonth365; int num2 = parseData.Year - 1; long num3 = (num2 * 365 + num2 / 4 - num2 / 100 + num2 / 400 + readOnlySpan[parseData.Month - 1] + parseData.Day - 1) * 864000000000; int num4 = parseData.Hour * 3600 + parseData.Minute * 60 + parseData.Second; num3 += (long)num4 * 10000000; num3 += parseData.Fraction; value = new DateTime(num3, 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 = (maxEscapedLength > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(maxEscapedLength))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> destination = span; 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> span = (maxEscapedLength > 256) ? ((Span<byte>)(array = ArrayPool<byte>.Shared.Rent(maxEscapedLength))) : new Span<byte>(stackalloc byte[256], 256); Span<byte> destination = span; 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; } } }