< Summary

Line coverage
37%
Covered lines: 215
Uncovered lines: 362
Coverable lines: 577
Total lines: 980
Line coverage: 37.2%
Branch coverage
31%
Covered branches: 67
Total branches: 211
Branch coverage: 31.7%
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(...)38.46%131350%
File 1: IsTokenTypePrimitive(...)100%110%
File 1: IsHexDigit(...)100%11100%
File 1: TryGetValue(...)16.66%6637.5%
File 1: TryGetEscapedDateTime(...)0%440%
File 1: TryGetValue(...)16.66%6637.5%
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(...)58.33%121252%
File 1: TryGetFloatingPointConstant(...)58.33%121252%
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(...)50%8875%
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(...)50%22100%
File 3: TryUnescape(...)100%110%
File 3: TryUnescape(...)36.2%585846.66%

File(s)

D:\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)
 2527897        {
 2527898            int lastLineFeedIndex = data.LastIndexOf(JsonConstants.LineFeed);
 2527899            int newLines = 0;
 100
 25278101            if (lastLineFeedIndex >= 0)
 618102            {
 618103                newLines = 1;
 618104                data = data.Slice(0, lastLineFeedIndex);
 618105                newLines += data.Count(JsonConstants.LineFeed);
 618106            }
 107
 25278108            return (newLines, lastLineFeedIndex);
 25278109        }
 110
 111        internal static JsonValueKind ToValueKind(this JsonTokenType tokenType)
 288112        {
 288113            switch (tokenType)
 114            {
 115                case JsonTokenType.None:
 0116                    return JsonValueKind.Undefined;
 117                case JsonTokenType.StartArray:
 0118                    return JsonValueKind.Array;
 119                case JsonTokenType.StartObject:
 0120                    return JsonValueKind.Object;
 121                case JsonTokenType.String:
 122                case JsonTokenType.Number:
 123                case JsonTokenType.True:
 124                case JsonTokenType.False:
 125                case JsonTokenType.Null:
 126                    // This is the offset between the set of literals within JsonValueType and JsonTokenType
 127                    // Essentially: JsonTokenType.Null - JsonValueType.Null
 288128                    return (JsonValueKind)((byte)tokenType - 4);
 129                default:
 0130                    Debug.Fail($"No mapping for token type {tokenType}");
 131                    return JsonValueKind.Undefined;
 132            }
 288133        }
 134
 135        // Returns true if the TokenType is a primitive "value", i.e. String, Number, True, False, and Null
 136        // Otherwise, return false.
 137        public static bool IsTokenTypePrimitive(JsonTokenType tokenType) =>
 0138            (tokenType - JsonTokenType.String) <= (JsonTokenType.Null - JsonTokenType.String);
 139
 140        // A hex digit is valid if it is in the range: [0..9] | [A..F] | [a..f]
 141        // Otherwise, return false.
 224142        public static bool IsHexDigit(byte nextByte) => HexConverter.IsHexChar(nextByte);
 143
 144        public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTime value)
 162145        {
 162146            if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length))
 162147            {
 162148                value = default;
 162149                return false;
 150            }
 151
 152            // Segment needs to be unescaped
 0153            if (isEscaped)
 0154            {
 0155                return TryGetEscapedDateTime(segment, out value);
 156            }
 157
 0158            Debug.Assert(!segment.Contains(JsonConstants.BackSlash));
 159
 0160            if (JsonHelpers.TryParseAsISO(segment, out DateTime tmp))
 0161            {
 0162                value = tmp;
 0163                return true;
 164            }
 165
 0166            value = default;
 0167            return false;
 162168        }
 169
 170        public static unsafe bool TryGetEscapedDateTime(ReadOnlySpan<byte> source, out DateTime value)
 0171        {
 0172            Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
 0173            Span<byte> sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
 174
 0175            Unescape(source, sourceUnescaped, out int written);
 0176            Debug.Assert(written > 0);
 177
 0178            sourceUnescaped = sourceUnescaped.Slice(0, written);
 0179            Debug.Assert(!sourceUnescaped.IsEmpty);
 180
 0181            if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(sourceUnescaped.Length)
 0182                && JsonHelpers.TryParseAsISO(sourceUnescaped, out DateTime tmp))
 0183            {
 0184                value = tmp;
 0185                return true;
 186            }
 187
 0188            value = default;
 0189            return false;
 0190        }
 191
 192        public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out DateTimeOffset value)
 86193        {
 86194            if (!JsonHelpers.IsValidDateTimeOffsetParseLength(segment.Length))
 86195            {
 86196                value = default;
 86197                return false;
 198            }
 199
 200            // Segment needs to be unescaped
 0201            if (isEscaped)
 0202            {
 0203                return TryGetEscapedDateTimeOffset(segment, out value);
 204            }
 205
 0206            Debug.Assert(!segment.Contains(JsonConstants.BackSlash));
 207
 0208            if (JsonHelpers.TryParseAsISO(segment, out DateTimeOffset tmp))
 0209            {
 0210                value = tmp;
 0211                return true;
 212            }
 213
 0214            value = default;
 0215            return false;
 86216        }
 217
 218        public static unsafe bool TryGetEscapedDateTimeOffset(ReadOnlySpan<byte> source, out DateTimeOffset value)
 0219        {
 0220            Debug.Assert(source.Length <= JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
 0221            Span<byte> sourceUnescaped = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
 222
 0223            Unescape(source, sourceUnescaped, out int written);
 0224            Debug.Assert(written > 0);
 225
 0226            sourceUnescaped = sourceUnescaped.Slice(0, written);
 0227            Debug.Assert(!sourceUnescaped.IsEmpty);
 228
 0229            if (JsonHelpers.IsValidUnescapedDateTimeOffsetParseLength(sourceUnescaped.Length)
 0230                && JsonHelpers.TryParseAsISO(sourceUnescaped, out DateTimeOffset tmp))
 0231            {
 0232                value = tmp;
 0233                return true;
 234            }
 235
 0236            value = default;
 0237            return false;
 0238        }
 239
 240        public static bool TryGetValue(ReadOnlySpan<byte> segment, bool isEscaped, out Guid value)
 110241        {
 110242            if (segment.Length > JsonConstants.MaximumEscapedGuidLength)
 0243            {
 0244                value = default;
 0245                return false;
 246            }
 247
 248            // Segment needs to be unescaped
 110249            if (isEscaped)
 0250            {
 0251                return TryGetEscapedGuid(segment, out value);
 252            }
 253
 110254            Debug.Assert(!segment.Contains(JsonConstants.BackSlash));
 255
 110256            if (segment.Length == JsonConstants.MaximumFormatGuidLength
 110257                && Utf8Parser.TryParse(segment, out Guid tmp, out _, 'D'))
 0258            {
 0259                value = tmp;
 0260                return true;
 261            }
 262
 110263            value = default;
 110264            return false;
 110265        }
 266
 267        public static unsafe bool TryGetEscapedGuid(ReadOnlySpan<byte> source, out Guid value)
 0268        {
 0269            Debug.Assert(source.Length <= JsonConstants.MaximumEscapedGuidLength);
 270
 0271            Span<byte> utf8Unescaped = stackalloc byte[JsonConstants.MaximumEscapedGuidLength];
 0272            Unescape(source, utf8Unescaped, out int written);
 0273            Debug.Assert(written > 0);
 274
 0275            utf8Unescaped = utf8Unescaped.Slice(0, written);
 0276            Debug.Assert(!utf8Unescaped.IsEmpty);
 277
 0278            if (utf8Unescaped.Length == JsonConstants.MaximumFormatGuidLength
 0279                && Utf8Parser.TryParse(utf8Unescaped, out Guid tmp, out _, 'D'))
 0280            {
 0281                value = tmp;
 0282                return true;
 283            }
 284
 0285            value = default;
 0286            return false;
 0287        }
 288
 289#if NET
 290        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out Half value)
 44291        {
 44292            if (span.Length == 3)
 10293            {
 10294                if (span.SequenceEqual(JsonConstants.NaNValue))
 0295                {
 0296                    value = Half.NaN;
 0297                    return true;
 298                }
 10299            }
 34300            else if (span.Length == 8)
 0301            {
 0302                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0303                {
 0304                    value = Half.PositiveInfinity;
 0305                    return true;
 306                }
 0307            }
 34308            else if (span.Length == 9)
 0309            {
 0310                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0311                {
 0312                    value = Half.NegativeInfinity;
 0313                    return true;
 314                }
 0315            }
 316
 44317            value = default;
 44318            return false;
 44319        }
 320#endif
 321
 322        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out float value)
 52323        {
 52324            if (span.Length == 3)
 10325            {
 10326                if (span.SequenceEqual(JsonConstants.NaNValue))
 0327                {
 0328                    value = float.NaN;
 0329                    return true;
 330                }
 10331            }
 42332            else if (span.Length == 8)
 6333            {
 6334                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0335                {
 0336                    value = float.PositiveInfinity;
 0337                    return true;
 338                }
 6339            }
 36340            else if (span.Length == 9)
 0341            {
 0342                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0343                {
 0344                    value = float.NegativeInfinity;
 0345                    return true;
 346                }
 0347            }
 348
 52349            value = 0;
 52350            return false;
 52351        }
 352
 353        public static bool TryGetFloatingPointConstant(ReadOnlySpan<byte> span, out double value)
 58354        {
 58355            if (span.Length == 3)
 14356            {
 14357                if (span.SequenceEqual(JsonConstants.NaNValue))
 0358                {
 0359                    value = double.NaN;
 0360                    return true;
 361                }
 14362            }
 44363            else if (span.Length == 8)
 6364            {
 6365                if (span.SequenceEqual(JsonConstants.PositiveInfinityValue))
 0366                {
 0367                    value = double.PositiveInfinity;
 0368                    return true;
 369                }
 6370            }
 38371            else if (span.Length == 9)
 0372            {
 0373                if (span.SequenceEqual(JsonConstants.NegativeInfinityValue))
 0374                {
 0375                    value = double.NegativeInfinity;
 0376                    return true;
 377                }
 0378            }
 379
 58380            value = 0;
 58381            return false;
 58382        }
 383    }
 384}

