< Summary

Line coverage
7%
Covered lines: 41
Uncovered lines: 536
Coverable lines: 577
Total lines: 992
Line coverage: 7.1%
Branch coverage
2%
Covered branches: 5
Total branches: 211
Branch coverage: 2.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .cctor()100%11100%
File 1: ContainsSpecialCharacters(...)100%110%
File 1: AppendEscapedPropertyName(...)0%10100%
File 1: AppendEscapedPropertyName(...)0%10100%
File 1: CountNewLines(...)100%22100%
File 1: ToValueKind(...)0%13130%
File 1: IsTokenTypePrimitive(...)100%110%
File 1: IsHexDigit(...)100%110%
File 1: TryGetValue(...)0%660%
File 1: TryGetEscapedDateTime(...)0%440%
File 1: TryGetValue(...)0%660%
File 1: TryGetEscapedDateTimeOffset(...)0%440%
File 1: TryGetValue(...)0%880%
File 1: TryGetEscapedGuid(...)0%440%
File 1: TryGetFloatingPointConstant(...)0%12120%
File 1: TryGetFloatingPointConstant(...)0%12120%
File 1: TryGetFloatingPointConstant(...)0%12120%
File 2: .cctor()100%11100%
File 2: IndexOfQuoteOrAnyControlOrBackSlash(...)100%11100%
File 2: IndexOfFirstNonWhiteSpace(...)100%22100%
File 3: TryGetUnescapedBase64Bytes(...)0%440%
File 3: .cctor()100%11100%
File 3: GetUnescapedString(...)0%440%
File 3: GetUnescaped(...)0%440%
File 3: UnescapeAndCompare(...)0%660%
File 3: UnescapeAndCompare(...)0%880%
File 3: UnescapeAndCompareBothInputs(...)0%880%
File 3: TryDecodeBase64InPlace(...)0%220%
File 3: TryDecodeBase64(...)0%880%
File 3: TranscodeHelper(...)100%110%
File 3: TranscodeHelper(...)100%110%
File 3: ValidateUtf8(...)50%2260%
File 3: GetUtf8ByteCount(...)100%1157.14%
File 3: GetUtf8FromText(...)100%1157.14%
File 3: GetTextFromUtf8(...)100%11100%
File 3: Unescape(...)100%110%
File 3: Unescape(...)0%220%
File 3: TryUnescape(...)100%110%
File 3: TryUnescape(...)0%58580%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Buffers.Text;
 6using System.Diagnostics;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9using System.Text;
 10
 11namespace System.Text.Json
 12{
 13    internal static partial class JsonReaderHelper
 14    {
 15        // Characters that require the bracketed property name syntax in JSON Path.
 116        private static readonly SearchValues<char> s_specialCharacters = SearchValues.Create("$. '/\"[]()\t\n\r\f\b\\\u0
 17
 18        // Characters that need to be escaped in the single-quoted bracket notation.
 119        private static readonly SearchValues<char> s_charactersToEscape = SearchValues.Create("'\\");
 20
 21        public static bool ContainsSpecialCharacters(this ReadOnlySpan<char> text) =>
 022            text.ContainsAny(s_specialCharacters);
 23
 24        /// <summary>
 25        /// Appends a property name escaped for use in JSON Path single-quoted bracket notation.
 26        /// Escapes single quotes as \' and backslashes as \\.
 27        /// </summary>
 28        public static void AppendEscapedPropertyName(this ref ValueStringBuilder builder, string propertyName)
 029        {
 030            ReadOnlySpan<char> span = propertyName.AsSpan();
 31
 032            int i = span.IndexOfAny(s_charactersToEscape);
 33
 34            // Fast path: if no characters need escaping, append directly.
 035            if (i < 0)
 036            {
 037                builder.Append(propertyName);
 038                return;
 39            }
 40
 41            // Append the portion before the first character needing escaping.
 042            if (i > 0)
 043            {
 044                builder.Append(span.Slice(0, i));
 045            }
 46
 47            // Escape characters from position i onward.
 048            for (; i < span.Length; i++)
 049            {
 050                char c = span[i];
 051                if (c is '\\' or '\'')
 052                {
 053                    builder.Append('\\');
 054                }
 55
 056                builder.Append(c);
 057            }
 058        }
 59
 60        /// <summary>
 61        /// Appends a property name escaped for use in JSON Path single-quoted bracket notation.
 62        /// Escapes single quotes as \' and backslashes as \\.
 63        /// </summary>
 64        public static void AppendEscapedPropertyName(this StringBuilder builder, string propertyName)
 065        {
 066            ReadOnlySpan<char> span = propertyName.AsSpan();
 67
 068            int i = span.IndexOfAny(s_charactersToEscape);
 69
 70            // Fast path: if no characters need escaping, append directly.
 071            if (i < 0)
 072            {
 073                builder.Append(propertyName);
 074                return;
 75            }
 76
 77            // Append the portion before the first character needing escaping.
 078            if (i > 0)
 079            {
 080                builder.Append(span.Slice(0, i));
 081            }
 82
 83            // Escape characters from position i onward.
 084            for (; i < span.Length; i++)
 085            {
 086                char c = span[i];
 087                if (c is '\\' or '\'')
 088                {
 089                    builder.Append('\\');
 090                }
 91
 092                builder.Append(c);
 093            }
 094        }
 95
 96        public static (int, int) CountNewLines(ReadOnlySpan<byte> data)
 33697        {
 33698            int lastLineFeedIndex = data.LastIndexOf(JsonConstants.LineFeed);
 33699            int newLines = 0;
 100
 336101            if (lastLineFeedIndex >= 0)
 84102            {
 84103                newLines = 1;
 84104                data = data.Slice(0, lastLineFeedIndex);
 105#if NET
 84106                newLines += data.Count(JsonConstants.LineFeed);
 107#else
 108                int pos;
 109                while ((pos = data.IndexOf(JsonConstants.LineFeed)) >= 0)
 110                {
 111                    newLines++;
 112                    data = data.Slice(pos + 1);
 113                }
 114#endif
 84115            }
 116
 336117            return (newLines, lastLineFeedIndex);
 336118        }
 119
 120        internal static JsonValueKind ToValueKind(this JsonTokenType tokenType)
 0121        {
 0122            switch (tokenType)
 123            {
 124                case JsonTokenType.None:
 0125                    return JsonValueKind.Undefined;
 126                case JsonTokenType.StartArray:
 0127                    return JsonValueKind.Array;
 128                case JsonTokenType.StartObject:
 0129                    return JsonValueKind.Object;
 130                case JsonTokenType.String:
 131                case JsonTokenType.Number:
 132                case JsonTokenType.True:
 133                case JsonTokenType.False:
 134                case JsonTokenType.Null:
 135                    // This is the offset between the set of literals within JsonValueType and JsonTokenType
 136                    // Essentially: JsonTokenType.Null - JsonValueType.Null
 0137                    return (JsonValueKind)((byte)tokenType - 4);
 138                default:
 0139                    Debug.Fail($"No mapping for token type {tokenType}");
 140                    return JsonValueKind.Undefined;
 141            }
 0142        }
 143
 144        // Returns true if the TokenType is a primitive "value", i.e. String, Number, True, False, and Null
 145        // Otherwise, return false.
 146        public static bool IsTokenTypePrimitive(JsonTokenType tokenType) =>
 0147            (tokenType - JsonTokenType.String) <= (JsonTokenType.Null - JsonTokenType.String);
 148
 149        // A hex digit is valid if it is in the range: [0..9] | [A..F] | [a..f]
 150        // Otherwise, return false.
 0151        public static bool IsHexDigit(byte nextByte) => HexConverter.IsHexChar(nextByte);
 152
 153        public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTime value)
 0154        {
 0155            if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length))
 0156            {
 0157                value = default;
 0158                return false;
 159            }
 160
 161            // Segment needs to be unescaped
 0162            if (isEscaped)
 0163            {
 0164                return TryGetEscapedDateTime(segment, out value);
 165            }
 166
 0167            Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);
 168
 0169            if (JsonHelpers.TryParseAsISO(segment, out DateTime tmp))
 0170            {
 0171                value = tmp;
 0172                return true;
 173            }
 174
 0175            value = default;
 0176            return false;
 0177        }
 178
 179        public static unsafe bool TryGetEscapedDateTime(ReadOnlySpan<byte> source, out DateTime value)
 0180        {
 0181            Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
 0182            Span<byte> sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
 183
 0184            Unescape(source, sourceUnescaped, out int written);
 0185            Debug.Assert(written > 0);
 186
 0187            sourceUnescaped = sourceUnescaped.Slice(0, written);
 0188            Debug.Assert(!sourceUnescaped.IsEmpty);
 189
 0190            if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(sourceUnescaped.Length)
 0191                && JsonHelpers.TryParseAsISO(sourceUnescaped, out DateTime tmp))
 0192            {
 0193                value = tmp;
 0194                return true;
 195            }
 196
 0197            value = default;
 0198            return false;
 0199        }
 200
 201        public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTimeOffset value)
 0202        {
 0203            if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length))
 0204            {
 0205                value = default;
 0206                return false;
 207            }
 208
 209            // Segment needs to be unescaped
 0210            if (isEscaped)
 0211            {
 0212                return TryGetEscapedDateTimeOffset(segment, out value);
 213            }
 214
 0215            Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);
 216
 0217            if (JsonHelpers.TryParseAsISO(segment, out DateTimeOffset tmp))
 0218            {
 0219                value = tmp;
 0220                return true;
 221            }
 222
 0223            value = default;
 0224            return false;
 0225        }
 226
 227        public static unsafe bool TryGetEscapedDateTimeOffset(ReadOnlySpan<byte> source, out DateTimeOffset value)
 0228        {
 0229            Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
 0230            Span<byte> sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
 231
 0232            Unescape(source, sourceUnescaped, out int written);
 0233            Debug.Assert(written > 0);
 234
 0235            sourceUnescaped = sourceUnescaped.Slice(0, written);
 0236            Debug.Assert(!sourceUnescaped.IsEmpty);
 237
 0238            if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(sourceUnescaped.Length)
 0239                && JsonHelpers.TryParseAsISO(sourceUnescaped, out DateTimeOffset tmp))
 0240            {
 0241                value = tmp;
 0242                return true;
 243            }
 244
 0245            value = default;
 0246            return false;
 0247        }
 248
 249        public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out Guid value)
 0250        {
 0251            if (segment.Length > JsonConstants.MaximumEscapedGuidLength)
 0252            {
 0253                value = default;
 0254                return false;
 255            }
 256
 257            // Segment needs to be unescaped
 0258            if (isEscaped)
 0259            {
 0260                return TryGetEscapedGuid(segment, out value);
 261            }
 262
 0263            Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);
 264
 0265            if (segment.Length == JsonConstants.MaximumFormatGuidLength
 0266                && Utf8Parser.TryParse(segment, out Guid tmp, out _, 'D'))
 0267            {
 0268                value = tmp;
 0269                return true;
 270            }
 271
 0272            value = default;
 0273            return false;
 0274        }
 275
 276        public static unsafe bool TryGetEscapedGuid(ReadOnlySpan<byte> source, out Guid value)
 0277        {
 0278            Debug.Assert(source.Length <= JsonConstants.MaximumEscapedGuidLength);
 279
 0280            Span<byte> utf8Unescaped = stackalloc byte[JsonConstants.MaximumEscapedGuidLength];
 0281            Unescape(source, utf8Unescaped, out int written);
 0282            Debug.Assert(written > 0);
 283
 0284            utf8Unescaped = utf8Unescaped.Slice(0, written);
 0285            Debug.Assert(!utf8Unescaped.IsEmpty);
 286
 0287            if (utf8Unescaped.Length == JsonConstants.MaximumFormatGuidLength
 0288                && Utf8Parser.TryParse(utf8Unescaped, out Guid tmp, out _, 'D'))
 0289            {
 0290                value = tmp;
 0291                return true;
 292            }
 293
 0294            value = default;
 0295            return false;
 0296        }
 297
 298#if NET
 299        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out Half value)
 0300        {
 0301            if (span.Length == 3)
 0302            {
 0303                if (span.SequenceEqual(JsonConstants.NaNValue))
 0304                {
 0305                    value = Half.NaN;
 0306                    return true;
 307                }
 0308            }
 0309            else if (span.Length == 8)
 0310            {
 0311                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0312                {
 0313                    value = Half.PositiveInfinity;
 0314                    return true;
 315                }
 0316            }
 0317            else if (span.Length == 9)
 0318            {
 0319                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0320                {
 0321                    value = Half.NegativeInfinity;
 0322                    return true;
 323                }
 0324            }
 325
 0326            value = default;
 0327            return false;
 0328        }
 329#endif
 330
 331        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out float value)
 0332        {
 0333            if (span.Length == 3)
 0334            {
 0335                if (span.SequenceEqual(JsonConstants.NaNValue))
 0336                {
 0337                    value = float.NaN;
 0338                    return true;
 339                }
 0340            }
 0341            else if (span.Length == 8)
 0342            {
 0343                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0344                {
 0345                    value = float.PositiveInfinity;
 0346                    return true;
 347                }
 0348            }
 0349            else if (span.Length == 9)
 0350            {
 0351                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0352                {
 0353                    value = float.NegativeInfinity;
 0354                    return true;
 355                }
 0356            }
 357
 0358            value = 0;
 0359            return false;
 0360        }
 361
 362        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out double value)
 0363        {
 0364            if (span.Length == 3)
 0365            {
 0366                if (span.SequenceEqual(JsonConstants.NaNValue))
 0367                {
 0368                    value = double.NaN;
 0369                    return true;
 370                }
 0371            }
 0372            else if (span.Length == 8)
 0373            {
 0374                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0375                {
 0376                    value = double.PositiveInfinity;
 0377                    return true;
 378                }
 0379            }
 0380            else if (span.Length == 9)
 0381            {
 0382                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0383                {
 0384                    value = double.NegativeInfinity;
 0385                    return true;
 386                }
 0387            }
 388
 0389            value = 0;
 0390            return false;
 0391        }
 392    }
 393}
 394

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.net8.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Runtime.CompilerServices;
 6
 7namespace System.Text.Json
 8{
 9    internal static partial class JsonReaderHelper
 10    {
 11        /// <summary>'"', '\',  or any control characters (i.e. 0 to 31).</summary>
 12        /// <remarks>https://tools.ietf.org/html/rfc8259</remarks>
 113        private static readonly SearchValues<byte> s_controlQuoteBackslash = SearchValues.Create(
 114            // Any Control, < 32 (' ')
 115            "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u001
 116            // Quote
 117            "\""u8 +
 118            // Backslash
 119            "\\"u8);
 20
 21        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22        public static int IndexOfQuoteOrAnyControlOrBackSlash(this ReadOnlySpan<byte> span) =>
 92423            span.IndexOfAny(s_controlQuoteBackslash);
 24
 25        /// <summary>JSON insignificant whitespace: space (0x20), tab (0x09), CR (0x0D), and LF (0x0A).</summary>
 26        /// <remarks>https://tools.ietf.org/html/rfc8259#section-2</remarks>
 127        private static readonly SearchValues<byte> s_whiteSpace = SearchValues.Create(" \t\r\n"u8);
 28
 29        /// <summary>
 30        /// Returns the index of the first byte that is not JSON insignificant whitespace,
 31        /// or the length of the span if every byte is whitespace.
 32        /// </summary>
 33        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 34        public static int IndexOfFirstNonWhiteSpace(this ReadOnlySpan<byte> span)
 369635        {
 369636            int index = span.IndexOfAnyExcept(s_whiteSpace);
 369637            return index < 0 ? span.Length : index;
 369638        }
 39    }
 40}
 41

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderHelper.Unescaping.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Buffers.Text;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Text.Unicode;
 9
 10namespace System.Text.Json
 11{
 12    internal static partial class JsonReaderHelper
 13    {
 14        public static unsafe bool TryGetUnescapedBase64Bytes(ReadOnlySpan<byte> utf8Source, [NotNullWhen(true)] out byte
 015        {
 016            byte[]? unescapedArray = null;
 17
 018            Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocByteThreshold ?
 019                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 020                (unescapedArray = ArrayPool<byte>.Shared.Rent(utf8Source.Length));
 21
 022            Unescape(utf8Source, utf8Unescaped, out int written);
 023            Debug.Assert(written > 0);
 24
 025            utf8Unescaped = utf8Unescaped.Slice(0, written);
 026            Debug.Assert(!utf8Unescaped.IsEmpty);
 27
 028            bool result = TryDecodeBase64InPlace(utf8Unescaped, out bytes!);
 29
 030            if (unescapedArray is not null)
 031            {
 032                utf8Unescaped.Clear();
 033                ArrayPool<byte>.Shared.Return(unescapedArray);
 034            }
 035            return result;
 036        }
 37
 38        // Reject any invalid UTF-8 data rather than silently replacing.
 139        public static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, th
 40
 41        // TODO: Similar to escaping, replace the unescaping logic with publicly shipping APIs from https://github.com/d
 42        public static unsafe string GetUnescapedString(ReadOnlySpan<byte> utf8Source)
 043        {
 44            // The escaped name is always >= than the unescaped, so it is safe to use escaped name for the buffer length
 045            int length = utf8Source.Length;
 046            byte[]? pooledName = null;
 47
 048            Span<byte> utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ?
 049                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 050                (pooledName = ArrayPool<byte>.Shared.Rent(length));
 51
 052            Unescape(utf8Source, utf8Unescaped, out int written);
 053            Debug.Assert(written > 0);
 54
 055            utf8Unescaped = utf8Unescaped.Slice(0, written);
 056            Debug.Assert(!utf8Unescaped.IsEmpty);
 57
 058            string utf8String = TranscodeHelper(utf8Unescaped);
 59
 060            if (pooledName is not null)
 061            {
 062                utf8Unescaped.Clear();
 063                ArrayPool<byte>.Shared.Return(pooledName);
 064            }
 65
 066            return utf8String;
 067        }
 68
 69        public static unsafe byte[] GetUnescaped(ReadOnlySpan<byte> utf8Source)
 070        {
 71            // The escaped name is always >= than the unescaped, so it is safe to use escaped name for the buffer length
 072            int length = utf8Source.Length;
 073            byte[]? pooledName = null;
 74
 075            Span<byte> utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ?
 076                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 077                (pooledName = ArrayPool<byte>.Shared.Rent(length));
 78
 079            Unescape(utf8Source, utf8Unescaped, out int written);
 080            Debug.Assert(written > 0);
 81
 082            byte[] propertyName = utf8Unescaped.Slice(0, written).ToArray();
 083            Debug.Assert(propertyName.Length is not 0);
 84
 085            if (pooledName is not null)
 086            {
 087                new Span<byte>(pooledName, 0, written).Clear();
 088                ArrayPool<byte>.Shared.Return(pooledName);
 089            }
 90
 091            return propertyName;
 092        }
 93
 94        public static unsafe bool UnescapeAndCompare(ReadOnlySpan<byte> utf8Source, ReadOnlySpan<byte> other)
 095        {
 096            Debug.Assert(utf8Source.Length >= other.Length && utf8Source.Length / JsonConstants.MaxExpansionFactorWhileE
 97
 098            byte[]? unescapedArray = null;
 99
 0100            Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocByteThreshold ?
 0101                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0102                (unescapedArray = ArrayPool<byte>.Shared.Rent(utf8Source.Length));
 103
 0104            Unescape(utf8Source, utf8Unescaped, 0, out int written);
 0105            Debug.Assert(written > 0);
 106
 0107            utf8Unescaped = utf8Unescaped.Slice(0, written);
 0108            Debug.Assert(!utf8Unescaped.IsEmpty);
 109
 0110            bool result = other.SequenceEqual(utf8Unescaped);
 111
 0112            if (unescapedArray is not null)
 0113            {
 0114                utf8Unescaped.Clear();
 0115                ArrayPool<byte>.Shared.Return(unescapedArray);
 0116            }
 117
 0118            return result;
 0119        }
 120
 121        public static unsafe bool UnescapeAndCompare(ReadOnlySequence<byte> utf8Source, ReadOnlySpan<byte> other)
 0122        {
 0123            Debug.Assert(!utf8Source.IsSingleSegment);
 0124            Debug.Assert(utf8Source.Length >= other.Length && utf8Source.Length / JsonConstants.MaxExpansionFactorWhileE
 125
 0126            byte[]? escapedArray = null;
 0127            byte[]? unescapedArray = null;
 128
 0129            int length = checked((int)utf8Source.Length);
 130
 0131            Span<byte> utf8Unescaped = length <= JsonConstants.StackallocByteThreshold ?
 0132                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0133                (unescapedArray = ArrayPool<byte>.Shared.Rent(length));
 134
 0135            Span<byte> utf8Escaped = length <= JsonConstants.StackallocByteThreshold ?
 0136                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0137                (escapedArray = ArrayPool<byte>.Shared.Rent(length));
 138
 0139            utf8Source.CopyTo(utf8Escaped);
 0140            utf8Escaped = utf8Escaped.Slice(0, length);
 141
 0142            Unescape(utf8Escaped, utf8Unescaped, 0, out int written);
 0143            Debug.Assert(written > 0);
 144
 0145            utf8Unescaped = utf8Unescaped.Slice(0, written);
 0146            Debug.Assert(!utf8Unescaped.IsEmpty);
 147
 0148            bool result = other.SequenceEqual(utf8Unescaped);
 149
 0150            if (unescapedArray is not null)
 0151            {
 0152                Debug.Assert(escapedArray is not null);
 0153                utf8Unescaped.Clear();
 0154                ArrayPool<byte>.Shared.Return(unescapedArray);
 0155                utf8Escaped.Clear();
 0156                ArrayPool<byte>.Shared.Return(escapedArray);
 0157            }
 158
 0159            return result;
 0160        }
 161
 162        public static unsafe bool UnescapeAndCompareBothInputs(ReadOnlySpan<byte> utf8Source1, ReadOnlySpan<byte> utf8So
 0163        {
 0164            int index1 = utf8Source1.IndexOf(JsonConstants.BackSlash);
 0165            int index2 = utf8Source2.IndexOf(JsonConstants.BackSlash);
 166
 0167            Debug.Assert(index1 >= 0, "the first parameter is not escaped");
 0168            Debug.Assert(index2 >= 0, "the second parameter is not escaped");
 169
 0170            byte[]? unescapedArray1 = null;
 0171            byte[]? unescapedArray2 = null;
 172
 0173            Span<byte> utf8Unescaped1 = utf8Source1.Length <= JsonConstants.StackallocByteThreshold ?
 0174                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0175                (unescapedArray1 = ArrayPool<byte>.Shared.Rent(utf8Source1.Length));
 176
 0177            Span<byte> utf8Unescaped2 = utf8Source2.Length <= JsonConstants.StackallocByteThreshold ?
 0178                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0179                (unescapedArray2 = ArrayPool<byte>.Shared.Rent(utf8Source2.Length));
 180
 0181            Unescape(utf8Source1, utf8Unescaped1, index1, out int written);
 0182            utf8Unescaped1 = utf8Unescaped1.Slice(0, written);
 0183            Debug.Assert(!utf8Unescaped1.IsEmpty);
 184
 0185            Unescape(utf8Source2, utf8Unescaped2, index2, out written);
 0186            utf8Unescaped2 = utf8Unescaped2.Slice(0, written);
 0187            Debug.Assert(!utf8Unescaped2.IsEmpty);
 188
 0189            bool result = utf8Unescaped1.SequenceEqual(utf8Unescaped2);
 190
 0191            if (unescapedArray1 is not null)
 0192            {
 0193                utf8Unescaped1.Clear();
 0194                ArrayPool<byte>.Shared.Return(unescapedArray1);
 0195            }
 196
 0197            if (unescapedArray2 is not null)
 0198            {
 0199                utf8Unescaped2.Clear();
 0200                ArrayPool<byte>.Shared.Return(unescapedArray2);
 0201            }
 202
 0203            return result;
 0204        }
 205
 206        public static bool TryDecodeBase64InPlace(Span<byte> utf8Unescaped, [NotNullWhen(true)] out byte[]? bytes)
 0207        {
 0208            OperationStatus status = Base64.DecodeFromUtf8InPlace(utf8Unescaped, out int bytesWritten);
 0209            if (status != OperationStatus.Done)
 0210            {
 0211                bytes = null;
 0212                return false;
 213            }
 0214            bytes = utf8Unescaped.Slice(0, bytesWritten).ToArray();
 0215            return true;
 0216        }
 217
 218        public static unsafe bool TryDecodeBase64(ReadOnlySpan<byte> utf8Unescaped, [NotNullWhen(true)] out byte[]? byte
 0219        {
 0220            byte[]? pooledArray = null;
 221
 0222            Span<byte> byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocByteThreshold ?
 0223                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0224                (pooledArray = ArrayPool<byte>.Shared.Rent(utf8Unescaped.Length));
 225
 0226            OperationStatus status = Base64.DecodeFromUtf8(utf8Unescaped, byteSpan, out int bytesConsumed, out int bytes
 227
 0228            if (status != OperationStatus.Done)
 0229            {
 0230                bytes = null;
 231
 0232                if (pooledArray is not null)
 0233                {
 0234                    byteSpan.Clear();
 0235                    ArrayPool<byte>.Shared.Return(pooledArray);
 0236                }
 237
 0238                return false;
 239            }
 0240            Debug.Assert(bytesConsumed == utf8Unescaped.Length);
 241
 0242            bytes = byteSpan.Slice(0, bytesWritten).ToArray();
 243
 0244            if (pooledArray is not null)
 0245            {
 0246                byteSpan.Clear();
 0247                ArrayPool<byte>.Shared.Return(pooledArray);
 0248            }
 249
 0250            return true;
 0251        }
 252
 253        public static string TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped)
 0254        {
 255            try
 0256            {
 0257                return s_utf8Encoding.GetString(utf8Unescaped);
 258            }
 0259            catch (DecoderFallbackException ex)
 0260            {
 261                // We want to be consistent with the exception being thrown
 262                // so the user only has to catch a single exception.
 263                // Since we already throw InvalidOperationException for mismatch token type,
 264                // and while unescaping, using that exception for failure to decode invalid UTF-8 bytes as well.
 265                // Therefore, wrapping the DecoderFallbackException around an InvalidOperationException.
 0266                throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(ex);
 267            }
 0268        }
 269
 270        public static int TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped, Span<char> destination)
 0271        {
 272            try
 0273            {
 0274                return s_utf8Encoding.GetChars(utf8Unescaped, destination);
 275            }
 0276            catch (DecoderFallbackException dfe)
 0277            {
 278                // We want to be consistent with the exception being thrown
 279                // so the user only has to catch a single exception.
 280                // Since we already throw InvalidOperationException for mismatch token type,
 281                // and while unescaping, using that exception for failure to decode invalid UTF-8 bytes as well.
 282                // Therefore, wrapping the DecoderFallbackException around an InvalidOperationException.
 0283                throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(dfe);
 284            }
 0285            catch (ArgumentException)
 0286            {
 287                // Destination buffer was too small; clear it up since the encoder might have not.
 0288                destination.Clear();
 0289                throw;
 290            }
 0291        }
 292
 293        public static void ValidateUtf8(ReadOnlySpan<byte> utf8Buffer)
 12294        {
 295#if NET
 12296            if (!Utf8.IsValid(utf8Buffer))
 0297            {
 0298                throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8();
 299            }
 300#else
 301            try
 302            {
 303                _ = s_utf8Encoding.GetCharCount(utf8Buffer);
 304            }
 305            catch (DecoderFallbackException ex)
 306            {
 307                // We want to be consistent with the exception being thrown
 308                // so the user only has to catch a single exception.
 309                // Since we already throw InvalidOperationException for mismatch token type,
 310                // and while unescaping, using that exception for failure to decode invalid UTF-8 bytes as well.
 311                // Therefore, wrapping the DecoderFallbackException around an InvalidOperationException.
 312                throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(ex);
 313            }
 314#endif
 12315        }
 316
 317        internal static int GetUtf8ByteCount(ReadOnlySpan<char> text)
 4318        {
 319            try
 4320            {
 4321                return s_utf8Encoding.GetByteCount(text);
 322            }
 0323            catch (EncoderFallbackException ex)
 0324            {
 325                // We want to be consistent with the exception being thrown
 326                // so the user only has to catch a single exception.
 327                // Since we already throw ArgumentException when validating other arguments,
 328                // using that exception for failure to encode invalid UTF-16 chars as well.
 329                // Therefore, wrapping the EncoderFallbackException around an ArgumentException.
 0330                throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(ex);
 331            }
 4332        }
 333
 334        internal static int GetUtf8FromText(ReadOnlySpan<char> text, Span<byte> dest)
 4335        {
 336            try
 4337            {
 4338                return s_utf8Encoding.GetBytes(text, dest);
 339            }
 0340            catch (EncoderFallbackException ex)
 0341            {
 342                // We want to be consistent with the exception being thrown
 343                // so the user only has to catch a single exception.
 344                // Since we already throw ArgumentException when validating other arguments,
 345                // using that exception for failure to encode invalid UTF-16 chars as well.
 346                // Therefore, wrapping the EncoderFallbackException around an ArgumentException.
 0347                throw ThrowHelper.GetArgumentException_ReadInvalidUTF16(ex);
 348            }
 4349        }
 350
 351        internal static string GetTextFromUtf8(ReadOnlySpan<byte> utf8Text)
 4352        {
 4353            return s_utf8Encoding.GetString(utf8Text);
 4354        }
 355
 356        internal static void Unescape(ReadOnlySpan<byte> source, Span<byte> destination, out int written)
 0357        {
 0358            Debug.Assert(destination.Length >= source.Length);
 359
 0360            int idx = source.IndexOf(JsonConstants.BackSlash);
 0361            Debug.Assert(idx >= 0);
 362
 0363            bool result = TryUnescape(source, destination, idx, out written);
 0364            Debug.Assert(result);
 0365        }
 366
 367        internal static void Unescape(ReadOnlySpan<byte> source, Span<byte> destination, int idx, out int written)
 0368        {
 0369            Debug.Assert(idx >= 0 && idx < source.Length);
 0370            Debug.Assert(source[idx] == JsonConstants.BackSlash);
 0371            Debug.Assert(destination.Length >= source.Length);
 372
 0373            bool result = TryUnescape(source, destination, idx, out written);
 0374            Debug.Assert(result);
 0375        }
 376
 377        /// <summary>
 378        /// Used when writing to buffers not guaranteed to fit the unescaped result.
 379        /// </summary>
 380        internal static bool TryUnescape(ReadOnlySpan<byte> source, Span<byte> destination, out int written)
 0381        {
 0382            int idx = source.IndexOf(JsonConstants.BackSlash);
 0383            Debug.Assert(idx >= 0);
 384
 0385            return TryUnescape(source, destination, idx, out written);
 0386        }
 387
 388        /// <summary>
 389        /// Used when writing to buffers not guaranteed to fit the unescaped result.
 390        /// </summary>
 391        private static bool TryUnescape(ReadOnlySpan<byte> source, Span<byte> destination, int idx, out int written)
 0392        {
 0393            Debug.Assert(idx >= 0 && idx < source.Length);
 0394            Debug.Assert(source[idx] == JsonConstants.BackSlash);
 395
 0396            if (!source.Slice(0, idx).TryCopyTo(destination))
 0397            {
 0398                written = 0;
 0399                goto DestinationTooShort;
 400            }
 401
 0402            written = idx;
 403
 0404            while (true)
 0405            {
 0406                Debug.Assert(source[idx] == JsonConstants.BackSlash);
 407
 0408                if (written == destination.Length)
 0409                {
 0410                    goto DestinationTooShort;
 411                }
 412
 0413                switch (source[++idx])
 414                {
 415                    case JsonConstants.Quote:
 0416                        destination[written++] = JsonConstants.Quote;
 0417                        break;
 418                    case (byte)'n':
 0419                        destination[written++] = JsonConstants.LineFeed;
 0420                        break;
 421                    case (byte)'r':
 0422                        destination[written++] = JsonConstants.CarriageReturn;
 0423                        break;
 424                    case JsonConstants.BackSlash:
 0425                        destination[written++] = JsonConstants.BackSlash;
 0426                        break;
 427                    case JsonConstants.Slash:
 0428                        destination[written++] = JsonConstants.Slash;
 0429                        break;
 430                    case (byte)'t':
 0431                        destination[written++] = JsonConstants.Tab;
 0432                        break;
 433                    case (byte)'b':
 0434                        destination[written++] = JsonConstants.BackSpace;
 0435                        break;
 436                    case (byte)'f':
 0437                        destination[written++] = JsonConstants.FormFeed;
 0438                        break;
 439                    default:
 0440                        Debug.Assert(source[idx] == 'u', "invalid escape sequences must have already been caught by Utf8
 441
 442                        // The source is known to be valid JSON, and hence if we see a \u, it is guaranteed to have 4 he
 443                        // Otherwise, the Utf8JsonReader would have already thrown an exception.
 0444                        Debug.Assert(source.Length >= idx + 5);
 445
 0446                        bool result = Utf8Parser.TryParse(source.Slice(idx + 1, 4), out int scalar, out int bytesConsume
 0447                        Debug.Assert(result);
 0448                        Debug.Assert(bytesConsumed == 4);
 0449                        idx += 4;
 450
 0451                        if (JsonHelpers.IsInRangeInclusive((uint)scalar, JsonConstants.HighSurrogateStartValue, JsonCons
 0452                        {
 453                            // The first hex value cannot be a low surrogate.
 0454                            if (scalar >= JsonConstants.LowSurrogateStartValue)
 0455                            {
 0456                                ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16(scalar);
 457                            }
 458
 0459                            Debug.Assert(JsonHelpers.IsInRangeInclusive((uint)scalar, JsonConstants.HighSurrogateStartVa
 460
 461                            // We must have a low surrogate following a high surrogate.
 0462                            if (source.Length < idx + 7 || source[idx + 1] != '\\' || source[idx + 2] != 'u')
 0463                            {
 0464                                ThrowHelper.ThrowInvalidOperationException_ReadIncompleteUTF16();
 465                            }
 466
 467                            // The source is known to be valid JSON, and hence if we see a \u, it is guaranteed to have 
 468                            // Otherwise, the Utf8JsonReader would have already thrown an exception.
 0469                            result = Utf8Parser.TryParse(source.Slice(idx + 3, 4), out int lowSurrogate, out bytesConsum
 0470                            Debug.Assert(result);
 0471                            Debug.Assert(bytesConsumed == 4);
 0472                            idx += 6;
 473
 474                            // If the first hex value is a high surrogate, the next one must be a low surrogate.
 0475                            if (!JsonHelpers.IsInRangeInclusive((uint)lowSurrogate, JsonConstants.LowSurrogateStartValue
 0476                            {
 0477                                ThrowHelper.ThrowInvalidOperationException_ReadInvalidUTF16(lowSurrogate);
 478                            }
 479
 480                            // To find the unicode scalar:
 481                            // (0x400 * (High surrogate - 0xD800)) + Low surrogate - 0xDC00 + 0x10000
 0482                            scalar = (JsonConstants.BitShiftBy10 * (scalar - JsonConstants.HighSurrogateStartValue))
 0483                                + (lowSurrogate - JsonConstants.LowSurrogateStartValue)
 0484                                + JsonConstants.UnicodePlane01StartValue;
 0485                        }
 486
 0487                        var rune = new Rune(scalar);
 0488                        bool success = rune.TryEncodeToUtf8(destination.Slice(written), out int bytesWritten);
 0489                        if (!success)
 0490                        {
 0491                            goto DestinationTooShort;
 492                        }
 493
 0494                        Debug.Assert(bytesWritten <= 4);
 0495                        written += bytesWritten;
 0496                        break;
 497                }
 498
 0499                if (++idx == source.Length)
 0500                {
 0501                    goto Success;
 502                }
 503
 0504                if (source[idx] != JsonConstants.BackSlash)
 0505                {
 0506                    ReadOnlySpan<byte> remaining = source.Slice(idx);
 0507                    int nextUnescapedSegmentLength = remaining.IndexOf(JsonConstants.BackSlash);
 0508                    if (nextUnescapedSegmentLength < 0)
 0509                    {
 0510                        nextUnescapedSegmentLength = remaining.Length;
 0511                    }
 512
 0513                    if ((uint)(written + nextUnescapedSegmentLength) >= (uint)destination.Length)
 0514                    {
 0515                        goto DestinationTooShort;
 516                    }
 517
 0518                    Debug.Assert(nextUnescapedSegmentLength > 0);
 0519                    switch (nextUnescapedSegmentLength)
 520                    {
 521                        case 1:
 0522                            destination[written++] = source[idx++];
 0523                            break;
 524                        case 2:
 0525                            destination[written++] = source[idx++];
 0526                            destination[written++] = source[idx++];
 0527                            break;
 528                        case 3:
 0529                            destination[written++] = source[idx++];
 0530                            destination[written++] = source[idx++];
 0531                            destination[written++] = source[idx++];
 0532                            break;
 533                        default:
 0534                            remaining.Slice(0, nextUnescapedSegmentLength).CopyTo(destination.Slice(written));
 0535                            written += nextUnescapedSegmentLength;
 0536                            idx += nextUnescapedSegmentLength;
 0537                            break;
 538                    }
 539
 0540                    Debug.Assert(idx == source.Length || source[idx] == JsonConstants.BackSlash);
 541
 0542                    if (idx == source.Length)
 0543                    {
 0544                        goto Success;
 545                    }
 0546                }
 0547            }
 548
 0549        Success:
 0550            return true;
 551
 0552        DestinationTooShort:
 0553            return false;
 0554        }
 555    }
 556}
 557

Methods/Properties

.cctor()
ContainsSpecialCharacters(System.ReadOnlySpan`1<System.Char>)
AppendEscapedPropertyName(System.Text.ValueStringBuilder&,System.String)
AppendEscapedPropertyName(System.Text.StringBuilder,System.String)
CountNewLines(System.ReadOnlySpan`1<System.Byte>)
ToValueKind(System.Text.Json.JsonTokenType)
IsTokenTypePrimitive(System.Text.Json.JsonTokenType)
IsHexDigit(System.Byte)
TryGetValue(System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.DateTime&)
TryGetEscapedDateTime(System.ReadOnlySpan`1<System.Byte>,System.DateTime&)
TryGetValue(System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.DateTimeOffset&)
TryGetEscapedDateTimeOffset(System.ReadOnlySpan`1<System.Byte>,System.DateTimeOffset&)
TryGetValue(System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.Guid&)
TryGetEscapedGuid(System.ReadOnlySpan`1<System.Byte>,System.Guid&)
TryGetFloatingPointConstant(System.ReadOnlySpan`1<System.Byte>,System.Half&)
TryGetFloatingPointConstant(System.ReadOnlySpan`1<System.Byte>,System.Single&)
TryGetFloatingPointConstant(System.ReadOnlySpan`1<System.Byte>,System.Double&)
.cctor()
IndexOfQuoteOrAnyControlOrBackSlash(System.ReadOnlySpan`1<System.Byte>)
IndexOfFirstNonWhiteSpace(System.ReadOnlySpan`1<System.Byte>)
TryGetUnescapedBase64Bytes(System.ReadOnlySpan`1<System.Byte>,System.Byte[]&)
.cctor()
GetUnescapedString(System.ReadOnlySpan`1<System.Byte>)
GetUnescaped(System.ReadOnlySpan`1<System.Byte>)
UnescapeAndCompare(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>)
UnescapeAndCompare(System.Buffers.ReadOnlySequence`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>)
UnescapeAndCompareBothInputs(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>)
TryDecodeBase64InPlace(System.Span`1<System.Byte>,System.Byte[]&)
TryDecodeBase64(System.ReadOnlySpan`1<System.Byte>,System.Byte[]&)
TranscodeHelper(System.ReadOnlySpan`1<System.Byte>)
TranscodeHelper(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Char>)
ValidateUtf8(System.ReadOnlySpan`1<System.Byte>)
GetUtf8ByteCount(System.ReadOnlySpan`1<System.Char>)
GetUtf8FromText(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Byte>)
GetTextFromUtf8(System.ReadOnlySpan`1<System.Byte>)
Unescape(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32&)
Unescape(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32,System.Int32&)
TryUnescape(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32&)
TryUnescape(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32,System.Int32&)