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;
}
}
}