D:\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) =>
 807623            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)
 2997335        {
 2997336            int index = span.IndexOfAnyExcept(s_whiteSpace);
 2997337            return index < 0 ? span.Length : index;
 2997338        }
 39    }
 40}

D:\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 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
 12163        {
 12164            int index1 = utf8Source1.IndexOf(JsonConstants.BackSlash);
 12165            int index2 = utf8Source2.IndexOf(JsonConstants.BackSlash);
 166
 12167            Debug.Assert(index1 >= 0, "the first parameter is not escaped");
 12168            Debug.Assert(index2 >= 0, "the second parameter is not escaped");
 169
 12170            byte[]? unescapedArray1 = null;
 12171            byte[]? unescapedArray2 = null;
 172
 12173            Span<byte> utf8Unescaped1 = utf8Source1.Length <= JsonConstants.StackallocByteThreshold ?
 12174                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 12175                (unescapedArray1 = ArrayPool<byte>.Shared.Rent(utf8Source1.Length));
 176
 12177            Span<byte> utf8Unescaped2 = utf8Source2.Length <= JsonConstants.StackallocByteThreshold ?
 12178                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 12179                (unescapedArray2 = ArrayPool<byte>.Shared.Rent(utf8Source2.Length));
 180
 12181            Unescape(utf8Source1, utf8Unescaped1, index1, out int written);
 12182            utf8Unescaped1 = utf8Unescaped1.Slice(0, written);
 12183            Debug.Assert(!utf8Unescaped1.IsEmpty);
 184
 12185            Unescape(utf8Source2, utf8Unescaped2, index2, out written);
 12186            utf8Unescaped2 = utf8Unescaped2.Slice(0, written);
 12187            Debug.Assert(!utf8Unescaped2.IsEmpty);
 188
 12189            bool result = utf8Unescaped1.SequenceEqual(utf8Unescaped2);
 190
 12191            if (unescapedArray1 is not null)
 0192            {
 0193                utf8Unescaped1.Clear();
 0194                ArrayPool<byte>.Shared.Return(unescapedArray1);
 0195            }
 196
 12197            if (unescapedArray2 is not null)
 0198            {
 0199                utf8Unescaped2.Clear();
 0200                ArrayPool<byte>.Shared.Return(unescapedArray2);
 0201            }
 202
 12203            return result;
 12204        }
 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
 318219        {
 318220            byte[]? pooledArray = null;
 221
 318222            Span<byte> byteSpan = utf8Unescaped.Length <= JsonConstants.StackallocByteThreshold ?
 318223                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 318224                (pooledArray = ArrayPool<byte>.Shared.Rent(utf8Unescaped.Length));
 225
 318226            OperationStatus status = Base64.DecodeFromUtf8(utf8Unescaped, byteSpan, out int bytesConsumed, out int bytes
 227
 318228            if (status != OperationStatus.Done)
 198229            {
 198230                bytes = null;
 231
 198232                if (pooledArray is not null)
 0233                {
 0234                    byteSpan.Clear();
 0235                    ArrayPool<byte>.Shared.Return(pooledArray);
 0236                }
 237
 198238                return false;
 239            }
 120240            Debug.Assert(bytesConsumed == utf8Unescaped.Length);
 241
 120242            bytes = byteSpan.Slice(0, bytesWritten).ToArray();
 243
 120244            if (pooledArray is not null)
 0245            {
 0246                byteSpan.Clear();
 0247                ArrayPool<byte>.Shared.Return(pooledArray);
 0248            }
 249
 120250            return true;
 318251        }
 252
 253        public static string TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped)
 332254        {
 255            try
 332256            {
 332257                return s_utf8Encoding.GetString(utf8Unescaped);
 258            }
 60259            catch (DecoderFallbackException ex)
 60260            {
 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.
 60266                throw ThrowHelper.GetInvalidOperationException_ReadInvalidUTF8(ex);
 267            }
 272268        }
 269
 270        public static int TranscodeHelper(ReadOnlySpan<byte> utf8Unescaped, Span<char> destination)
 166271        {
 272            try
 166273            {
 166274                return s_utf8Encoding.GetChars(utf8Unescaped, destination);
 275            }
 30276            catch (DecoderFallbackException dfe)
 30277            {
 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.
 30283                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            }
 136291        }
 292
 293        public static void ValidateUtf8(ReadOnlySpan<byte> utf8Buffer)
 775294        {
 295#if NET
 775296            if (!Utf8.IsValid(utf8Buffer))
 104297            {
 104298                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
 671315        }
 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)
 24368        {
 24369            Debug.Assert(idx >= 0 && idx < source.Length);
 24370            Debug.Assert(source[idx] == JsonConstants.BackSlash);
 24371            Debug.Assert(destination.Length >= source.Length);
 372
 24373            bool result = TryUnescape(source, destination, idx, out written);
 24374            Debug.Assert(result);
 24375        }
 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)
 24392        {
 24393            Debug.Assert(idx >= 0 && idx < source.Length);
 24394            Debug.Assert(source[idx] == JsonConstants.BackSlash);
 395
 24396            if (!source.Slice(0, idx).TryCopyTo(destination))
 0397            {
 0398                written = 0;
 0399                goto DestinationTooShort;
 400            }
 401
 24402            written = idx;
 403
 28404            while (true)
 28405            {
 28406                Debug.Assert(source[idx] == JsonConstants.BackSlash);
 407
 28408                if (written == destination.Length)
 0409                {
 0410                    goto DestinationTooShort;
 411                }
 412
 28413                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:
 28440                        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.
 28444                        Debug.Assert(source.Length >= idx + 5);
 445
 28446                        bool result = Utf8Parser.TryParse(source.Slice(idx + 1, 4), out int scalar, out int bytesConsume
 28447                        Debug.Assert(result);
 28448                        Debug.Assert(bytesConsumed == 4);
 28449                        idx += 4;
 450
 28451                        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
 28487                        var rune = new Rune(scalar);
 28488                        bool success = rune.TryEncodeToUtf8(destination.Slice(written), out int bytesWritten);
 28489                        if (!success)
 0490                        {
 0491                            goto DestinationTooShort;
 492                        }
 493
 28494                        Debug.Assert(bytesWritten <= 4);
 28495                        written += bytesWritten;
 28496                        break;
 497                }
 498
 28499                if (++idx == source.Length)
 4500                {
 4501                    goto Success;
 502                }
 503
 24504                if (source[idx] != JsonConstants.BackSlash)
 20505                {
 20506                    ReadOnlySpan<byte> remaining = source.Slice(idx);
 20507                    int nextUnescapedSegmentLength = remaining.IndexOf(JsonConstants.BackSlash);
 20508                    if (nextUnescapedSegmentLength < 0)
 20509                    {
 20510                        nextUnescapedSegmentLength = remaining.Length;
 20511                    }
 512
 20513                    if ((uint)(written + nextUnescapedSegmentLength) >= (uint)destination.Length)
 0514                    {
 0515                        goto DestinationTooShort;
 516                    }
 517
 20518                    Debug.Assert(nextUnescapedSegmentLength > 0);
 20519                    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:
 20534                            remaining.Slice(0, nextUnescapedSegmentLength).CopyTo(destination.Slice(written));
 20535                            written += nextUnescapedSegmentLength;
 20536                            idx += nextUnescapedSegmentLength;
 20537                            break;
 538                    }
 539
 20540                    Debug.Assert(idx == source.Length || source[idx] == JsonConstants.BackSlash);
 541
 20542                    if (idx == source.Length)
 20543                    {
 20544                        goto Success;
 545                    }
 0546                }
 4547            }
 548
 24549        Success:
 24550            return true;
 551
 0552        DestinationTooShort:
 0553            return false;
 24554        }
 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>)
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&)