< Summary

Line coverage
23%
Covered lines: 135
Uncovered lines: 437
Coverable lines: 572
Total lines: 974
Line coverage: 23.6%
Branch coverage
22%
Covered branches: 48
Total branches: 213
Branch coverage: 22.5%
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%12120%
File 1: AppendEscapedPropertyName(...)0%12120%
File 1: CountNewLines(...)0%220%
File 1: ToValueKind(...)46.15%131362.5%
File 1: IsTokenTypePrimitive(...)100%110%
File 1: IsHexDigit(...)100%110%
File 1: TryGetValue(...)66.66%6668.75%
File 1: TryGetEscapedDateTime(...)0%440%
File 1: TryGetValue(...)66.66%6668.75%
File 1: TryGetEscapedDateTimeOffset(...)0%440%
File 1: TryGetValue(...)50%8852.94%
File 1: TryGetEscapedGuid(...)0%440%
File 1: TryGetFloatingPointConstant(...)41.66%121240%
File 1: TryGetFloatingPointConstant(...)75%121264%
File 1: TryGetFloatingPointConstant(...)75%121264%
File 2: .cctor()100%11100%
File 2: IndexOfQuoteOrAnyControlOrBackSlash(...)100%11100%
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(...)62.5%8866.66%
File 3: TranscodeHelper(...)100%11100%
File 3: TranscodeHelper(...)100%1163.63%
File 3: ValidateUtf8(...)100%22100%
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)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\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)
 097        {
 098            int lastLineFeedIndex = data.LastIndexOf(JsonConstants.LineFeed);
 099            int newLines = 0;
 100
 0101            if (lastLineFeedIndex >= 0)
 0102            {
 0103                newLines = 1;
 0104                data = data.Slice(0, lastLineFeedIndex);
 105#if NET
 0106                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
 0115            }
 116
 0117            return (newLines, lastLineFeedIndex);
 0118        }
 119
 120        internal static JsonValueKind ToValueKind(this JsonTokenType tokenType)
 142121        {
 142122            switch (tokenType)
 123            {
 124                case JsonTokenType.None:
 0125                    return JsonValueKind.Undefined;
 126                case JsonTokenType.StartArray:
 66127                    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
 76137                    return (JsonValueKind)((byte)tokenType - 4);
 138                default:
 0139                    Debug.Fail($"No mapping for token type {tokenType}");
 140                    return JsonValueKind.Undefined;
 141            }
 142142        }
 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)
 400154        {
 400155            if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length))
 360156            {
 360157                value = default;
 360158                return false;
 159            }
 160
 161            // Segment needs to be unescaped
 40162            if (isEscaped)
 0163            {
 0164                return TryGetEscapedDateTime(segment, out value);
 165            }
 166
 40167            Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);
 168
 40169            if (JsonHelpers.TryParseAsISO(segment, out DateTime tmp))
 0170            {
 0171                value = tmp;
 0172                return true;
 173            }
 174
 40175            value = default;
 40176            return false;
 400177        }
 178
 179        public static 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)
 210202        {
 210203            if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length))
 192204            {
 192205                value = default;
 192206                return false;
 207            }
 208
 209            // Segment needs to be unescaped
 18210            if (isEscaped)
 0211            {
 0212                return TryGetEscapedDateTimeOffset(segment, out value);
 213            }
 214
 18215            Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);
 216
 18217            if (JsonHelpers.TryParseAsISO(segment, out DateTimeOffset tmp))
 0218            {
 0219                value = tmp;
 0220                return true;
 221            }
 222
 18223            value = default;
 18224            return false;
 210225        }
 226
 227        public static 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)
 312250        {
 312251            if (segment.Length > JsonConstants.MaximumEscapedGuidLength)
 0252            {
 0253                value = default;
 0254                return false;
 255            }
 256
 257            // Segment needs to be unescaped
 312258            if (isEscaped)
 0259            {
 0260                return TryGetEscapedGuid(segment, out value);
 261            }
 262
 312263            Debug.Assert(segment.IndexOf(JsonConstants.BackSlash) == -1);
 264
 312265            if (segment.Length == JsonConstants.MaximumFormatGuidLength
 312266                && Utf8Parser.TryParse(segment, out Guid tmp, out _, 'D'))
 0267            {
 0268                value = tmp;
 0269                return true;
 270            }
 271
 312272            value = default;
 312273            return false;
 312274        }
 275
 276        public static 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)
 90300        {
 90301            if (span.Length == 3)
 10302            {
 10303                if (span.SequenceEqual(JsonConstants.NaNValue))
 0304                {
 0305                    value = Half.NaN;
 0306                    return true;
 307                }
 10308            }
 80309            else if (span.Length == 8)
 0310            {
 0311                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0312                {
 0313                    value = Half.PositiveInfinity;
 0314                    return true;
 315                }
 0316            }
 80317            else if (span.Length == 9)
 0318            {
 0319                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0320                {
 0321                    value = Half.NegativeInfinity;
 0322                    return true;
 323                }
 0324            }
 325
 90326            value = default;
 90327            return false;
 90328        }
 329#endif
 330
 331        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out float value)
 140332        {
 140333            if (span.Length == 3)
 16334            {
 16335                if (span.SequenceEqual(JsonConstants.NaNValue))
 0336                {
 0337                    value = float.NaN;
 0338                    return true;
 339                }
 16340            }
 124341            else if (span.Length == 8)
 12342            {
 12343                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0344                {
 0345                    value = float.PositiveInfinity;
 0346                    return true;
 347                }
 12348            }
 112349            else if (span.Length == 9)
 2350            {
 2351                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0352                {
 0353                    value = float.NegativeInfinity;
 0354                    return true;
 355                }
 2356            }
 357
 140358            value = 0;
 140359            return false;
 140360        }
 361
 362        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out double value)
 160363        {
 160364            if (span.Length == 3)
 22365            {
 22366                if (span.SequenceEqual(JsonConstants.NaNValue))
 0367                {
 0368                    value = double.NaN;
 0369                    return true;
 370                }
 22371            }
 138372            else if (span.Length == 8)
 16373            {
 16374                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0375                {
 0376                    value = double.PositiveInfinity;
 0377                    return true;
 378                }
 16379            }
 122380            else if (span.Length == 9)
 2381            {
 2382                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0383                {
 0384                    value = double.NegativeInfinity;
 0385                    return true;
 386                }
 2387            }
 388
 160389            value = 0;
 160390            return false;
 160391        }
 392    }
 393}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\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) =>
 1923823            span.IndexOfAny(s_controlQuoteBackslash);
 24    }
 25}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\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 bool TryGetUnescapedBase64Bytes(ReadOnlySpan<byte> utf8Source, [NotNullWhen(true)] out byte[]? byt
 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 != 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 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 != null)
 061            {
 062                utf8Unescaped.Clear();
 063                ArrayPool<byte>.Shared.Return(pooledName);
 064            }
 65
 066            return utf8String;
 067        }
 68
 69        public static 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 != 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 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 != null)
 0113            {
 0114                utf8Unescaped.Clear();
 0115                ArrayPool<byte>.Shared.Return(unescapedArray);
 0116            }
 117
 0118            return result;
 0119        }
 120
 121        public static 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 != null)
 0151            {
 0152                Debug.Assert(escapedArray != 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 bool UnescapeAndCompareBothInputs(ReadOnlySpan<byte> utf8Source1, ReadOnlySpan<byte> utf8Source2)
 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 != null)
 0192            {
 0193                utf8Unescaped1.Clear();
 0194                ArrayPool<byte>.Shared.Return(unescapedArray1);
 0195            }
 196
 0197            if (unescapedArray2 != 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 bool TryDecodeBase64(ReadOnlySpan<byte> utf8Unescaped, [NotNullWhen(true)] out byte[]? bytes)
 834219        {
 834220            byte[]? pooledArray = null;
 221
 834222            Span<byte> byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocByteThreshold ?
 834223                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 834224                (pooledArray = ArrayPool<byte>.Shared.Rent(utf8Unescaped.Length));
 225
 834226            OperationStatus status = Base64.DecodeFromUtf8(utf8Unescaped, byteSpan, out int bytesConsumed, out int bytes
 227
 834228            if (status != OperationStatus.Done)
 474229            {
 474230                bytes = null;
 231
 474232                if (pooledArray != null)
 0233                {
 0234                    byteSpan.Clear();
 0235                    ArrayPool<byte>.Shared.Return(pooledArray);
 0236                }
 237
 474238                return false;
 239            }
 360240            Debug.Assert(bytesConsumed == utf8Unescaped.Length);
 241
 360242            bytes = byteSpan.Slice(0, bytesWritten).ToArray();
 243
 360244            if (pooledArray != null)
 0245            {
 0246                byteSpan.Clear();
 0247                ArrayPool<byte>.Shared.Return(pooledArray);
 0248            }
 249
 360250            return true;
 834251        }
 252
 253        public static string TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped)
 842254        {
 255            try
 842256            {
 842257                return s_utf8Encoding.GetString(utf8Unescaped);
 258            }
 310259            catch (DecoderFallbackException ex)
 310260            {
 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.
 310266                throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(ex);
 267            }
 532268        }
 269
 270        public static int TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped, Span<char> destination)
 542271        {
 272            try
 542273            {
 542274                return s_utf8Encoding.GetChars(utf8Unescaped, destination);
 275            }
 246276            catch (DecoderFallbackException dfe)
 246277            {
 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.
 246283                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            }
 296291        }
 292
 293        public static void ValidateUtf8(ReadOnlySpan<byte> utf8Buffer)
 1547294        {
 295#if NET
 1547296            if (!Utf8.IsValid(utf8Buffer))
 416297            {
 416298                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
 1131315        }
 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}

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>)
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&)