< Summary

Line coverage
5%
Covered lines: 24
Uncovered lines: 440
Coverable lines: 464
Total lines: 815
Line coverage: 5.1%
Branch coverage
5%
Covered branches: 12
Total branches: 206
Branch coverage: 5.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: WriteIndentation(...)0%660%
File 1: ValidateNewLine(...)0%660%
File 1: ValidateIndentCharacter(...)0%440%
File 1: ValidateIndentSize(...)0%660%
File 1: ValidateProperty(...)0%220%
File 1: ValidateValue(...)50%2275%
File 1: ValidateDouble(...)0%220%
File 1: ValidateSingle(...)0%220%
File 1: ValidateProperty(...)0%220%
File 1: ValidateValue(...)50%2275%
File 1: ValidatePropertyAndValue(...)0%440%
File 1: ValidatePropertyAndValue(...)0%440%
File 1: ValidatePropertyAndValue(...)0%440%
File 1: ValidatePropertyAndValue(...)0%440%
File 1: ValidatePropertyNameLength(...)0%220%
File 1: ValidatePropertyNameLength(...)0%220%
File 1: ValidateNumber(...)20%404021.73%
File 1: IsValidUtf8String(...)100%110%
File 1: ToUtf8(...)0%660%
File 1: WriteString(...)0%10100%
File 2: .cctor()100%110%
File 2: WriteDateTimeTrimmed(...)100%110%
File 2: WriteDateTimeOffsetTrimmed(...)100%110%
File 2: TrimDateTimeOffset(...)0%22220%
File 3: .cctor()100%110%
File 3: NeedsEscaping(...)100%110%
File 3: NeedsEscapingNoBoundsCheck(...)100%110%
File 3: NeedsEscaping(...)100%22100%
File 3: NeedsEscaping(...)0%440%
File 3: GetMaxEscapedLength(...)0%220%
File 3: EscapeString(...)0%12120%
File 3: EscapeString(...)100%110%
File 3: EscapeString(...)0%10100%
File 3: EscapeNextBytes(...)0%11110%
File 3: IsAsciiValue(...)100%110%
File 3: IsAsciiValue(...)100%110%
File 3: EscapeString(...)0%12120%
File 3: EscapeString(...)100%110%
File 3: EscapeString(...)0%10100%
File 3: EscapeNextChars(...)0%11110%

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Writer\JsonWriterHelper.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.Diagnostics;
 6using System.Runtime.CompilerServices;
 7using System.Text.Encodings.Web;
 8using System.Text.Unicode;
 9
 10namespace System.Text.Json
 11{
 12    internal static partial class JsonWriterHelper
 13    {
 14        public static void WriteIndentation(Span<byte> buffer, int indent, byte indentByte)
 015        {
 016            Debug.Assert(buffer.Length >= indent);
 17
 18            // Based on perf tests, the break-even point where vectorized Fill is faster
 19            // than explicitly writing the space in a loop is 8.
 020            if (indent < 8)
 021            {
 022                int i = 0;
 023                while (i + 1 < indent)
 024                {
 025                    buffer[i++] = indentByte;
 026                    buffer[i++] = indentByte;
 027                }
 28
 029                if (i < indent)
 030                {
 031                    buffer[i] = indentByte;
 032                }
 033            }
 34            else
 035            {
 036                buffer.Slice(0, indent).Fill(indentByte);
 037            }
 038        }
 39
 40        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 41        public static void ValidateNewLine(string value)
 042        {
 043            if (value is null)
 044                ThrowHelper.ThrowArgumentNullException(nameof(value));
 45
 046            if (value is not JsonConstants.NewLineLineFeed and not JsonConstants.NewLineCarriageReturnLineFeed)
 047                ThrowHelper.ThrowArgumentOutOfRangeException_NewLine(nameof(value));
 048        }
 49
 50        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 51        public static void ValidateIndentCharacter(char value)
 052        {
 053            if (value is not JsonConstants.DefaultIndentCharacter and not JsonConstants.TabIndentCharacter)
 054                ThrowHelper.ThrowArgumentOutOfRangeException_IndentCharacter(nameof(value));
 055        }
 56
 57        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 58        public static void ValidateIndentSize(int value)
 059        {
 060            if (value is < JsonConstants.MinimumIndentSize or > JsonConstants.MaximumIndentSize)
 061                ThrowHelper.ThrowArgumentOutOfRangeException_IndentSize(nameof(value), JsonConstants.MinimumIndentSize, 
 062        }
 63
 64        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 65        public static void ValidateProperty(ReadOnlySpan<byte> propertyName)
 066        {
 067            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize)
 068                ThrowHelper.ThrowArgumentException_PropertyNameTooLarge(propertyName.Length);
 069        }
 70
 71        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 72        public static void ValidateValue(ReadOnlySpan<byte> value)
 2873        {
 2874            if (value.Length > JsonConstants.MaxUnescapedTokenSize)
 075                ThrowHelper.ThrowArgumentException_ValueTooLarge(value.Length);
 2876        }
 77
 78        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 79        public static void ValidateDouble(double value)
 080        {
 081            if (!JsonHelpers.IsFinite(value))
 082            {
 083                ThrowHelper.ThrowArgumentException_ValueNotSupported();
 84            }
 085        }
 86
 87        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 88        public static void ValidateSingle(float value)
 089        {
 090            if (!JsonHelpers.IsFinite(value))
 091            {
 092                ThrowHelper.ThrowArgumentException_ValueNotSupported();
 93            }
 094        }
 95
 96        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 97        public static void ValidateProperty(ReadOnlySpan<char> propertyName)
 098        {
 099            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize)
 0100                ThrowHelper.ThrowArgumentException_PropertyNameTooLarge(propertyName.Length);
 0101        }
 102
 103        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 104        public static void ValidateValue(ReadOnlySpan<char> value)
 4105        {
 4106            if (value.Length > JsonConstants.MaxCharacterTokenSize)
 0107                ThrowHelper.ThrowArgumentException_ValueTooLarge(value.Length);
 4108        }
 109
 110        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 111        public static void ValidatePropertyAndValue(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> value)
 0112        {
 0113            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize || value.Length > JsonConstants.MaxUnescapedTo
 0114                ThrowHelper.ThrowArgumentException(propertyName, value);
 0115        }
 116
 117        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 118        public static void ValidatePropertyAndValue(ReadOnlySpan<byte> propertyName, ReadOnlySpan<char> value)
 0119        {
 0120            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize || value.Length > JsonConstants.MaxCharacterTo
 0121                ThrowHelper.ThrowArgumentException(propertyName, value);
 0122        }
 123
 124        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 125        public static void ValidatePropertyAndValue(ReadOnlySpan<byte> propertyName, ReadOnlySpan<byte> value)
 0126        {
 0127            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize || value.Length > JsonConstants.MaxUnescapedTo
 0128                ThrowHelper.ThrowArgumentException(propertyName, value);
 0129        }
 130
 131        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 132        public static void ValidatePropertyAndValue(ReadOnlySpan<char> propertyName, ReadOnlySpan<char> value)
 0133        {
 0134            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize || value.Length > JsonConstants.MaxCharacterTo
 0135                ThrowHelper.ThrowArgumentException(propertyName, value);
 0136        }
 137
 138        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 139        public static void ValidatePropertyNameLength(ReadOnlySpan<char> propertyName)
 0140        {
 0141            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize)
 0142                ThrowHelper.ThrowPropertyNameTooLargeArgumentException(propertyName.Length);
 0143        }
 144
 145        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 146        public static void ValidatePropertyNameLength(ReadOnlySpan<byte> propertyName)
 0147        {
 0148            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize)
 0149                ThrowHelper.ThrowPropertyNameTooLargeArgumentException(propertyName.Length);
 0150        }
 151
 152        internal static void ValidateNumber(ReadOnlySpan<byte> utf8FormattedNumber)
 28153        {
 154            // This is a simplified version of the number reader from Utf8JsonReader.TryGetNumber,
 155            // because it doesn't need to deal with "NeedsMoreData", or remembering the format.
 156            //
 157            // The Debug.Asserts in this method should change to validated ArgumentExceptions if/when
 158            // writing a formatted number becomes public API.
 28159            Debug.Assert(!utf8FormattedNumber.IsEmpty);
 160
 28161            int i = 0;
 162
 28163            if (utf8FormattedNumber[i] == '-')
 0164            {
 0165                i++;
 166
 0167                if (utf8FormattedNumber.Length <= i)
 0168                {
 0169                    throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 170                }
 0171            }
 172
 28173            if (utf8FormattedNumber[i] == '0')
 0174            {
 0175                i++;
 0176            }
 177            else
 28178            {
 388179                while (i < utf8FormattedNumber.Length && JsonHelpers.IsDigit(utf8FormattedNumber[i]))
 360180                {
 360181                    i++;
 360182                }
 28183            }
 184
 28185            if (i == utf8FormattedNumber.Length)
 28186            {
 28187                return;
 188            }
 189
 190            // The non digit character inside the number
 0191            byte val = utf8FormattedNumber[i];
 192
 0193            if (val == '.')
 0194            {
 0195                i++;
 196
 0197                if (utf8FormattedNumber.Length <= i)
 0198                {
 0199                    throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 200                }
 201
 0202                while (i < utf8FormattedNumber.Length && JsonHelpers.IsDigit(utf8FormattedNumber[i]))
 0203                {
 0204                    i++;
 0205                }
 206
 0207                if (i == utf8FormattedNumber.Length)
 0208                {
 0209                    return;
 210                }
 211
 0212                Debug.Assert(i < utf8FormattedNumber.Length);
 0213                val = utf8FormattedNumber[i];
 0214            }
 215
 0216            if (val == 'e' || val == 'E')
 0217            {
 0218                i++;
 219
 0220                if (utf8FormattedNumber.Length <= i)
 0221                {
 0222                    throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 223                }
 224
 0225                val = utf8FormattedNumber[i];
 226
 0227                if (val == '+' || val == '-')
 0228                {
 0229                    i++;
 0230                }
 0231            }
 232            else
 0233            {
 0234                throw new ArgumentException(
 0235                    SR.Format(SR.ExpectedEndOfDigitNotFound, ThrowHelper.GetPrintableString(val)),
 0236                    nameof(utf8FormattedNumber));
 237            }
 238
 0239            if (utf8FormattedNumber.Length <= i)
 0240            {
 0241                throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 242            }
 243
 0244            while (i < utf8FormattedNumber.Length && JsonHelpers.IsDigit(utf8FormattedNumber[i]))
 0245            {
 0246                i++;
 0247            }
 248
 0249            if (i != utf8FormattedNumber.Length)
 0250            {
 0251                throw new ArgumentException(
 0252                    SR.Format(SR.ExpectedEndOfDigitNotFound, ThrowHelper.GetPrintableString(utf8FormattedNumber[i])),
 0253                    nameof(utf8FormattedNumber));
 254            }
 28255        }
 256
 257#if !NET
 258        private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, t
 259#endif
 260
 261        public static bool IsValidUtf8String(ReadOnlySpan<byte> bytes)
 0262        {
 263#if NET
 0264            return Utf8.IsValid(bytes);
 265#else
 266            try
 267            {
 268                _ = s_utf8Encoding.GetCharCount(bytes);
 269                return true;
 270            }
 271            catch (DecoderFallbackException)
 272            {
 273                return false;
 274            }
 275#endif
 0276        }
 277
 278        internal static OperationStatus ToUtf8(ReadOnlySpan<char> source, Span<byte> destination, out int written)
 0279        {
 280#if NET
 0281            OperationStatus status = Utf8.FromUtf16(source, destination, out int charsRead, out written, replaceInvalidS
 0282            Debug.Assert(status is OperationStatus.Done or OperationStatus.DestinationTooSmall or OperationStatus.Invali
 0283            Debug.Assert(charsRead == source.Length || status is not OperationStatus.Done);
 0284            return status;
 285#else
 286            written = 0;
 287            try
 288            {
 289                written = s_utf8Encoding.GetBytes(source, destination);
 290                return OperationStatus.Done;
 291            }
 292            catch (EncoderFallbackException)
 293            {
 294                return OperationStatus.InvalidData;
 295            }
 296            catch (ArgumentException)
 297            {
 298                return OperationStatus.DestinationTooSmall;
 299            }
 300#endif
 0301        }
 302
 303        internal delegate T WriteCallback<T>(ReadOnlySpan<byte> serializedValue);
 304
 305        internal static T WriteString<T>(ReadOnlySpan<byte> utf8Value, WriteCallback<T> writeCallback)
 0306        {
 0307            int firstByteToEscape = JsonWriterHelper.NeedsEscaping(utf8Value, JavaScriptEncoder.Default);
 308
 0309            if (firstByteToEscape == -1)
 0310            {
 0311                int quotedLength = utf8Value.Length + 2;
 0312                byte[]? rented = null;
 313
 314                try
 0315                {
 0316                    Span<byte> quotedValue = quotedLength > JsonConstants.StackallocByteThreshold
 0317                        ? (rented = ArrayPool<byte>.Shared.Rent(quotedLength)).AsSpan(0, quotedLength)
 0318                        : stackalloc byte[JsonConstants.StackallocByteThreshold].Slice(0, quotedLength);
 319
 0320                    quotedValue[0] = JsonConstants.Quote;
 0321                    utf8Value.CopyTo(quotedValue.Slice(1));
 0322                    quotedValue[quotedValue.Length - 1] = JsonConstants.Quote;
 323
 0324                    return writeCallback(quotedValue);
 325                }
 326                finally
 0327                {
 0328                    if (rented != null)
 0329                    {
 0330                        ArrayPool<byte>.Shared.Return(rented);
 0331                    }
 0332                }
 333            }
 334            else
 0335            {
 0336                Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
 337
 0338                int length = checked(2 + JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstByteToEscape));
 0339                byte[]? rented = null;
 340
 341                try
 0342                {
 343                    scoped Span<byte> escapedValue;
 344
 0345                    if (length > JsonConstants.StackallocByteThreshold)
 0346                    {
 0347                        rented = ArrayPool<byte>.Shared.Rent(length);
 0348                        escapedValue = rented;
 0349                    }
 350                    else
 0351                    {
 0352                        escapedValue = stackalloc byte[JsonConstants.StackallocByteThreshold];
 0353                    }
 354
 0355                    escapedValue[0] = JsonConstants.Quote;
 0356                    JsonWriterHelper.EscapeString(utf8Value, escapedValue.Slice(1), firstByteToEscape, JavaScriptEncoder
 0357                    escapedValue[1 + written] = JsonConstants.Quote;
 358
 0359                    return writeCallback(escapedValue.Slice(0, written + 2));
 360                }
 361                finally
 0362                {
 0363                    if (rented != null)
 0364                    {
 0365                        ArrayPool<byte>.Shared.Return(rented);
 0366                    }
 0367                }
 368            }
 0369        }
 370    }
 371}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Writer\JsonWriterHelper.Date.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;
 8
 9namespace System.Text.Json
 10{
 11    internal static partial class JsonWriterHelper
 12    {
 013        private static readonly StandardFormat s_dateTimeStandardFormat = new StandardFormat('O');
 14
 15        public static void WriteDateTimeTrimmed(Span<byte> buffer, DateTime value, out int bytesWritten)
 016        {
 017            Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
 018            bool result = Utf8Formatter.TryFormat(value, tempSpan, out bytesWritten, s_dateTimeStandardFormat);
 019            Debug.Assert(result);
 020            TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
 021            tempSpan.Slice(0, bytesWritten).CopyTo(buffer);
 022        }
 23
 24        public static void WriteDateTimeOffsetTrimmed(Span<byte> buffer, DateTimeOffset value, out int bytesWritten)
 025        {
 026            Span<byte> tempSpan = stackalloc byte[JsonConstants.MaximumFormatDateTimeOffsetLength];
 027            bool result = Utf8Formatter.TryFormat(value, tempSpan, out bytesWritten, s_dateTimeStandardFormat);
 028            Debug.Assert(result);
 029            TrimDateTimeOffset(tempSpan.Slice(0, bytesWritten), out bytesWritten);
 030            tempSpan.Slice(0, bytesWritten).CopyTo(buffer);
 031        }
 32
 33        //
 34        // Trims roundtrippable DateTime(Offset) input.
 35        // If the milliseconds part of the date is zero, we omit the fraction part of the date,
 36        // else we write the fraction up to 7 decimal places with no trailing zeros. i.e. the output format is
 37        // YYYY-MM-DDThh:mm:ss[.s]TZD where TZD = Z or +-hh:mm.
 38        // e.g.
 39        //   ---------------------------------
 40        //   2017-06-12T05:30:45.768-07:00
 41        //   2017-06-12T05:30:45.00768Z           (Z is short for "+00:00" but also distinguishes DateTimeKind.Utc from 
 42        //   2017-06-12T05:30:45                  (interpreted as local time wrt to current time zone)
 43        public static void TrimDateTimeOffset(Span<byte> buffer, out int bytesWritten)
 044        {
 45            const int maxDateTimeLength = JsonConstants.MaximumFormatDateTimeLength;
 46
 47            // Assert buffer is the right length for:
 48            // YYYY-MM-DDThh:mm:ss.fffffff (JsonConstants.MaximumFormatDateTimeLength)
 49            // YYYY-MM-DDThh:mm:ss.fffffffZ (JsonConstants.MaximumFormatDateTimeLength + 1)
 50            // YYYY-MM-DDThh:mm:ss.fffffff(+|-)hh:mm (JsonConstants.MaximumFormatDateTimeOffsetLength)
 051            Debug.Assert(buffer.Length == maxDateTimeLength ||
 052                buffer.Length == maxDateTimeLength + 1 ||
 053                buffer.Length == JsonConstants.MaximumFormatDateTimeOffsetLength);
 54
 55            // Find the last significant digit.
 56            int curIndex;
 057            if (buffer[maxDateTimeLength - 1] == '0')
 058                if (buffer[maxDateTimeLength - 2] == '0')
 059                    if (buffer[maxDateTimeLength - 3] == '0')
 060                        if (buffer[maxDateTimeLength - 4] == '0')
 061                            if (buffer[maxDateTimeLength - 5] == '0')
 062                                if (buffer[maxDateTimeLength - 6] == '0')
 063                                    if (buffer[maxDateTimeLength - 7] == '0')
 064                                    {
 65                                        // All decimal places are 0 so we can delete the decimal point too.
 066                                        curIndex = maxDateTimeLength - 7 - 1;
 067                                    }
 068                                    else { curIndex = maxDateTimeLength - 6; }
 069                                else { curIndex = maxDateTimeLength - 5; }
 070                            else { curIndex = maxDateTimeLength - 4; }
 071                        else { curIndex = maxDateTimeLength - 3; }
 072                    else { curIndex = maxDateTimeLength - 2; }
 073                else { curIndex = maxDateTimeLength - 1; }
 74            else
 075            {
 76                // There is nothing to trim.
 077                bytesWritten = buffer.Length;
 078                return;
 79            }
 80
 81            // We are either trimming a DateTimeOffset, or a DateTime with
 82            // DateTimeKind.Local or DateTimeKind.Utc
 083            if (buffer.Length == maxDateTimeLength)
 084            {
 85                // There is no offset to copy.
 086                bytesWritten = curIndex;
 087            }
 088            else if (buffer.Length == JsonConstants.MaximumFormatDateTimeOffsetLength)
 089            {
 90                // We have a non-UTC offset (+|-)hh:mm that are 6 characters to copy.
 091                buffer[curIndex] = buffer[maxDateTimeLength];
 092                buffer[curIndex + 1] = buffer[maxDateTimeLength + 1];
 093                buffer[curIndex + 2] = buffer[maxDateTimeLength + 2];
 094                buffer[curIndex + 3] = buffer[maxDateTimeLength + 3];
 095                buffer[curIndex + 4] = buffer[maxDateTimeLength + 4];
 096                buffer[curIndex + 5] = buffer[maxDateTimeLength + 5];
 097                bytesWritten = curIndex + 6;
 098            }
 99            else
 0100            {
 101                // There is a single 'Z'. Just write it at the current index.
 0102                Debug.Assert(buffer[maxDateTimeLength] == 'Z');
 103
 0104                buffer[curIndex] = (byte)'Z';
 0105                bytesWritten = curIndex + 1;
 0106            }
 0107        }
 108    }
 109}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Writer\JsonWriterHelper.Escaping.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.Text.Encodings.Web;
 8
 9#if !NET
 10using System.Runtime.CompilerServices;
 11#endif
 12
 13namespace System.Text.Json
 14{
 15    internal static partial class JsonWriterHelper
 16    {
 17        // Only allow ASCII characters between ' ' (0x20) and '~' (0x7E), inclusively,
 18        // but exclude characters that need to be escaped as hex: '"', '\'', '&', '+', '<', '>', '`'
 19        // and exclude characters that need to be escaped by adding a backslash: '\n', '\r', '\t', '\\', '\b', '\f'
 20        //
 21        // non-zero = allowed, 0 = disallowed
 22        public const int LastAsciiCharacter = 0x7F;
 23        private static ReadOnlySpan<byte> AllowList => // byte.MaxValue + 1
 024        [
 025            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+0000..U+000F
 026            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+0010..U+001F
 027            1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, // U+0020..U+002F
 028            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, // U+0030..U+003F
 029            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // U+0040..U+004F
 030            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // U+0050..U+005F
 031            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // U+0060..U+006F
 032            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // U+0070..U+007F
 033
 034            // Also include the ranges from U+0080 to U+00FF for performance to avoid UTF8 code from checking boundary.
 035            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 036            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 037            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 038            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 039            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 040            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 041            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 042            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+00F0..U+00FF
 043        ];
 44
 45#if NET
 46        private const string HexFormatString = "X4";
 47#endif
 48
 049        private static readonly StandardFormat s_hexStandardFormat = new StandardFormat('X', 4);
 50
 051        private static bool NeedsEscaping(byte value) => AllowList[value] == 0;
 52
 053        private static bool NeedsEscapingNoBoundsCheck(char value) => AllowList[value] == 0;
 54
 55        public static int NeedsEscaping(ReadOnlySpan<byte> value, JavaScriptEncoder? encoder)
 779856        {
 779857            return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncodeUtf8(value);
 779858        }
 59
 60        public static int NeedsEscaping(ReadOnlySpan<char> value, JavaScriptEncoder? encoder)
 061        {
 62            // Some implementations of JavaScriptEncoder.FindFirstCharacterToEncode may not accept
 63            // null pointers and guard against that. Hence, check up-front to return -1.
 064            if (value.IsEmpty)
 065            {
 066                return -1;
 67            }
 68
 69            // Unfortunately, there is no public API for FindFirstCharacterToEncode(Span<char>) yet,
 70            // so we have to use the unsafe FindFirstCharacterToEncode(char*, int) instead.
 71            unsafe
 072            {
 073                fixed (char* ptr = value)
 074                {
 075                    return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncode(ptr, value.Length);
 76                }
 77            }
 078        }
 79
 80        public static int GetMaxEscapedLength(int textLength, int firstIndexToEscape)
 081        {
 082            Debug.Assert(textLength > 0);
 083            Debug.Assert(firstIndexToEscape >= 0 && firstIndexToEscape < textLength);
 084            return firstIndexToEscape + JsonConstants.MaxExpansionFactorWhileEscaping * (textLength - firstIndexToEscape
 085        }
 86
 87        private static void EscapeString(ReadOnlySpan<byte> value, Span<byte> destination, JavaScriptEncoder encoder, re
 088        {
 089            Debug.Assert(encoder != null);
 90
 091            OperationStatus result = encoder.EncodeUtf8(value, destination, out int encoderBytesConsumed, out int encode
 92
 093            Debug.Assert(result != OperationStatus.DestinationTooSmall);
 094            Debug.Assert(result != OperationStatus.NeedMoreData || !isFinalBlock);
 95
 096            if (!(result == OperationStatus.Done || (result == OperationStatus.NeedMoreData && !isFinalBlock)))
 097            {
 098                ThrowHelper.ThrowArgumentException_InvalidUTF8(value.Slice(encoderBytesWritten));
 99            }
 100
 0101            Debug.Assert(encoderBytesConsumed == value.Length || (result == OperationStatus.NeedMoreData && !isFinalBloc
 102
 0103            written += encoderBytesWritten;
 0104            consumed += encoderBytesConsumed;
 0105        }
 106
 107        public static void EscapeString(ReadOnlySpan<byte> value, Span<byte> destination, int indexOfFirstByteToEscape, 
 0108            => EscapeString(value, destination, indexOfFirstByteToEscape, encoder, out _, out written, isFinalBlock: tru
 109
 110        public static void EscapeString(ReadOnlySpan<byte> value, Span<byte> destination, int indexOfFirstByteToEscape, 
 0111        {
 0112            Debug.Assert(indexOfFirstByteToEscape >= 0 && indexOfFirstByteToEscape < value.Length);
 113
 0114            value.Slice(0, indexOfFirstByteToEscape).CopyTo(destination);
 0115            written = indexOfFirstByteToEscape;
 0116            consumed = indexOfFirstByteToEscape;
 117
 0118            if (encoder != null)
 0119            {
 0120                destination = destination.Slice(indexOfFirstByteToEscape);
 0121                value = value.Slice(indexOfFirstByteToEscape);
 0122                EscapeString(value, destination, encoder, ref consumed, ref written, isFinalBlock);
 0123            }
 124            else
 0125            {
 126                // For performance when no encoder is specified, perform escaping here for Ascii and on the
 127                // first occurrence of a non-Ascii character, then call into the default encoder.
 0128                while (indexOfFirstByteToEscape < value.Length)
 0129                {
 0130                    byte val = value[indexOfFirstByteToEscape];
 0131                    if (IsAsciiValue(val))
 0132                    {
 0133                        if (NeedsEscaping(val))
 0134                        {
 0135                            EscapeNextBytes(val, destination, ref written);
 0136                            indexOfFirstByteToEscape++;
 0137                            consumed++;
 0138                        }
 139                        else
 0140                        {
 0141                            destination[written] = val;
 0142                            written++;
 0143                            indexOfFirstByteToEscape++;
 0144                            consumed++;
 0145                        }
 0146                    }
 147                    else
 0148                    {
 149                        // Fall back to default encoder.
 0150                        destination = destination.Slice(written);
 0151                        value = value.Slice(indexOfFirstByteToEscape);
 0152                        EscapeString(value, destination, JavaScriptEncoder.Default, ref consumed, ref written, isFinalBl
 0153                        break;
 154                    }
 0155                }
 0156            }
 0157        }
 158
 159        private static void EscapeNextBytes(byte value, Span<byte> destination, ref int written)
 0160        {
 0161            destination[written++] = (byte)'\\';
 0162            switch (value)
 163            {
 164                case JsonConstants.Quote:
 165                    // Optimize for the common quote case.
 0166                    destination[written++] = (byte)'u';
 0167                    destination[written++] = (byte)'0';
 0168                    destination[written++] = (byte)'0';
 0169                    destination[written++] = (byte)'2';
 0170                    destination[written++] = (byte)'2';
 0171                    break;
 172                case JsonConstants.LineFeed:
 0173                    destination[written++] = (byte)'n';
 0174                    break;
 175                case JsonConstants.CarriageReturn:
 0176                    destination[written++] = (byte)'r';
 0177                    break;
 178                case JsonConstants.Tab:
 0179                    destination[written++] = (byte)'t';
 0180                    break;
 181                case JsonConstants.BackSlash:
 0182                    destination[written++] = (byte)'\\';
 0183                    break;
 184                case JsonConstants.BackSpace:
 0185                    destination[written++] = (byte)'b';
 0186                    break;
 187                case JsonConstants.FormFeed:
 0188                    destination[written++] = (byte)'f';
 0189                    break;
 190                default:
 0191                    destination[written++] = (byte)'u';
 192
 0193                    bool result = Utf8Formatter.TryFormat(value, destination.Slice(written), out int bytesWritten, forma
 0194                    Debug.Assert(result);
 0195                    Debug.Assert(bytesWritten == 4);
 0196                    written += bytesWritten;
 0197                    break;
 198            }
 0199        }
 200
 0201        private static bool IsAsciiValue(byte value) => value <= LastAsciiCharacter;
 202
 0203        private static bool IsAsciiValue(char value) => value <= LastAsciiCharacter;
 204
 205        private static void EscapeString(ReadOnlySpan<char> value, Span<char> destination, JavaScriptEncoder encoder, re
 0206        {
 0207            Debug.Assert(encoder != null);
 208
 0209            OperationStatus result = encoder.Encode(value, destination, out int encoderBytesConsumed, out int encoderCha
 210
 0211            Debug.Assert(result != OperationStatus.DestinationTooSmall);
 0212            Debug.Assert(result != OperationStatus.NeedMoreData || !isFinalBlock);
 213
 0214            if (!(result == OperationStatus.Done || (result == OperationStatus.NeedMoreData && !isFinalBlock)))
 0215            {
 0216                ThrowHelper.ThrowArgumentException_InvalidUTF16(value[encoderCharsWritten]);
 217            }
 218
 0219            Debug.Assert(encoderBytesConsumed == value.Length || (result == OperationStatus.NeedMoreData && !isFinalBloc
 220
 0221            written += encoderCharsWritten;
 0222            consumed += encoderBytesConsumed;
 0223        }
 224
 225        public static void EscapeString(ReadOnlySpan<char> value, Span<char> destination, int indexOfFirstByteToEscape, 
 0226            => EscapeString(value, destination, indexOfFirstByteToEscape, encoder, out _, out written, isFinalBlock: tru
 227
 228        public static void EscapeString(ReadOnlySpan<char> value, Span<char> destination, int indexOfFirstByteToEscape, 
 0229        {
 0230            Debug.Assert(indexOfFirstByteToEscape >= 0 && indexOfFirstByteToEscape < value.Length);
 231
 0232            value.Slice(0, indexOfFirstByteToEscape).CopyTo(destination);
 0233            written = indexOfFirstByteToEscape;
 0234            consumed = indexOfFirstByteToEscape;
 235
 0236            if (encoder != null)
 0237            {
 0238                destination = destination.Slice(indexOfFirstByteToEscape);
 0239                value = value.Slice(indexOfFirstByteToEscape);
 0240                EscapeString(value, destination, encoder, ref consumed, ref written, isFinalBlock);
 0241            }
 242            else
 0243            {
 244                // For performance when no encoder is specified, perform escaping here for Ascii and on the
 245                // first occurrence of a non-Ascii character, then call into the default encoder.
 0246                while (indexOfFirstByteToEscape < value.Length)
 0247                {
 0248                    char val = value[indexOfFirstByteToEscape];
 0249                    if (IsAsciiValue(val))
 0250                    {
 0251                        if (NeedsEscapingNoBoundsCheck(val))
 0252                        {
 0253                            EscapeNextChars(val, destination, ref written);
 0254                            indexOfFirstByteToEscape++;
 0255                            consumed++;
 0256                        }
 257                        else
 0258                        {
 0259                            destination[written] = val;
 0260                            written++;
 0261                            indexOfFirstByteToEscape++;
 0262                            consumed++;
 0263                        }
 0264                    }
 265                    else
 0266                    {
 267                        // Fall back to default encoder.
 0268                        destination = destination.Slice(written);
 0269                        value = value.Slice(indexOfFirstByteToEscape);
 0270                        EscapeString(value, destination, JavaScriptEncoder.Default, ref consumed, ref written, isFinalBl
 0271                        break;
 272                    }
 0273                }
 0274            }
 0275        }
 276
 277        private static void EscapeNextChars(char value, Span<char> destination, ref int written)
 0278        {
 0279            Debug.Assert(IsAsciiValue(value));
 280
 0281            destination[written++] = '\\';
 0282            switch ((byte)value)
 283            {
 284                case JsonConstants.Quote:
 285                    // Optimize for the common quote case.
 0286                    destination[written++] = 'u';
 0287                    destination[written++] = '0';
 0288                    destination[written++] = '0';
 0289                    destination[written++] = '2';
 0290                    destination[written++] = '2';
 0291                    break;
 292                case JsonConstants.LineFeed:
 0293                    destination[written++] = 'n';
 0294                    break;
 295                case JsonConstants.CarriageReturn:
 0296                    destination[written++] = 'r';
 0297                    break;
 298                case JsonConstants.Tab:
 0299                    destination[written++] = 't';
 0300                    break;
 301                case JsonConstants.BackSlash:
 0302                    destination[written++] = '\\';
 0303                    break;
 304                case JsonConstants.BackSpace:
 0305                    destination[written++] = 'b';
 0306                    break;
 307                case JsonConstants.FormFeed:
 0308                    destination[written++] = 'f';
 0309                    break;
 310                default:
 0311                    destination[written++] = 'u';
 312#if NET
 0313                    int intChar = value;
 0314                    intChar.TryFormat(destination.Slice(written), out int charsWritten, HexFormatString);
 0315                    Debug.Assert(charsWritten == 4);
 0316                    written += charsWritten;
 317#else
 318                    written = WriteHex(value, destination, written);
 319#endif
 0320                    break;
 321            }
 0322        }
 323
 324#if !NET
 325        private static int WriteHex(int value, Span<char> destination, int written)
 326        {
 327            destination[written++] = HexConverter.ToCharUpper(value >> 12);
 328            destination[written++] = HexConverter.ToCharUpper(value >> 8);
 329            destination[written++] = HexConverter.ToCharUpper(value >> 4);
 330            destination[written++] = HexConverter.ToCharUpper(value);
 331            return written;
 332        }
 333#endif
 334    }
 335}

Methods/Properties

WriteIndentation(System.Span`1<System.Byte>,System.Int32,System.Byte)
ValidateNewLine(System.String)
ValidateIndentCharacter(System.Char)
ValidateIndentSize(System.Int32)
ValidateProperty(System.ReadOnlySpan`1<System.Byte>)
ValidateValue(System.ReadOnlySpan`1<System.Byte>)
ValidateDouble(System.Double)
ValidateSingle(System.Single)
ValidateProperty(System.ReadOnlySpan`1<System.Char>)
ValidateValue(System.ReadOnlySpan`1<System.Char>)
ValidatePropertyAndValue(System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Byte>)
ValidatePropertyAndValue(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Char>)
ValidatePropertyAndValue(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>)
ValidatePropertyAndValue(System.ReadOnlySpan`1<System.Char>,System.ReadOnlySpan`1<System.Char>)
ValidatePropertyNameLength(System.ReadOnlySpan`1<System.Char>)
ValidatePropertyNameLength(System.ReadOnlySpan`1<System.Byte>)
ValidateNumber(System.ReadOnlySpan`1<System.Byte>)
IsValidUtf8String(System.ReadOnlySpan`1<System.Byte>)
ToUtf8(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Byte>,System.Int32&)
WriteString(System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonWriterHelper/WriteCallback`1<T>)
.cctor()
WriteDateTimeTrimmed(System.Span`1<System.Byte>,System.DateTime,System.Int32&)
WriteDateTimeOffsetTrimmed(System.Span`1<System.Byte>,System.DateTimeOffset,System.Int32&)
TrimDateTimeOffset(System.Span`1<System.Byte>,System.Int32&)
AllowList()
.cctor()
NeedsEscaping(System.Byte)
NeedsEscapingNoBoundsCheck(System.Char)
NeedsEscaping(System.ReadOnlySpan`1<System.Byte>,System.Text.Encodings.Web.JavaScriptEncoder)
NeedsEscaping(System.ReadOnlySpan`1<System.Char>,System.Text.Encodings.Web.JavaScriptEncoder)
GetMaxEscapedLength(System.Int32,System.Int32)
EscapeString(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Text.Encodings.Web.JavaScriptEncoder,System.Int32&,System.Int32&,System.Boolean)
EscapeString(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32,System.Text.Encodings.Web.JavaScriptEncoder,System.Int32&)
EscapeString(System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32,System.Text.Encodings.Web.JavaScriptEncoder,System.Int32&,System.Int32&,System.Boolean)
EscapeNextBytes(System.Byte,System.Span`1<System.Byte>,System.Int32&)
IsAsciiValue(System.Byte)
IsAsciiValue(System.Char)
EscapeString(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Char>,System.Text.Encodings.Web.JavaScriptEncoder,System.Int32&,System.Int32&,System.Boolean)
EscapeString(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Char>,System.Int32,System.Text.Encodings.Web.JavaScriptEncoder,System.Int32&)
EscapeString(System.ReadOnlySpan`1<System.Char>,System.Span`1<System.Char>,System.Int32,System.Text.Encodings.Web.JavaScriptEncoder,System.Int32&,System.Int32&,System.Boolean)
EscapeNextChars(System.Char,System.Span`1<System.Char>,System.Int32&)