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