JsonReaderHelper
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Unicode;
namespace System.Text.Json
{
internal static class JsonReaderHelper
{
private static readonly SearchValues<char> s_specialCharacters = SearchValues.Create(". '/\"[]()\t\n\r\b\\
".AsSpan());
public static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(false, true);
private static readonly SearchValues<byte> s_controlQuoteBackslash = SearchValues.Create(new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.AF588FFF8363545AB79B72C6BC89DA690C48DC7F5ED52015A88E121C552F86E1, 34));
public static bool ContainsSpecialCharacters(this ReadOnlySpan<char> text)
{
return text.ContainsAny(s_specialCharacters);
}
public static (int, int) CountNewLines(ReadOnlySpan<byte> data)
{
int num = data.LastIndexOf<byte>(10);
int item = 0;
if (num >= 0) {
item = 1;
data = data.Slice(0, num);
item += data.Count<byte>(10);
}
return (item, 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 static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTime value)
{
if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length)) {
value = default(DateTime);
return false;
}
if (isEscaped)
return TryGetEscapedDateTime(segment, out value);
if (JsonHelpers.TryParseAsISO(segment, out DateTime value2)) {
value = value2;
return true;
}
value = default(DateTime);
return false;
}
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 static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTimeOffset value)
{
if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length)) {
value = default(DateTimeOffset);
return false;
}
if (isEscaped)
return TryGetEscapedDateTimeOffset(segment, out value);
if (JsonHelpers.TryParseAsISO(segment, out DateTimeOffset value2)) {
value = value2;
return true;
}
value = default(DateTimeOffset);
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 Half value)
{
if (span.Length == 3) {
if (span.SequenceEqual(JsonConstants.NaNValue)) {
value = Half.NaN;
return true;
}
} else if (span.Length == 8) {
if (span.SequenceEqual(JsonConstants.PositiveInfinityValue)) {
value = Half.PositiveInfinity;
return true;
}
} else if (span.Length == 9 && span.SequenceEqual(JsonConstants.NegativeInfinityValue)) {
value = Half.NegativeInfinity;
return true;
}
value = default(Half);
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, [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 byte[] GetUnescaped(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);
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, [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, [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 static string TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped)
{
try {
return s_utf8Encoding.GetString(utf8Unescaped);
} catch (DecoderFallbackException innerException) {
throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException);
}
}
public static int TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped, Span<char> destination)
{
try {
return s_utf8Encoding.GetChars(utf8Unescaped, destination);
} catch (DecoderFallbackException innerException) {
throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(innerException);
} catch (ArgumentException) {
destination.Clear();
throw;
}
}
public static void ValidateUtf8(ReadOnlySpan<byte> utf8Buffer)
{
if (!Utf8.IsValid(utf8Buffer))
throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(null);
}
internal static int GetUtf8ByteCount(ReadOnlySpan<char> text)
{
try {
return s_utf8Encoding.GetByteCount(text);
} catch (EncoderFallbackException innerException) {
throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(innerException);
}
}
internal static int GetUtf8FromText(ReadOnlySpan<char> text, Span<byte> dest)
{
try {
return s_utf8Encoding.GetBytes(text, dest);
} catch (EncoderFallbackException innerException) {
throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(innerException);
}
}
internal static string GetTextFromUtf8(ReadOnlySpan<byte> utf8Text)
{
return s_utf8Encoding.GetString(utf8Text);
}
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_0260;
case 110:
destination[written++] = 10;
goto IL_0260;
case 114:
destination[written++] = 13;
goto IL_0260;
case 92:
destination[written++] = 92;
goto IL_0260;
case 47:
destination[written++] = 47;
goto IL_0260;
case 116:
destination[written++] = 9;
goto IL_0260;
case 98:
destination[written++] = 8;
goto IL_0260;
case 102:
destination[written++] = 12;
goto IL_0260;
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 (!new Rune(value).TryEncodeToUtf8(destination.Slice(written), out int bytesWritten))
break;
written += bytesWritten;
goto IL_0260;
}
IL_0260:
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_03dd;
}
continue;
}
goto IL_03dd;
IL_03dd:
return true;
}
}
break;
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span)
{
return span.IndexOfAny(s_controlQuoteBackslash);
}
}
}