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 string SpecialCharacters = ". '/\"[]()\t\n\r\b\\
";
public static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(false, true);
private const ulong XorPowerOfTwoToHighByte = 283686952306184;
public static bool ContainsSpecialCharacters(this ReadOnlySpan<char> text)
{
return text.IndexOfAny(". '/\"[]()\t\n\r\b\\
".AsSpan()) >= 0;
}
public static (int, int) CountNewLines(ReadOnlySpan<byte> data)
{
int num = data.LastIndexOf<byte>(10);
int num2 = 0;
if (num >= 0) {
num2 = 1;
data = data.Slice(0, num);
int num3;
while ((num3 = data.IndexOf<byte>(10)) >= 0) {
num2++;
data = data.Slice(num3 + 1);
}
}
return (num2, 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 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 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 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> 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 ReadOnlySpan<byte> GetUnescapedSpan(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);
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> 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, [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 = (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 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);
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_0256;
case 110:
destination[written++] = 10;
goto IL_0256;
case 114:
destination[written++] = 13;
goto IL_0256;
case 92:
destination[written++] = 92;
goto IL_0256;
case 47:
destination[written++] = 47;
goto IL_0256;
case 116:
destination[written++] = 9;
goto IL_0256;
case 98:
destination[written++] = 8;
goto IL_0256;
case 102:
destination[written++] = 12;
goto IL_0256;
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 (!TryEncodeToUtf8Bytes((uint)value, destination.Slice(written), out int bytesWritten))
break;
written += bytesWritten;
goto IL_0256;
}
IL_0256:
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_03d3;
}
continue;
}
goto IL_03d3;
IL_03d3:
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;
}
public unsafe static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span)
{
ref byte reference = ref MemoryMarshal.GetReference(span);
int length = span.Length;
IntPtr intPtr = (IntPtr)0;
IntPtr intPtr2 = (IntPtr)length;
if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2) {
int num = (int)Unsafe.AsPointer(ref reference) & (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 reference, intPtr);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03b2;
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 1);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03ba;
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 2);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03c8;
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 3);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03d6;
intPtr += 4;
}
while ((void*)intPtr2 != null) {
intPtr2 -= 1;
num2 = Unsafe.AddByteOffset(ref reference, intPtr);
if (34 != num2 && 92 != num2 && 32 <= num2) {
intPtr += 1;
continue;
}
goto IL_03b2;
}
if (Vector.IsHardwareAccelerated && (int)(void*)intPtr < length) {
intPtr2 = (IntPtr)((length - (int)(void*)intPtr) & ~(Vector<byte>.Count - 1));
Vector<byte> right = new Vector<byte>(34);
Vector<byte> right2 = new Vector<byte>(92);
Vector<byte> right3 = new Vector<byte>(32);
while ((void*)intPtr2 > (void*)intPtr) {
Vector<byte> left = Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref reference, 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 reference, intPtr);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03b2;
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 1);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03ba;
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 2);
if (34 == num2 || 92 == num2 || 32 > num2)
goto IL_03c8;
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 3);
if (34 != num2 && 92 != num2 && 32 <= num2) {
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 4);
if (34 == num2 || 92 == num2 || 32 > num2)
return (int)(void*)(intPtr + 4);
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 5);
if (34 == num2 || 92 == num2 || 32 > num2)
return (int)(void*)(intPtr + 5);
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 6);
if (34 == num2 || 92 == num2 || 32 > num2)
return (int)(void*)(intPtr + 6);
num2 = Unsafe.AddByteOffset(ref reference, intPtr + 7);
if (34 == num2 || 92 == num2 || 32 > num2)
break;
intPtr += 8;
continue;
}
goto IL_03d6;
IL_03b2:
return (int)(void*)intPtr;
IL_03ba:
return (int)(void*)(intPtr + 1);
IL_03d6:
return (int)(void*)(intPtr + 3);
IL_03c8:
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)
{
return (int)((match ^ (match - 1)) * 283686952306184 >> 57);
}
}
}