< Summary

Line coverage
9%
Covered lines: 44
Uncovered lines: 444
Coverable lines: 488
Total lines: 864
Line coverage: 9%
Branch coverage
3%
Covered branches: 8
Total branches: 212
Branch coverage: 3.7%
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%440%
File 1: ValidateIndentCharacter(...)0%440%
File 1: ValidateIndentSize(...)0%440%
File 1: ValidateProperty(...)0%220%
File 1: ValidateValue(...)0%220%
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(...)0%40400%
File 1: IsValidUtf8String(...)100%110%
File 1: ToUtf8(...)0%660%
File 1: WriteString(...)0%10100%
File 2: .cctor()100%11100%
File 2: WriteDateTimeTrimmed(...)100%110%
File 2: WriteDateTimeOffsetTrimmed(...)100%110%
File 2: TrimDateTimeOffset(...)0%22220%
File 3: .cctor()100%22100%
File 3: NeedsEscaping(...)100%110%
File 3: NeedsEscapingNoBoundsCheck(...)100%110%
File 3: NeedsEscaping(...)83.33%66100%
File 3: NeedsEscaping(...)0%880%
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)

D:\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.Unicode;
 8
 9namespace System.Text.Json
 10{
 11    internal static partial class JsonWriterHelper
 12    {
 13        public static void WriteIndentation(Span<byte> buffer, int indent, byte indentByte)
 014        {
 015            Debug.Assert(buffer.Length >= indent);
 16
 17            // Based on perf tests, the break-even point where vectorized Fill is faster
 18            // than explicitly writing the space in a loop is 8.
 019            if (indent < 8)
 020            {
 021                int i = 0;
 022                while (i + 1 < indent)
 023                {
 024                    buffer[i++] = indentByte;
 025                    buffer[i++] = indentByte;
 026                }
 27
 028                if (i < indent)
 029                {
 030                    buffer[i] = indentByte;
 031                }
 032            }
 33            else
 034            {
 035                buffer.Slice(0, indent).Fill(indentByte);
 036            }
 037        }
 38
 39        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 40        public static void ValidateNewLine(string value)
 041        {
 042            ArgumentNullException.ThrowIfNull(value);
 43
 044            if (value is not JsonConstants.NewLineLineFeed and not JsonConstants.NewLineCarriageReturnLineFeed)
 045                ThrowHelper.ThrowArgumentOutOfRangeException_NewLine(nameof(value));
 046        }
 47
 48        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 49        public static void ValidateIndentCharacter(char value)
 050        {
 051            if (value is not JsonConstants.DefaultIndentCharacter and not JsonConstants.TabIndentCharacter)
 052                ThrowHelper.ThrowArgumentOutOfRangeException_IndentCharacter(nameof(value));
 053        }
 54
 55        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 56        public static void ValidateIndentSize(int value)
 057        {
 058            if (value is < JsonConstants.MinimumIndentSize or > JsonConstants.MaximumIndentSize)
 059                ThrowHelper.ThrowArgumentOutOfRangeException_IndentSize(nameof(value), JsonConstants.MinimumIndentSize, 
 060        }
 61
 62        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 63        public static void ValidateProperty(ReadOnlySpan<byte> propertyName)
 064        {
 065            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize)
 066                ThrowHelper.ThrowArgumentException_PropertyNameTooLarge(propertyName.Length);
 067        }
 68
 69        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 70        public static void ValidateValue(ReadOnlySpan<byte> value)
 071        {
 072            if (value.Length > JsonConstants.MaxUnescapedTokenSize)
 073                ThrowHelper.ThrowArgumentException_ValueTooLarge(value.Length);
 074        }
 75
 76        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 77        public static void ValidateDouble(double value)
 078        {
 079            if (!double.IsFinite(value))
 080            {
 081                ThrowHelper.ThrowArgumentException_ValueNotSupported();
 82            }
 083        }
 84
 85        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 86        public static void ValidateSingle(float value)
 087        {
 088            if (!float.IsFinite(value))
 089            {
 090                ThrowHelper.ThrowArgumentException_ValueNotSupported();
 91            }
 092        }
 93
 94        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 95        public static void ValidateProperty(ReadOnlySpan<char> propertyName)
 096        {
 097            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize)
 098                ThrowHelper.ThrowArgumentException_PropertyNameTooLarge(propertyName.Length);
 099        }
 100
 101        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 102        public static void ValidateValue(ReadOnlySpan<char> value)
 4103        {
 4104            if (value.Length > JsonConstants.MaxCharacterTokenSize)
 0105                ThrowHelper.ThrowArgumentException_ValueTooLarge(value.Length);
 4106        }
 107
 108        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 109        public static void ValidatePropertyAndValue(ReadOnlySpan<char> propertyName, ReadOnlySpan<byte> value)
 0110        {
 0111            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize || value.Length > JsonConstants.MaxUnescapedTo
 0112                ThrowHelper.ThrowArgumentException(propertyName, value);
 0113        }
 114
 115        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 116        public static void ValidatePropertyAndValue(ReadOnlySpan<byte> propertyName, ReadOnlySpan<char> value)
 0117        {
 0118            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize || value.Length > JsonConstants.MaxCharacterTo
 0119                ThrowHelper.ThrowArgumentException(propertyName, value);
 0120        }
 121
 122        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 123        public static void ValidatePropertyAndValue(ReadOnlySpan<byte> propertyName, ReadOnlySpan<byte> value)
 0124        {
 0125            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize || value.Length > JsonConstants.MaxUnescapedTo
 0126                ThrowHelper.ThrowArgumentException(propertyName, value);
 0127        }
 128
 129        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 130        public static void ValidatePropertyAndValue(ReadOnlySpan<char> propertyName, ReadOnlySpan<char> value)
 0131        {
 0132            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize || value.Length > JsonConstants.MaxCharacterTo
 0133                ThrowHelper.ThrowArgumentException(propertyName, value);
 0134        }
 135
 136        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 137        public static void ValidatePropertyNameLength(ReadOnlySpan<char> propertyName)
 0138        {
 0139            if (propertyName.Length > JsonConstants.MaxCharacterTokenSize)
 0140                ThrowHelper.ThrowPropertyNameTooLargeArgumentException(propertyName.Length);
 0141        }
 142
 143        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 144        public static void ValidatePropertyNameLength(ReadOnlySpan<byte> propertyName)
 0145        {
 0146            if (propertyName.Length > JsonConstants.MaxUnescapedTokenSize)
 0147                ThrowHelper.ThrowPropertyNameTooLargeArgumentException(propertyName.Length);
 0148        }
 149
 150        internal static void ValidateNumber(ReadOnlySpan<byte> utf8FormattedNumber)
 0151        {
 152            // This is a simplified version of the number reader from Utf8JsonReader.TryGetNumber,
 153            // because it doesn't need to deal with "NeedsMoreData", or remembering the format.
 154            //
 155            // The Debug.Asserts in this method should change to validated ArgumentExceptions if/when
 156            // writing a formatted number becomes public API.
 0157            Debug.Assert(!utf8FormattedNumber.IsEmpty);
 158
 0159            int i = 0;
 160
 0161            if (utf8FormattedNumber[i] == '-')
 0162            {
 0163                i++;
 164
 0165                if (utf8FormattedNumber.Length <= i)
 0166                {
 0167                    throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 168                }
 0169            }
 170
 0171            if (utf8FormattedNumber[i] == '0')
 0172            {
 0173                i++;
 0174            }
 175            else
 0176            {
 0177                while (i < utf8FormattedNumber.Length && JsonHelpers.IsDigit(utf8FormattedNumber[i]))
 0178                {
 0179                    i++;
 0180                }
 0181            }
 182
 0183            if (i == utf8FormattedNumber.Length)
 0184            {
 0185                return;
 186            }
 187
 188            // The non digit character inside the number
 0189            byte val = utf8FormattedNumber[i];
 190
 0191            if (val == '.')
 0192            {
 0193                i++;
 194
 0195                if (utf8FormattedNumber.Length <= i)
 0196                {
 0197                    throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 198                }
 199
 0200                while (i < utf8FormattedNumber.Length && JsonHelpers.IsDigit(utf8FormattedNumber[i]))
 0201                {
 0202                    i++;
 0203                }
 204
 0205                if (i == utf8FormattedNumber.Length)
 0206                {
 0207                    return;
 208                }
 209
 0210                Debug.Assert(i < utf8FormattedNumber.Length);
 0211                val = utf8FormattedNumber[i];
 0212            }
 213
 0214            if (val is (byte)'e' or (byte)'E')
 0215            {
 0216                i++;
 217
 0218                if (utf8FormattedNumber.Length <= i)
 0219                {
 0220                    throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 221                }
 222
 0223                val = utf8FormattedNumber[i];
 224
 0225                if (val is (byte)'+' or (byte)'-')
 0226                {
 0227                    i++;
 0228                }
 0229            }
 230            else
 0231            {
 0232                throw new ArgumentException(
 0233                    SR.Format(SR.ExpectedEndOfDigitNotFound, ThrowHelper.GetPrintableString(val)),
 0234                    nameof(utf8FormattedNumber));
 235            }
 236
 0237            if (utf8FormattedNumber.Length <= i)
 0238            {
 0239                throw new ArgumentException(SR.RequiredDigitNotFoundEndOfData, nameof(utf8FormattedNumber));
 240            }
 241
 0242            while (i < utf8FormattedNumber.Length && JsonHelpers.IsDigit(utf8FormattedNumber[i]))
 0243            {
 0244                i++;
 0245            }
 246
 0247            if (i != utf8FormattedNumber.Length)
 0248            {
 0249                throw new ArgumentException(
 0250                    SR.Format(SR.ExpectedEndOfDigitNotFound, ThrowHelper.GetPrintableString(utf8FormattedNumber[i])),
 0251                    nameof(utf8FormattedNumber));
 252            }
 0253        }
 254
 255#if !NET
 256        private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, t
 257#endif
 258
 259        public static bool IsValidUtf8String(ReadOnlySpan<byte> bytes)
 0260        {
 261#if NET
 0262            return Utf8.IsValid(bytes);
 263#else
 264            try
 265            {
 266                _ = s_utf8Encoding.GetCharCount(bytes);
 267                return true;
 268            }
 269            catch (DecoderFallbackException)
 270            {
 271                return false;
 272            }
 273#endif
 0274        }
 275
 276        internal static OperationStatus ToUtf8(ReadOnlySpan<char> source, Span<byte> destination, out int written)
 0277        {
 278#if NET
 0279            OperationStatus status = Utf8.FromUtf16(source, destination, out int charsRead, out written, replaceInvalidS
 0280            Debug.Assert(status is OperationStatus.Done or OperationStatus.DestinationTooSmall or OperationStatus.Invali
 0281            Debug.Assert(charsRead == source.Length || status is not OperationStatus.Done);
 0282            return status;
 283#else
 284            written = 0;
 285            try
 286            {
 287                written = s_utf8Encoding.GetBytes(source, destination);
 288                return OperationStatus.Done;
 289            }
 290            catch (EncoderFallbackException)
 291            {
 292                return OperationStatus.InvalidData;
 293            }
 294            catch (ArgumentException)
 295            {
 296                return OperationStatus.DestinationTooSmall;
 297            }
 298#endif
 0299        }
 300
 301        internal delegate T WriteCallback<T>(ReadOnlySpan<byte> serializedValue);
 302
 303        internal static unsafe T WriteString<T>(ReadOnlySpan<byte> utf8Value, WriteCallback<T> writeCallback)
 0304        {
 0305            int firstByteToEscape = JsonWriterHelper.NeedsEscaping(utf8Value, encoder: null);
 306
 0307            if (firstByteToEscape == -1)
 0308            {
 0309                int quotedLength = utf8Value.Length + 2;
 0310                byte[]? rented = null;
 311
 312                try
 0313                {
 0314                    Span<byte> quotedValue = quotedLength > JsonConstants.StackallocByteThreshold
 0315                        ? (rented = ArrayPool<byte>.Shared.Rent(quotedLength)).AsSpan(0, quotedLength)
 0316                        : stackalloc byte[JsonConstants.StackallocByteThreshold].Slice(0, quotedLength);
 317
 0318                    quotedValue[0] = JsonConstants.Quote;
 0319                    utf8Value.CopyTo(quotedValue.Slice(1));
 0320                    quotedValue[quotedValue.Length - 1] = JsonConstants.Quote;
 321
 0322                    return writeCallback(quotedValue);
 323                }
 324                finally
 0325                {
 0326                    if (rented is not null)
 0327                    {
 0328                        ArrayPool<byte>.Shared.Return(rented);
 0329                    }
 0330                }
 331            }
 332            else
 0333            {
 0334                Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
 335
 0336                int length = checked(2 + JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstByteToEscape));
 0337                byte[]? rented = null;
 338
 339                try
 0340                {
 341                    scoped Span<byte> escapedValue;
 342
 0343                    if (length > JsonConstants.StackallocByteThreshold)
 0344                    {
 0345                        rented = ArrayPool<byte>.Shared.Rent(length);
 0346                        escapedValue = rented;
 0347                    }
 348                    else
 0349                    {
 0350                        escapedValue = stackalloc byte[JsonConstants.StackallocByteThreshold];
 0351                    }
 352
 0353                    escapedValue[0] = JsonConstants.Quote;
 0354                    JsonWriterHelper.EscapeString(utf8Value, escapedValue.Slice(1), firstByteToEscape, encoder: null, ou
 0355                    escapedValue[1 + written] = JsonConstants.Quote;
 356
 0357                    return writeCallback(escapedValue.Slice(0, written + 2));
 358                }
 359                finally
 0360                {
 0361                    if (rented is not null)
 0362                    {
 0363                        ArrayPool<byte>.Shared.Return(rented);
 0364                    }
 0365                }
 366            }
 0367        }
 368    }
 369}

D:\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    {
 113        private static readonly StandardFormat s_dateTimeStandardFormat = new StandardFormat('O');
 14
 15        public static unsafe 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 unsafe void WriteDateTimeOffsetTrimmed(Span<byte> buffer, DateTimeOffset value, out int bytesWritt
 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}

D:\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        // This exactly matches the set used by JavaScriptEncoder.Default.
 23        // SearchValues instances below also represent the same ASCII set, validated to match in the debug static ctor b
 24        public const int LastAsciiCharacter = 0x7F;
 25        private static ReadOnlySpan<byte> AllowList => // byte.MaxValue + 1
 12826        [
 12827            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+0000..U+000F
 12828            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+0010..U+001F
 12829            1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, // U+0020..U+002F
 12830            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, // U+0030..U+003F
 12831            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // U+0040..U+004F
 12832            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // U+0050..U+005F
 12833            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // U+0060..U+006F
 12834            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // U+0070..U+007F
 12835
 12836            // Also include the ranges from U+0080 to U+00FF for performance to avoid UTF8 code from checking boundary.
 12837            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12838            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12839            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12840            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12841            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12842            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12843            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 12844            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+00F0..U+00FF
 12845        ];
 46
 47#if DEBUG && NET
 48        static JsonWriterHelper()
 149        {
 25850            for (int i = 0; i < 128; i++)
 12851            {
 12852                bool needsEscaping = AllowList[i] == 0;
 12853                Debug.Assert(needsEscaping == JavaScriptEncoder.Default.WillEncode(i));
 12854                Debug.Assert(needsEscaping == !s_allowedBytes.Contains((byte)i));
 12855                Debug.Assert(needsEscaping == !s_allowedChars.Contains((char)i));
 12856            }
 157        }
 58#endif
 59
 60#if NET
 61        private const string HexFormatString = "X4";
 62#endif
 63
 164        private static readonly StandardFormat s_hexStandardFormat = new StandardFormat('X', 4);
 65
 066        private static bool NeedsEscaping(byte value) => AllowList[value] == 0;
 67
 068        private static bool NeedsEscapingNoBoundsCheck(char value) => AllowList[value] == 0;
 69
 70#if NET
 71        // The characters allowed by AllowList, used to vectorize the default (no custom encoder) escaping
 72        // scan directly instead of routing through System.Text.Encodings.Web.
 73        // Validated to match the AllowList and JavaScriptEncoder.Default in the static ctor above.
 174        private static readonly SearchValues<byte> s_allowedBytes = SearchValues.Create(
 175            " !#$%()*,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"u8);
 176        private static readonly SearchValues<char> s_allowedChars = SearchValues.Create(
 177            " !#$%()*,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~");
 78#endif
 79
 80        public static int NeedsEscaping(ReadOnlySpan<byte> value, JavaScriptEncoder? encoder)
 62881        {
 82#if NET
 62883            if (encoder is null || ReferenceEquals(encoder, JavaScriptEncoder.Default))
 23284            {
 23285                return value.IndexOfAnyExcept(s_allowedBytes);
 86            }
 87#endif
 88
 39689            return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncodeUtf8(value);
 62890        }
 91
 92        public static int NeedsEscaping(ReadOnlySpan<char> value, JavaScriptEncoder? encoder)
 093        {
 94#if NET
 095            if (encoder is null || ReferenceEquals(encoder, JavaScriptEncoder.Default))
 096            {
 097                return value.IndexOfAnyExcept(s_allowedChars);
 98            }
 99#endif
 100
 101            // Some implementations of JavaScriptEncoder.FindFirstCharacterToEncode may not accept
 102            // null pointers and guard against that. Hence, check up-front to return -1.
 0103            if (value.IsEmpty)
 0104            {
 0105                return -1;
 106            }
 107
 108            // Unfortunately, there is no public API for FindFirstCharacterToEncode(Span<char>) yet,
 109            // so we have to use the unsafe FindFirstCharacterToEncode(char*, int) instead.
 110            unsafe
 0111            {
 0112                fixed (char* ptr = value)
 0113                {
 0114                    return (encoder ?? JavaScriptEncoder.Default).FindFirstCharacterToEncode(ptr, value.Length);
 115                }
 116            }
 0117        }
 118
 119        public static int GetMaxEscapedLength(int textLength, int firstIndexToEscape)
 0120        {
 0121            Debug.Assert(textLength > 0);
 0122            Debug.Assert(firstIndexToEscape >= 0 && firstIndexToEscape < textLength);
 0123            return firstIndexToEscape + JsonConstants.MaxExpansionFactorWhileEscaping * (textLength - firstIndexToEscape
 0124        }
 125
 126        private static void EscapeString(ReadOnlySpan<byte> value, Span<byte> destination, JavaScriptEncoder encoder, re
 0127        {
 0128            Debug.Assert(encoder is not null);
 129
 0130            OperationStatus result = encoder.EncodeUtf8(value, destination, out int encoderBytesConsumed, out int encode
 131
 0132            Debug.Assert(result != OperationStatus.DestinationTooSmall);
 0133            Debug.Assert(result != OperationStatus.NeedMoreData || !isFinalBlock);
 134
 0135            if (!(result == OperationStatus.Done || (result == OperationStatus.NeedMoreData && !isFinalBlock)))
 0136            {
 0137                ThrowHelper.ThrowArgumentException_InvalidUTF8(value.Slice(encoderBytesWritten));
 138            }
 139
 0140            Debug.Assert(encoderBytesConsumed == value.Length || (result == OperationStatus.NeedMoreData && !isFinalBloc
 141
 0142            written += encoderBytesWritten;
 0143            consumed += encoderBytesConsumed;
 0144        }
 145
 146        public static void EscapeString(ReadOnlySpan<byte> value, Span<byte> destination, int indexOfFirstByteToEscape, 
 0147            => EscapeString(value, destination, indexOfFirstByteToEscape, encoder, out _, out written, isFinalBlock: tru
 148
 149        public static void EscapeString(ReadOnlySpan<byte> value, Span<byte> destination, int indexOfFirstByteToEscape, 
 0150        {
 0151            Debug.Assert(indexOfFirstByteToEscape >= 0 && indexOfFirstByteToEscape < value.Length);
 152
 0153            value.Slice(0, indexOfFirstByteToEscape).CopyTo(destination);
 0154            written = indexOfFirstByteToEscape;
 0155            consumed = indexOfFirstByteToEscape;
 156
 0157            if (encoder is not null)
 0158            {
 0159                destination = destination.Slice(indexOfFirstByteToEscape);
 0160                value = value.Slice(indexOfFirstByteToEscape);
 0161                EscapeString(value, destination, encoder, ref consumed, ref written, isFinalBlock);
 0162            }
 163            else
 0164            {
 165                // For performance when no encoder is specified, perform escaping here for Ascii and on the
 166                // first occurrence of a non-Ascii character, then call into the default encoder.
 0167                while (indexOfFirstByteToEscape < value.Length)
 0168                {
 0169                    byte val = value[indexOfFirstByteToEscape];
 0170                    if (IsAsciiValue(val))
 0171                    {
 0172                        if (NeedsEscaping(val))
 0173                        {
 0174                            EscapeNextBytes(val, destination, ref written);
 0175                            indexOfFirstByteToEscape++;
 0176                            consumed++;
 0177                        }
 178                        else
 0179                        {
 0180                            destination[written] = val;
 0181                            written++;
 0182                            indexOfFirstByteToEscape++;
 0183                            consumed++;
 0184                        }
 0185                    }
 186                    else
 0187                    {
 188                        // Fall back to default encoder.
 0189                        destination = destination.Slice(written);
 0190                        value = value.Slice(indexOfFirstByteToEscape);
 0191                        EscapeString(value, destination, JavaScriptEncoder.Default, ref consumed, ref written, isFinalBl
 0192                        break;
 193                    }
 0194                }
 0195            }
 0196        }
 197
 198        private static void EscapeNextBytes(byte value, Span<byte> destination, ref int totalWritten)
 0199        {
 200            // Slice the span so the JIT can see that no bounds checks are needed below
 0201            destination = destination.Slice(totalWritten, JsonConstants.MaxExpansionFactorWhileEscaping);
 0202            int written = 0;
 203
 0204            destination[written++] = (byte)'\\';
 0205            switch (value)
 206            {
 207                case JsonConstants.Quote:
 208                    // Optimize for the common quote case.
 0209                    destination[written++] = (byte)'u';
 0210                    destination[written++] = (byte)'0';
 0211                    destination[written++] = (byte)'0';
 0212                    destination[written++] = (byte)'2';
 0213                    destination[written++] = (byte)'2';
 0214                    break;
 215                case JsonConstants.LineFeed:
 0216                    destination[written++] = (byte)'n';
 0217                    break;
 218                case JsonConstants.CarriageReturn:
 0219                    destination[written++] = (byte)'r';
 0220                    break;
 221                case JsonConstants.Tab:
 0222                    destination[written++] = (byte)'t';
 0223                    break;
 224                case JsonConstants.BackSlash:
 0225                    destination[written++] = (byte)'\\';
 0226                    break;
 227                case JsonConstants.BackSpace:
 0228                    destination[written++] = (byte)'b';
 0229                    break;
 230                case JsonConstants.FormFeed:
 0231                    destination[written++] = (byte)'f';
 0232                    break;
 233                default:
 0234                    destination[written++] = (byte)'u';
 235
 0236                    bool result = Utf8Formatter.TryFormat(value, destination.Slice(written), out int bytesWritten, forma
 0237                    Debug.Assert(result);
 0238                    Debug.Assert(bytesWritten == 4);
 0239                    written += bytesWritten;
 0240                    break;
 241            }
 242
 0243            totalWritten += written;
 0244        }
 245
 0246        private static bool IsAsciiValue(byte value) => value <= LastAsciiCharacter;
 247
 0248        private static bool IsAsciiValue(char value) => value <= LastAsciiCharacter;
 249
 250        private static void EscapeString(ReadOnlySpan<char> value, Span<char> destination, JavaScriptEncoder encoder, re
 0251        {
 0252            Debug.Assert(encoder is not null);
 253
 0254            OperationStatus result = encoder.Encode(value, destination, out int encoderBytesConsumed, out int encoderCha
 255
 0256            Debug.Assert(result != OperationStatus.DestinationTooSmall);
 0257            Debug.Assert(result != OperationStatus.NeedMoreData || !isFinalBlock);
 258
 0259            if (!(result == OperationStatus.Done || (result == OperationStatus.NeedMoreData && !isFinalBlock)))
 0260            {
 0261                ThrowHelper.ThrowArgumentException_InvalidUTF16(value[encoderCharsWritten]);
 262            }
 263
 0264            Debug.Assert(encoderBytesConsumed == value.Length || (result == OperationStatus.NeedMoreData && !isFinalBloc
 265
 0266            written += encoderCharsWritten;
 0267            consumed += encoderBytesConsumed;
 0268        }
 269
 270        public static void EscapeString(ReadOnlySpan<char> value, Span<char> destination, int indexOfFirstByteToEscape, 
 0271            => EscapeString(value, destination, indexOfFirstByteToEscape, encoder, out _, out written, isFinalBlock: tru
 272
 273        public static void EscapeString(ReadOnlySpan<char> value, Span<char> destination, int indexOfFirstByteToEscape, 
 0274        {
 0275            Debug.Assert(indexOfFirstByteToEscape >= 0 && indexOfFirstByteToEscape < value.Length);
 276
 0277            value.Slice(0, indexOfFirstByteToEscape).CopyTo(destination);
 0278            written = indexOfFirstByteToEscape;
 0279            consumed = indexOfFirstByteToEscape;
 280
 0281            if (encoder is not null)
 0282            {
 0283                destination = destination.Slice(indexOfFirstByteToEscape);
 0284                value = value.Slice(indexOfFirstByteToEscape);
 0285                EscapeString(value, destination, encoder, ref consumed, ref written, isFinalBlock);
 0286            }
 287            else
 0288            {
 289                // For performance when no encoder is specified, perform escaping here for Ascii and on the
 290                // first occurrence of a non-Ascii character, then call into the default encoder.
 0291                while (indexOfFirstByteToEscape < value.Length)
 0292                {
 0293                    char val = value[indexOfFirstByteToEscape];
 0294                    if (IsAsciiValue(val))
 0295                    {
 0296                        if (NeedsEscapingNoBoundsCheck(val))
 0297                        {
 0298                            EscapeNextChars(val, destination, ref written);
 0299                            indexOfFirstByteToEscape++;
 0300                            consumed++;
 0301                        }
 302                        else
 0303                        {
 0304                            destination[written] = val;
 0305                            written++;
 0306                            indexOfFirstByteToEscape++;
 0307                            consumed++;
 0308                        }
 0309                    }
 310                    else
 0311                    {
 312                        // Fall back to default encoder.
 0313                        destination = destination.Slice(written);
 0314                        value = value.Slice(indexOfFirstByteToEscape);
 0315                        EscapeString(value, destination, JavaScriptEncoder.Default, ref consumed, ref written, isFinalBl
 0316                        break;
 317                    }
 0318                }
 0319            }
 0320        }
 321
 322        private static void EscapeNextChars(char value, Span<char> destination, ref int totalWritten)
 0323        {
 0324            Debug.Assert(IsAsciiValue(value));
 325
 326            // Slice the span so the JIT can see that no bounds checks are needed below
 0327            destination = destination.Slice(totalWritten, JsonConstants.MaxExpansionFactorWhileEscaping);
 0328            int written = 0;
 329
 0330            destination[written++] = '\\';
 0331            switch ((byte)value)
 332            {
 333                case JsonConstants.Quote:
 334                    // Optimize for the common quote case.
 0335                    destination[written++] = 'u';
 0336                    destination[written++] = '0';
 0337                    destination[written++] = '0';
 0338                    destination[written++] = '2';
 0339                    destination[written++] = '2';
 0340                    break;
 341                case JsonConstants.LineFeed:
 0342                    destination[written++] = 'n';
 0343                    break;
 344                case JsonConstants.CarriageReturn:
 0345                    destination[written++] = 'r';
 0346                    break;
 347                case JsonConstants.Tab:
 0348                    destination[written++] = 't';
 0349                    break;
 350                case JsonConstants.BackSlash:
 0351                    destination[written++] = '\\';
 0352                    break;
 353                case JsonConstants.BackSpace:
 0354                    destination[written++] = 'b';
 0355                    break;
 356                case JsonConstants.FormFeed:
 0357                    destination[written++] = 'f';
 0358                    break;
 359                default:
 0360                    destination[written++] = 'u';
 361#if NET
 0362                    int intChar = value;
 0363                    intChar.TryFormat(destination.Slice(written), out int charsWritten, HexFormatString);
 0364                    Debug.Assert(charsWritten == 4);
 0365                    written += charsWritten;
 366#else
 367                    written = WriteHex(value, destination, written);
 368#endif
 0369                    break;
 370            }
 371
 0372            totalWritten += written;
 0373        }
 374
 375#if !NET
 376        private static int WriteHex(int value, Span<char> destination, int written)
 377        {
 378            destination[written++] = HexConverter.ToCharUpper(value >> 12);
 379            destination[written++] = HexConverter.ToCharUpper(value >> 8);
 380            destination[written++] = HexConverter.ToCharUpper(value >> 4);
 381            destination[written++] = HexConverter.ToCharUpper(value);
 382            return written;
 383        }
 384#endif
 385    }
 386}

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