< Summary

Line coverage
24%
Covered lines: 972
Uncovered lines: 3025
Coverable lines: 3997
Total lines: 6599
Line coverage: 24.3%
Branch coverage
23%
Covered branches: 410
Total branches: 1759
Branch coverage: 23.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .ctor(...)50%22100%
File 1: .ctor(...)100%110%
File 1: Read()90%1010100%
File 1: Skip()0%220%
File 1: SkipHelper()0%880%
File 1: TrySkip()0%440%
File 1: TrySkipPartial(...)0%16160%
File 1: ValueTextEquals(...)0%220%
File 1: ValueTextEquals(...)100%110%
File 1: TextEqualsHelper(...)0%440%
File 1: ValueTextEquals(...)0%10100%
File 1: CompareToSequence(...)0%880%
File 1: UnescapeAndCompare(...)0%660%
File 1: UnescapeSequenceAndCompare(...)0%14140%
File 1: IsTokenTypeString(...)0%220%
File 1: MatchNotPossible(...)0%880%
File 1: MatchNotPossibleSequence(...)0%660%
File 1: StartObject()100%22100%
File 1: EndObject()62.5%8857.14%
File 1: StartArray()100%22100%
File 1: EndArray()62.5%8857.14%
File 1: UpdateBitStackOnEndToken()100%11100%
File 1: ReadSingleSegment()95.83%242490.14%
File 1: HasMoreData()81.25%161677.77%
File 1: HasMoreData(...)75%4488.88%
File 1: ReadFirstToken(...)92.85%141494.73%
File 1: SkipWhiteSpace()33.33%181853.84%
File 1: ConsumeValue(...)72.91%484889.06%
File 1: ConsumeLiteral(...)100%66100%
File 1: CheckLiteral(...)75%121284%
File 1: ThrowInvalidLiteral(...)100%44100%
File 1: ConsumeNumber()83.33%121290.47%
File 1: ConsumePropertyName()70%101076%
File 1: ConsumeString()83.33%6696%
File 1: ConsumeStringAndValidate(...)92.3%262695.08%
File 1: ValidateHexDigits(...)100%66100%
File 1: TryGetNumber(...)83.33%424285%
File 1: ConsumeNegativeSign(...)87.5%8895%
File 1: ConsumeZero(...)92.85%141492%
File 1: ConsumeIntegerDigits(...)90%101090.47%
File 1: ConsumeDecimalDigits(...)83.33%6693.75%
File 1: ConsumeSign(...)85.71%141492.59%
File 1: ConsumeNextTokenOrRollback(...)75%4465%
File 1: ConsumeNextToken(...)65.38%525279.26%
File 1: ConsumeNextTokenFromLastNonCommentToken()0%86860%
File 1: SkipAllComments(...)90%101092%
File 1: SkipAllComments(...)70%101068%
File 1: ConsumeNextTokenUntilAfterAllCommentsAreSkipped(...)81.03%585880.86%
File 1: SkipComment()87.5%8894.44%
File 1: SkipSingleLineComment(...)83.33%121289.18%
File 1: FindLineSeparator(...)100%44100%
File 1: ThrowOnDangerousLineSeparator(...)100%88100%
File 1: SkipMultiLineComment(...)90%101096.55%
File 1: ConsumeComment()0%880%
File 1: ConsumeSingleLineComment(...)0%440%
File 1: ConsumeMultiLineComment(...)0%440%
File 1: GetUnescapedSpan()0%440%
File 2: .ctor(...)0%10100%
File 2: .ctor(...)100%110%
File 2: ReadMultiSegment()0%24240%
File 2: ValidateStateAtEndOfData()0%12120%
File 2: HasMoreDataMultiSegment()0%14140%
File 2: HasMoreDataMultiSegment(...)0%880%
File 2: GetNextSpan()0%10100%
File 2: ReadFirstTokenMultiSegment(...)0%14140%
File 2: SkipWhiteSpaceMultiSegment()0%440%
File 2: ConsumeValueMultiSegment(...)0%60600%
File 2: ConsumeLiteralMultiSegment(...)0%880%
File 2: CheckLiteralMultiSegment(...)0%18180%
File 2: AmountToWrite(System.ReadOnlySpan`1<System.Byte>,System.Int64,System.ReadOnlySpan`1<System.Byte>,System.Int32)100%110%
File 2: FindMismatch(...)0%220%
File 2: GetInvalidLiteralMultiSegment(...)0%440%
File 2: ConsumeNumberMultiSegment()0%12120%
File 2: ConsumePropertyNameMultiSegment()0%10100%
File 2: ConsumeStringMultiSegment()0%660%
File 2: ConsumeStringNextSegment()0%38380%
File 2: ConsumeStringAndValidateMultiSegment(...)0%40400%
File 2: RollBackState(...)0%220%
File 2: TryGetNumberMultiSegment(...)0%44440%
File 2: ConsumeNegativeSignMultiSegment(...)0%12120%
File 2: ConsumeZeroMultiSegment(...)0%22220%
File 2: ConsumeIntegerDigitsMultiSegment(...)0%22220%
File 2: ConsumeDecimalDigitsMultiSegment(...)0%10100%
File 2: ConsumeSignMultiSegment(...)0%22220%
File 2: ConsumeNextTokenOrRollbackMultiSegment(...)0%440%
File 2: ConsumeNextTokenMultiSegment(...)0%56560%
File 2: ConsumeNextTokenFromLastNonCommentTokenMultiSegment()0%90900%
File 2: SkipAllCommentsMultiSegment(...)0%10100%
File 2: SkipAllCommentsMultiSegment(...)0%10100%
File 2: ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiSegment(...)0%62620%
File 2: SkipOrConsumeCommentMultiSegmentWithRollback()0%10100%
File 2: SkipCommentMultiSegment(...)0%26260%
File 2: SkipSingleLineCommentMultiSegment(...)0%24240%
File 2: FindLineSeparatorMultiSegment(...)0%16160%
File 2: ThrowOnDangerousLineSeparatorMultiSegment(...)0%16160%
File 2: SkipMultiLineCommentMultiSegment(...)0%22220%
File 2: CaptureState()100%110%
File 2: .ctor(...)100%110%
File 2: GetStartPosition(...)100%110%
File 3: GetString()0%10100%
File 3: CopyString(...)0%440%
File 3: CopyValue(...)0%12120%
File 3: CopyString(...)0%440%
File 3: CopyValue(...)0%16160%
File 3: TryCopyEscapedString(...)0%880%
File 3: GetComment()0%440%
File 3: GetBoolean()0%880%
File 3: GetBytesFromBase64()0%220%
File 3: GetByte()0%220%
File 3: GetByteWithQuotes()0%220%
File 3: GetSByte()0%220%
File 3: GetSByteWithQuotes()0%220%
File 3: GetInt16()0%220%
File 3: GetInt16WithQuotes()0%220%
File 3: GetInt32()0%220%
File 3: GetInt32WithQuotes()0%220%
File 3: GetInt64()0%220%
File 3: GetInt64WithQuotes()0%220%
File 3: GetUInt16()0%220%
File 3: GetUInt16WithQuotes()0%220%
File 3: GetUInt32()0%220%
File 3: GetUInt32WithQuotes()0%220%
File 3: GetUInt64()0%220%
File 3: GetUInt64WithQuotes()0%220%
File 3: GetSingle()0%220%
File 3: GetSingleWithQuotes()0%880%
File 3: GetSingleFloatingPointConstant()0%220%
File 3: GetDouble()0%220%
File 3: GetDoubleWithQuotes()0%880%
File 3: GetDoubleFloatingPointConstant()0%220%
File 3: GetDecimal()0%220%
File 3: GetDecimalWithQuotes()0%220%
File 3: GetDateTime()0%220%
File 3: GetDateTimeNoValidation()0%220%
File 3: GetDateTimeOffset()0%220%
File 3: GetDateTimeOffsetNoValidation()0%220%
File 3: GetGuid()0%220%
File 3: GetGuidNoValidation()0%220%
File 3: TryGetBytesFromBase64(...)0%660%
File 3: TryGetByte(...)0%440%
File 3: TryGetByteCore(...)0%440%
File 3: TryGetSByte(...)0%440%
File 3: TryGetSByteCore(...)0%440%
File 3: TryGetInt16(...)0%440%
File 3: TryGetInt16Core(...)0%440%
File 3: TryGetInt32(...)0%440%
File 3: TryGetInt32Core(...)0%440%
File 3: TryGetInt64(...)0%440%
File 3: TryGetInt64Core(...)0%440%
File 3: TryGetUInt16(...)0%440%
File 3: TryGetUInt16Core(...)0%440%
File 3: TryGetUInt32(...)0%440%
File 3: TryGetUInt32Core(...)0%440%
File 3: TryGetUInt64(...)0%440%
File 3: TryGetUInt64Core(...)0%440%
File 3: TryGetSingle(...)0%880%
File 3: TryGetDouble(...)0%880%
File 3: TryGetDecimal(...)0%440%
File 3: TryGetDecimalCore(...)0%440%
File 3: TryGetDateTime(...)0%220%
File 3: TryGetDateTimeCore(...)0%440%
File 3: TryGetDateTimeOffset(...)0%220%
File 3: TryGetDateTimeOffsetCore(...)0%440%
File 3: TryGetGuid(...)0%220%
File 3: TryGetGuidCore(...)0%440%

File(s)

D:\runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Reader\Utf8JsonReader.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;
 7
 8namespace System.Text.Json
 9{
 10    /// <summary>
 11    /// Provides a high-performance API for forward-only, read-only access to the UTF-8 encoded JSON text.
 12    /// It processes the text sequentially with no caching and adheres strictly to the JSON RFC
 13    /// by default (https://tools.ietf.org/html/rfc8259). When it encounters invalid JSON, it throws
 14    /// a JsonException with basic error information like line number and byte position on the line.
 15    /// Since this type is a ref struct, it does not directly support async. However, it does provide
 16    /// support for reentrancy to read incomplete data, and continue reading once more data is presented.
 17    /// To be able to set max depth while reading OR allow skipping comments, create an instance of
 18    /// <see cref="JsonReaderState"/> and pass that in to the reader.
 19    /// </summary>
 20    [DebuggerDisplay("{DebuggerDisplay,nq}")]
 21    public ref partial struct Utf8JsonReader
 22    {
 23        private ReadOnlySpan<byte> _buffer;
 24
 25        private readonly bool _isFinalBlock;
 26        private readonly bool _isInputSequence;
 27
 28        private long _lineNumber;
 29        private long _bytePositionInLine;
 30
 31        // bytes consumed in the current segment (not token)
 32        private int _consumed;
 33        private bool _inObject;
 34        private bool _isNotPrimitive;
 35        private JsonTokenType _tokenType;
 36        private JsonTokenType _previousTokenType;
 37        private JsonReaderOptions _readerOptions;
 38        private BitStack _bitStack;
 39
 40        private long _totalConsumed;
 41        private bool _isLastSegment;
 42        private readonly bool _isMultiSegment;
 43        private bool _trailingCommaBeforeComment;
 44
 45        private SequencePosition _nextPosition;
 46        private SequencePosition _currentPosition;
 47        private readonly ReadOnlySequence<byte> _sequence;
 48
 222049        private readonly bool IsLastSpan => _isFinalBlock && (!_isMultiSegment || _isLastSegment);
 50
 051        internal readonly ReadOnlySequence<byte> OriginalSequence => _sequence;
 52
 053        internal readonly ReadOnlySpan<byte> OriginalSpan => _sequence.IsEmpty ? _buffer : default;
 54
 055        internal readonly int ValueLength => HasValueSequence ? checked((int)ValueSequence.Length) : ValueSpan.Length;
 56
 057        internal readonly bool AllowMultipleValues => _readerOptions.AllowMultipleValues;
 58
 59        /// <summary>
 60        /// Gets the value of the last processed token as a ReadOnlySpan&lt;byte&gt; slice
 61        /// of the input payload. If the JSON is provided within a ReadOnlySequence&lt;byte&gt;
 62        /// and the slice that represents the token value fits in a single segment, then
 63        /// <see cref="ValueSpan"/> will contain the sliced value since it can be represented as a span.
 64        /// Otherwise, the <see cref="ValueSequence"/> will contain the token value.
 65        /// </summary>
 66        /// <remarks>
 67        /// If <see cref="HasValueSequence"/> is true, <see cref="ValueSpan"/> contains useless data, likely for
 68        /// a previous single-segment token. Therefore, only access <see cref="ValueSpan"/> if <see cref="HasValueSequen
 69        /// Otherwise, the token value must be accessed from <see cref="ValueSequence"/>.
 70        /// </remarks>
 40013871        public ReadOnlySpan<byte> ValueSpan { get; private set; }
 72
 73        /// <summary>
 74        /// Returns the total amount of bytes consumed by the <see cref="Utf8JsonReader"/> so far
 75        /// for the current instance of the <see cref="Utf8JsonReader"/> with the given UTF-8 encoded input text.
 76        /// </summary>
 77        public readonly long BytesConsumed
 78        {
 79            get
 15080            {
 81#if DEBUG
 15082                if (!_isInputSequence)
 15083                {
 15084                    Debug.Assert(_totalConsumed == 0);
 15085                }
 86#endif
 15087                return _totalConsumed + _consumed;
 15088            }
 89        }
 90
 91        /// <summary>
 92        /// Returns the index that the last processed JSON token starts at
 93        /// within the given UTF-8 encoded input text, skipping any white space.
 94        /// </summary>
 95        /// <remarks>
 96        /// For JSON strings (including property names), this points to before the start quote.
 97        /// For comments, this points to before the first comment delimiter (i.e. '/').
 98        /// </remarks>
 59155999        public long TokenStartIndex { get; private set; }
 100
 101        /// <summary>
 102        /// Tracks the recursive depth of the nested objects / arrays within the JSON text
 103        /// processed so far. This provides the depth of the current token.
 104        /// </summary>
 105        public readonly int CurrentDepth
 106        {
 107            get
 0108            {
 0109                int readerDepth = _bitStack.CurrentDepth;
 0110                if (TokenType is JsonTokenType.StartArray or JsonTokenType.StartObject)
 0111                {
 0112                    Debug.Assert(readerDepth >= 1);
 0113                    readerDepth--;
 0114                }
 0115                return readerDepth;
 0116            }
 117        }
 118
 154860119        internal readonly bool IsInArray => !_inObject;
 120
 121        /// <summary>
 122        /// Gets the type of the last processed JSON token in the UTF-8 encoded JSON text.
 123        /// </summary>
 155414124        public readonly JsonTokenType TokenType => _tokenType;
 125
 126        /// <summary>
 127        /// Lets the caller know which of the two 'Value' properties to read to get the
 128        /// token value. For input data within a ReadOnlySpan&lt;byte&gt; this will
 129        /// always return false. For input data within a ReadOnlySequence&lt;byte&gt;, this
 130        /// will only return true if the token value straddles more than a single segment and
 131        /// hence couldn't be represented as a span.
 132        /// </summary>
 4884133        public bool HasValueSequence { get; private set; }
 134
 135        /// <summary>
 136        /// Lets the caller know whether the current <see cref="ValueSpan" /> or <see cref="ValueSequence"/> properties
 137        /// contain escape sequences per RFC 8259 section 7, and therefore require unescaping before being consumed.
 138        /// </summary>
 188883139        public bool ValueIsEscaped { get; private set; }
 140
 141        /// <summary>
 142        /// Returns the mode of this instance of the <see cref="Utf8JsonReader"/>.
 143        /// True when the reader was constructed with the input span containing the entire data to process.
 144        /// False when the reader was constructed knowing that the input span may contain partial data with more data to
 145        /// </summary>
 0146        public readonly bool IsFinalBlock => _isFinalBlock;
 147
 148        /// <summary>
 149        /// Gets the value of the last processed token as a ReadOnlySpan&lt;byte&gt; slice
 150        /// of the input payload. If the JSON is provided within a ReadOnlySequence&lt;byte&gt;
 151        /// and the slice that represents the token value fits in a single segment, then
 152        /// <see cref="ValueSpan"/> will contain the sliced value since it can be represented as a span.
 153        /// Otherwise, the <see cref="ValueSequence"/> will contain the token value.
 154        /// </summary>
 155        /// <remarks>
 156        /// If <see cref="HasValueSequence"/> is false, <see cref="ValueSequence"/> contains useless data, likely for
 157        /// a previous multi-segment token. Therefore, only access <see cref="ValueSequence"/> if <see cref="HasValueSeq
 158        /// Otherwise, the token value must be accessed from <see cref="ValueSpan"/>.
 159        /// </remarks>
 4884160        public ReadOnlySequence<byte> ValueSequence { get; private set; }
 161
 162        /// <summary>
 163        /// Returns the current <see cref="SequencePosition"/> within the provided UTF-8 encoded
 164        /// input ReadOnlySequence&lt;byte&gt;. If the <see cref="Utf8JsonReader"/> was constructed
 165        /// with a ReadOnlySpan&lt;byte&gt; instead, this will always return a default <see cref="SequencePosition"/>.
 166        /// </summary>
 167        public readonly SequencePosition Position
 168        {
 169            get
 0170            {
 0171                if (_isInputSequence)
 0172                {
 0173                    Debug.Assert(_currentPosition.GetObject() is not null);
 0174                    return _sequence.GetPosition(_consumed, _currentPosition);
 175                }
 0176                return default;
 0177            }
 178        }
 179
 180        /// <summary>
 181        /// Returns the current snapshot of the <see cref="Utf8JsonReader"/> state which must
 182        /// be captured by the caller and passed back in to the <see cref="Utf8JsonReader"/> ctor with more data.
 183        /// Unlike the <see cref="Utf8JsonReader"/>, which is a ref struct, the state can survive
 184        /// across async/await boundaries and hence this type is required to provide support for reading
 185        /// in more data asynchronously before continuing with a new instance of the <see cref="Utf8JsonReader"/>.
 186        /// </summary>
 9505187        public readonly JsonReaderState CurrentState => new JsonReaderState
 9505188        (
 9505189            lineNumber: _lineNumber,
 9505190            bytePositionInLine: _bytePositionInLine,
 9505191            inObject: _inObject,
 9505192            isNotPrimitive: _isNotPrimitive,
 9505193            valueIsEscaped: ValueIsEscaped,
 9505194            trailingCommaBeforeComment: _trailingCommaBeforeComment,
 9505195            tokenType: _tokenType,
 9505196            previousTokenType: _previousTokenType,
 9505197            readerOptions: _readerOptions,
 9505198            bitStack: _bitStack
 9505199        );
 200
 201        /// <summary>
 202        /// Constructs a new <see cref="Utf8JsonReader"/> instance.
 203        /// </summary>
 204        /// <param name="jsonData">The ReadOnlySpan&lt;byte&gt; containing the UTF-8 encoded JSON text to process.</para
 205        /// <param name="isFinalBlock">True when the input span contains the entire data to process.
 206        /// Set to false only if it is known that the input span contains partial data with more data to follow.</param>
 207        /// <param name="state">If this is the first call to the ctor, pass in a default state. Otherwise,
 208        /// capture the state from the previous instance of the <see cref="Utf8JsonReader"/> and pass that back.</param>
 209        /// <remarks>
 210        /// Since this type is a ref struct, it is a stack-only type and all the limitations of ref structs apply to it.
 211        /// This is the reason why the ctor accepts a <see cref="JsonReaderState"/>.
 212        /// </remarks>
 213        public Utf8JsonReader(ReadOnlySpan<byte> jsonData, bool isFinalBlock, JsonReaderState state)
 4884214        {
 4884215            _buffer = jsonData;
 216
 4884217            _isFinalBlock = isFinalBlock;
 4884218            _isInputSequence = false;
 219
 4884220            _lineNumber = state._lineNumber;
 4884221            _bytePositionInLine = state._bytePositionInLine;
 4884222            _inObject = state._inObject;
 4884223            _isNotPrimitive = state._isNotPrimitive;
 4884224            ValueIsEscaped = state._valueIsEscaped;
 4884225            _trailingCommaBeforeComment = state._trailingCommaBeforeComment;
 4884226            _tokenType = state._tokenType;
 4884227            _previousTokenType = state._previousTokenType;
 4884228            _readerOptions = state._readerOptions;
 4884229            if (_readerOptions.MaxDepth == 0)
 4884230            {
 4884231                _readerOptions.MaxDepth = JsonReaderOptions.DefaultMaxDepth;  // If max depth is not set, revert to the 
 4884232            }
 4884233            _bitStack = state._bitStack;
 234
 4884235            _consumed = 0;
 4884236            TokenStartIndex = 0;
 4884237            _totalConsumed = 0;
 4884238            _isLastSegment = _isFinalBlock;
 4884239            _isMultiSegment = false;
 240
 4884241            ValueSpan = ReadOnlySpan<byte>.Empty;
 242
 4884243            _currentPosition = default;
 4884244            _nextPosition = default;
 4884245            _sequence = default;
 4884246            HasValueSequence = false;
 4884247            ValueSequence = ReadOnlySequence<byte>.Empty;
 4884248        }
 249
 250        /// <summary>
 251        /// Constructs a new <see cref="Utf8JsonReader"/> instance.
 252        /// </summary>
 253        /// <param name="jsonData">The ReadOnlySpan&lt;byte&gt; containing the UTF-8 encoded JSON text to process.</para
 254        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonReader"/>
 255        /// that is different from the JSON RFC (for example how to handle comments or maximum depth allowed when readin
 256        /// By default, the <see cref="Utf8JsonReader"/> follows the JSON RFC strictly (i.e. comments within the JSON ar
 257        /// <remarks>
 258        ///   <para>
 259        ///     Since this type is a ref struct, it is a stack-only type and all the limitations of ref structs apply to
 260        ///   </para>
 261        ///   <para>
 262        ///     This assumes that the entire JSON payload is passed in (equivalent to <see cref="IsFinalBlock"/> = true)
 263        ///   </para>
 264        /// </remarks>
 265        public Utf8JsonReader(ReadOnlySpan<byte> jsonData, JsonReaderOptions options = default)
 0266            : this(jsonData, isFinalBlock: true, new JsonReaderState(options))
 0267        {
 0268        }
 269
 270        /// <summary>
 271        /// Read the next JSON token from input source.
 272        /// </summary>
 273        /// <returns>True if the token was read successfully, else false.</returns>
 274        /// <exception cref="JsonException">
 275        /// Thrown when an invalid JSON token is encountered according to the JSON RFC
 276        /// or if the current depth exceeds the recursive limit set by the max depth.
 277        /// </exception>
 278        public bool Read()
 159744279        {
 159744280            bool retVal = _isMultiSegment ? ReadMultiSegment() : ReadSingleSegment();
 281
 155414282            if (!retVal)
 554283            {
 554284                if (_isFinalBlock && TokenType is JsonTokenType.None && !_readerOptions.AllowMultipleValues)
 404285                {
 404286                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedJsonTokens);
 287                }
 150288            }
 155010289            return retVal;
 155010290        }
 291
 292        /// <summary>
 293        /// Skips the children of the current JSON token.
 294        /// </summary>
 295        /// <exception cref="InvalidOperationException">
 296        /// Thrown when the reader was given partial data with more data to follow (i.e. <see cref="IsFinalBlock"/> is f
 297        /// </exception>
 298        /// <exception cref="JsonException">
 299        /// Thrown when an invalid JSON token is encountered while skipping, according to the JSON RFC,
 300        /// or if the current depth exceeds the recursive limit set by the max depth.
 301        /// </exception>
 302        /// <remarks>
 303        /// When <see cref="TokenType"/> is <see cref="JsonTokenType.PropertyName" />, the reader first moves to the pro
 304        /// When <see cref="TokenType"/> (originally, or after advancing) is <see cref="JsonTokenType.StartObject" /> or
 305        /// <see cref="JsonTokenType.StartArray" />, the reader advances to the matching
 306        /// <see cref="JsonTokenType.EndObject" /> or <see cref="JsonTokenType.EndArray" />.
 307        ///
 308        /// For all other token types, the reader does not move. After the next call to <see cref="Read"/>, the reader w
 309        /// the next value (when in an array), the next property name (when in an object), or the end array/object token
 310        /// </remarks>
 311        public void Skip()
 0312        {
 0313            if (!_isFinalBlock)
 0314            {
 0315                ThrowHelper.ThrowInvalidOperationException_CannotSkipOnPartial();
 316            }
 317
 0318            SkipHelper();
 0319        }
 320
 321        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 322        private void SkipHelper()
 0323        {
 0324            Debug.Assert(_isFinalBlock);
 325
 0326            if (TokenType is JsonTokenType.PropertyName)
 0327            {
 0328                bool result = Read();
 329                // Since _isFinalBlock == true here, and the JSON token is not a primitive value or comment.
 330                // Read() is guaranteed to return true OR throw for invalid/incomplete data.
 0331                Debug.Assert(result);
 0332            }
 333
 0334            if (TokenType is JsonTokenType.StartObject or JsonTokenType.StartArray)
 0335            {
 0336                int depth = CurrentDepth;
 337                do
 0338                {
 0339                    bool result = Read();
 340                    // Since _isFinalBlock == true here, and the JSON token is not a primitive value or comment.
 341                    // Read() is guaranteed to return true OR throw for invalid/incomplete data.
 0342                    Debug.Assert(result);
 0343                }
 0344                while (depth < CurrentDepth);
 0345            }
 0346        }
 347
 348        /// <summary>
 349        /// Tries to skip the children of the current JSON token.
 350        /// </summary>
 351        /// <returns>True if there was enough data for the children to be skipped successfully, else false.</returns>
 352        /// <exception cref="JsonException">
 353        /// Thrown when an invalid JSON token is encountered while skipping, according to the JSON RFC,
 354        /// or if the current depth exceeds the recursive limit set by the max depth.
 355        /// </exception>
 356        /// <remarks>
 357        ///   <para>
 358        ///     If the reader did not have enough data to completely skip the children of the current token,
 359        ///     it will be reset to the state it was in before the method was called.
 360        ///   </para>
 361        ///   <para>
 362        ///     When <see cref="TokenType"/> is <see cref="JsonTokenType.PropertyName" />, the reader first moves to the
 363        ///     When <see cref="TokenType"/> (originally, or after advancing) is <see cref="JsonTokenType.StartObject" /
 364        ///     <see cref="JsonTokenType.StartArray" />, the reader advances to the matching
 365        ///     <see cref="JsonTokenType.EndObject" /> or <see cref="JsonTokenType.EndArray" />.
 366        ///
 367        ///     For all other token types, the reader does not move. After the next call to <see cref="Read"/>, the read
 368        ///     the next value (when in an array), the next property name (when in an object), or the end array/object t
 369        ///   </para>
 370        /// </remarks>
 371        public bool TrySkip()
 0372        {
 0373            if (_isFinalBlock)
 0374            {
 0375                SkipHelper();
 0376                return true;
 377            }
 378
 0379            Utf8JsonReader restore = this;
 0380            bool success = TrySkipPartial(targetDepth: CurrentDepth);
 0381            if (!success)
 0382            {
 383                // Roll back the reader if it contains partial data.
 0384                this = restore;
 0385            }
 386
 0387            return success;
 0388        }
 389
 390        /// <summary>
 391        /// Tries to skip the children of the current JSON token, advancing the reader even if there is not enough data.
 392        /// The skip operation can be resumed later, provided that the same <paramref name="targetDepth" /> is passed.
 393        /// </summary>
 394        /// <param name="targetDepth">The target depth we want to eventually skip to.</param>
 395        /// <returns>True if the entire JSON value has been skipped.</returns>
 396        internal bool TrySkipPartial(int targetDepth)
 0397        {
 0398            Debug.Assert(0 <= targetDepth && targetDepth <= CurrentDepth);
 399
 0400            if (targetDepth == CurrentDepth)
 0401            {
 402                // This is the first call to TrySkipHelper.
 0403                if (TokenType is JsonTokenType.PropertyName)
 0404                {
 405                    // Skip any property name tokens preceding the value.
 0406                    if (!Read())
 0407                    {
 0408                        return false;
 409                    }
 0410                }
 411
 0412                if (TokenType is not (JsonTokenType.StartObject or JsonTokenType.StartArray))
 0413                {
 414                    // The next value is not an object or array, so there is nothing to skip.
 0415                    return true;
 416                }
 0417            }
 418
 419            // Start or resume iterating through the JSON object or array.
 420            do
 0421            {
 0422                if (!Read())
 0423                {
 0424                    return false;
 425                }
 0426            }
 0427            while (targetDepth < CurrentDepth);
 428
 0429            Debug.Assert(targetDepth == CurrentDepth);
 0430            return true;
 0431        }
 432
 433        /// <summary>
 434        /// Compares the UTF-8 encoded text to the unescaped JSON token value in the source and returns true if they mat
 435        /// </summary>
 436        /// <param name="utf8Text">The UTF-8 encoded text to compare against.</param>
 437        /// <returns>True if the JSON token value in the source matches the UTF-8 encoded look up text.</returns>
 438        /// <exception cref="InvalidOperationException">
 439        /// Thrown if trying to find a text match on a JSON token that is not a string
 440        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>).
 441        /// <seealso cref="TokenType" />
 442        /// </exception>
 443        /// <remarks>
 444        ///   <para>
 445        ///     If the look up text is invalid UTF-8 text, the method will return false since you cannot have
 446        ///     invalid UTF-8 within the JSON payload.
 447        ///   </para>
 448        ///   <para>
 449        ///     The comparison of the JSON token value in the source and the look up text is done by first unescaping th
 450        ///     if required. The look up text is matched as is, without any modifications to it.
 451        ///   </para>
 452        /// </remarks>
 453        public readonly bool ValueTextEquals(ReadOnlySpan<byte> utf8Text)
 0454        {
 0455            if (!IsTokenTypeString(TokenType))
 0456            {
 0457                ThrowHelper.ThrowInvalidOperationException_ExpectedStringComparison(TokenType);
 458            }
 459
 0460            return TextEqualsHelper(utf8Text);
 0461        }
 462
 463        /// <summary>
 464        /// Compares the string text to the unescaped JSON token value in the source and returns true if they match.
 465        /// </summary>
 466        /// <param name="text">The text to compare against.</param>
 467        /// <returns>True if the JSON token value in the source matches the look up text.</returns>
 468        /// <exception cref="InvalidOperationException">
 469        /// Thrown if trying to find a text match on a JSON token that is not a string
 470        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>).
 471        /// <seealso cref="TokenType" />
 472        /// </exception>
 473        /// <remarks>
 474        ///   <para>
 475        ///     If the look up text is invalid UTF-8 text, the method will return false since you cannot have
 476        ///     invalid UTF-8 within the JSON payload.
 477        ///   </para>
 478        ///   <para>
 479        ///     The comparison of the JSON token value in the source and the look up text is done by first unescaping th
 480        ///     if required. The look up text is matched as is, without any modifications to it.
 481        ///   </para>
 482        /// </remarks>
 483        public readonly bool ValueTextEquals(string? text)
 0484        {
 0485            return ValueTextEquals(text.AsSpan());
 0486        }
 487
 488        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 489        private readonly bool TextEqualsHelper(ReadOnlySpan<byte> otherUtf8Text)
 0490        {
 0491            if (HasValueSequence)
 0492            {
 0493                return CompareToSequence(otherUtf8Text);
 494            }
 495
 0496            if (ValueIsEscaped)
 0497            {
 0498                return UnescapeAndCompare(otherUtf8Text);
 499            }
 500
 0501            return otherUtf8Text.SequenceEqual(ValueSpan);
 0502        }
 503
 504        /// <summary>
 505        /// Compares the text to the unescaped JSON token value in the source and returns true if they match.
 506        /// </summary>
 507        /// <param name="text">The text to compare against.</param>
 508        /// <returns>True if the JSON token value in the source matches the look up text.</returns>
 509        /// <exception cref="InvalidOperationException">
 510        /// Thrown if trying to find a text match on a JSON token that is not a string
 511        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>).
 512        /// <seealso cref="TokenType" />
 513        /// </exception>
 514        /// <remarks>
 515        ///   <para>
 516        ///     If the look up text is invalid or incomplete UTF-16 text (i.e. unpaired surrogates), the method will ret
 517        ///     since you cannot have invalid UTF-16 within the JSON payload.
 518        ///   </para>
 519        ///   <para>
 520        ///     The comparison of the JSON token value in the source and the look up text is done by first unescaping th
 521        ///     if required. The look up text is matched as is, without any modifications to it.
 522        ///   </para>
 523        /// </remarks>
 524        public readonly unsafe bool ValueTextEquals(ReadOnlySpan<char> text)
 0525        {
 0526            if (!IsTokenTypeString(TokenType))
 0527            {
 0528                ThrowHelper.ThrowInvalidOperationException_ExpectedStringComparison(TokenType);
 529            }
 530
 0531            if (MatchNotPossible(text.Length))
 0532            {
 0533                return false;
 534            }
 535
 0536            byte[]? otherUtf8TextArray = null;
 537
 538            scoped Span<byte> otherUtf8Text;
 539
 0540            int length = checked(text.Length * JsonConstants.MaxExpansionFactorWhileTranscoding);
 541
 0542            if (length > JsonConstants.StackallocByteThreshold)
 0543            {
 0544                otherUtf8TextArray = ArrayPool<byte>.Shared.Rent(length);
 0545                otherUtf8Text = otherUtf8TextArray;
 0546            }
 547            else
 0548            {
 0549                otherUtf8Text = stackalloc byte[JsonConstants.StackallocByteThreshold];
 0550            }
 551
 0552            OperationStatus status = JsonWriterHelper.ToUtf8(text, otherUtf8Text, out int written);
 0553            Debug.Assert(status != OperationStatus.DestinationTooSmall);
 554            bool result;
 0555            if (status == OperationStatus.InvalidData)
 0556            {
 0557                result = false;
 0558            }
 559            else
 0560            {
 0561                Debug.Assert(status == OperationStatus.Done);
 0562                result = TextEqualsHelper(otherUtf8Text.Slice(0, written));
 0563            }
 564
 0565            if (otherUtf8TextArray is not null)
 0566            {
 0567                otherUtf8Text.Slice(0, written).Clear();
 0568                ArrayPool<byte>.Shared.Return(otherUtf8TextArray);
 0569            }
 570
 0571            return result;
 0572        }
 573
 574        private readonly bool CompareToSequence(ReadOnlySpan<byte> other)
 0575        {
 0576            Debug.Assert(HasValueSequence);
 577
 0578            if (ValueIsEscaped)
 0579            {
 0580                return UnescapeSequenceAndCompare(other);
 581            }
 582
 0583            ReadOnlySequence<byte> localSequence = ValueSequence;
 584
 0585            Debug.Assert(!localSequence.IsSingleSegment);
 586
 0587            if (localSequence.Length != other.Length)
 0588            {
 0589                return false;
 590            }
 591
 0592            int matchedSoFar = 0;
 593
 0594            foreach (ReadOnlyMemory<byte> memory in localSequence)
 0595            {
 0596                ReadOnlySpan<byte> span = memory.Span;
 597
 0598                if (other.Slice(matchedSoFar).StartsWith(span))
 0599                {
 0600                    matchedSoFar += span.Length;
 0601                }
 602                else
 0603                {
 0604                    return false;
 605                }
 0606            }
 0607            return true;
 0608        }
 609
 610        private readonly bool UnescapeAndCompare(ReadOnlySpan<byte> other)
 0611        {
 0612            Debug.Assert(!HasValueSequence);
 0613            ReadOnlySpan<byte> localSpan = ValueSpan;
 614
 0615            if (localSpan.Length < other.Length || localSpan.Length / JsonConstants.MaxExpansionFactorWhileEscaping > ot
 0616            {
 0617                return false;
 618            }
 619
 0620            int idx = localSpan.IndexOf(JsonConstants.BackSlash);
 0621            Debug.Assert(idx != -1);
 622
 0623            if (!other.StartsWith(localSpan.Slice(0, idx)))
 0624            {
 0625                return false;
 626            }
 627
 0628            return JsonReaderHelper.UnescapeAndCompare(localSpan.Slice(idx), other.Slice(idx));
 0629        }
 630
 631        private readonly bool UnescapeSequenceAndCompare(ReadOnlySpan<byte> other)
 0632        {
 0633            Debug.Assert(HasValueSequence);
 0634            Debug.Assert(!ValueSequence.IsSingleSegment);
 635
 0636            ReadOnlySequence<byte> localSequence = ValueSequence;
 0637            long sequenceLength = localSequence.Length;
 638
 639            // The JSON token value will at most shrink by 6 when unescaping.
 640            // If it is still larger than the lookup string, there is no value in unescaping and doing the comparison.
 0641            if (sequenceLength < other.Length || sequenceLength / JsonConstants.MaxExpansionFactorWhileEscaping > other.
 0642            {
 0643                return false;
 644            }
 645
 0646            int matchedSoFar = 0;
 647
 0648            bool result = false;
 649
 0650            foreach (ReadOnlyMemory<byte> memory in localSequence)
 0651            {
 0652                ReadOnlySpan<byte> span = memory.Span;
 653
 0654                int idx = span.IndexOf(JsonConstants.BackSlash);
 655
 0656                if (idx != -1)
 0657                {
 0658                    if (!other.Slice(matchedSoFar).StartsWith(span.Slice(0, idx)))
 0659                    {
 0660                        break;
 661                    }
 0662                    matchedSoFar += idx;
 663
 0664                    other = other.Slice(matchedSoFar);
 0665                    localSequence = localSequence.Slice(matchedSoFar);
 666
 0667                    if (localSequence.IsSingleSegment)
 0668                    {
 0669                        result = JsonReaderHelper.UnescapeAndCompare(localSequence.FirstSpan, other);
 0670                    }
 671                    else
 0672                    {
 0673                        result = JsonReaderHelper.UnescapeAndCompare(localSequence, other);
 0674                    }
 0675                    break;
 676                }
 677
 0678                if (!other.Slice(matchedSoFar).StartsWith(span))
 0679                {
 0680                    break;
 681                }
 0682                matchedSoFar += span.Length;
 0683            }
 684
 0685            return result;
 0686        }
 687
 688        // Returns true if the TokenType is a primitive string "value", i.e. PropertyName or String
 689        // Otherwise, return false.
 690        private static bool IsTokenTypeString(JsonTokenType tokenType)
 0691        {
 0692            return tokenType is JsonTokenType.PropertyName or JsonTokenType.String;
 0693        }
 694
 695        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 696        private readonly bool MatchNotPossible(int charTextLength)
 0697        {
 0698            if (HasValueSequence)
 0699            {
 0700                return MatchNotPossibleSequence(charTextLength);
 701            }
 702
 0703            int sourceLength = ValueSpan.Length;
 704
 705            // Transcoding from UTF-16 to UTF-8 will change the length by somwhere between 1x and 3x.
 706            // Unescaping the token value will at most shrink its length by 6x.
 707            // There is no point incurring the transcoding/unescaping/comparing cost if:
 708            // - The token value is smaller than charTextLength
 709            // - The token value needs to be transcoded AND unescaped and it is more than 6x larger than charTextLength
 710            //      - For an ASCII UTF-16 characters, transcoding = 1x, escaping = 6x => 6x factor
 711            //      - For non-ASCII UTF-16 characters within the BMP, transcoding = 2-3x, but they are represented as a 
 712            //      - For non-ASCII UTF-16 characters outside of the BMP, transcoding = 4x, but the surrogate pair (2 ch
 713            // - The token value needs to be transcoded, but NOT escaped and it is more than 3x larger than charTextLeng
 714            //      - For an ASCII UTF-16 characters, transcoding = 1x,
 715            //      - For non-ASCII UTF-16 characters within the BMP, transcoding = 2-3x,
 716            //      - For non-ASCII UTF-16 characters outside of the BMP, transcoding = 2x, (surrogate pairs - 2 charact
 717
 0718            if (sourceLength < charTextLength
 0719                || sourceLength / (ValueIsEscaped ? JsonConstants.MaxExpansionFactorWhileEscaping : JsonConstants.MaxExp
 0720            {
 0721                return true;
 722            }
 0723            return false;
 0724        }
 725
 726        [MethodImpl(MethodImplOptions.NoInlining)]
 727        private readonly bool MatchNotPossibleSequence(int charTextLength)
 0728        {
 0729            long sourceLength = ValueSequence.Length;
 730
 0731            if (sourceLength < charTextLength
 0732                || sourceLength / (ValueIsEscaped ? JsonConstants.MaxExpansionFactorWhileEscaping : JsonConstants.MaxExp
 0733            {
 0734                return true;
 735            }
 0736            return false;
 0737        }
 738
 739        private void StartObject()
 2369740        {
 2369741            if (_bitStack.CurrentDepth >= _readerOptions.MaxDepth)
 8742                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ObjectDepthTooLarge);
 743
 2361744            _bitStack.PushTrue();
 745
 2361746            ValueSpan = _buffer.Slice(_consumed, 1);
 2361747            _consumed++;
 2361748            _bytePositionInLine++;
 2361749            _tokenType = JsonTokenType.StartObject;
 2361750            _inObject = true;
 2361751        }
 752
 753        private void EndObject()
 215754        {
 215755            if (!_inObject || _bitStack.CurrentDepth <= 0)
 46756                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.MismatchedObjectArray, JsonConstants.Cl
 757
 169758            if (_trailingCommaBeforeComment)
 0759            {
 0760                if (!_readerOptions.AllowTrailingCommas)
 0761                {
 0762                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeObject
 763                }
 0764                _trailingCommaBeforeComment = false;
 0765            }
 766
 169767            _tokenType = JsonTokenType.EndObject;
 169768            ValueSpan = _buffer.Slice(_consumed, 1);
 769
 169770            UpdateBitStackOnEndToken();
 169771        }
 772
 773        private void StartArray()
 68462774        {
 68462775            if (_bitStack.CurrentDepth >= _readerOptions.MaxDepth)
 29776                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ArrayDepthTooLarge);
 777
 68433778            _bitStack.PushFalse();
 779
 68433780            ValueSpan = _buffer.Slice(_consumed, 1);
 68433781            _consumed++;
 68433782            _bytePositionInLine++;
 68433783            _tokenType = JsonTokenType.StartArray;
 68433784            _inObject = false;
 68433785        }
 786
 787        private void EndArray()
 44902788        {
 44902789            if (_inObject || _bitStack.CurrentDepth <= 0)
 5790                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.MismatchedObjectArray, JsonConstants.Cl
 791
 44897792            if (_trailingCommaBeforeComment)
 0793            {
 0794                if (!_readerOptions.AllowTrailingCommas)
 0795                {
 0796                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeArrayE
 797                }
 0798                _trailingCommaBeforeComment = false;
 0799            }
 800
 44897801            _tokenType = JsonTokenType.EndArray;
 44897802            ValueSpan = _buffer.Slice(_consumed, 1);
 803
 44897804            UpdateBitStackOnEndToken();
 44897805        }
 806
 807        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 808        private void UpdateBitStackOnEndToken()
 45066809        {
 45066810            _consumed++;
 45066811            _bytePositionInLine++;
 45066812            _inObject = _bitStack.Pop();
 45066813        }
 814
 815        private bool ReadSingleSegment()
 159744816        {
 159744817            bool retVal = false;
 159744818            ValueSpan = default;
 159744819            ValueIsEscaped = false;
 820
 159744821            if (!HasMoreData())
 121822            {
 121823                goto Done;
 824            }
 825
 159299826            byte first = _buffer[_consumed];
 827
 828            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 829            // SkipWhiteSpace only skips the whitespace characters as defined by JSON RFC 8259 section 2.
 830            // We do not validate if 'first' is an invalid JSON byte here (such as control characters).
 831            // Those cases are captured in ConsumeNextToken and ConsumeValue.
 159299832            if (first <= JsonConstants.Space)
 2603833            {
 2603834                SkipWhiteSpace();
 2603835                if (!HasMoreData())
 15836                {
 15837                    goto Done;
 838                }
 2571839                first = _buffer[_consumed];
 2571840            }
 841
 159267842            TokenStartIndex = _consumed;
 843
 159267844            if (_tokenType == JsonTokenType.None)
 4876845            {
 4876846                goto ReadFirstToken;
 847            }
 848
 154391849            if (first == JsonConstants.Slash)
 4660850            {
 4660851                retVal = ConsumeNextTokenOrRollback(first);
 3876852                goto Done;
 853            }
 854
 149731855            if (_tokenType == JsonTokenType.StartObject)
 1573856            {
 1573857                if (first == JsonConstants.CloseBrace)
 83858                {
 83859                    EndObject();
 83860                }
 861                else
 1490862                {
 1490863                    if (first != JsonConstants.Quote)
 35864                    {
 35865                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 866                    }
 867
 1455868                    int prevConsumed = _consumed;
 1455869                    long prevPosition = _bytePositionInLine;
 1455870                    long prevLineNumber = _lineNumber;
 1455871                    retVal = ConsumePropertyName();
 1419872                    if (!retVal)
 0873                    {
 874                        // roll back potential changes
 0875                        _consumed = prevConsumed;
 0876                        _tokenType = JsonTokenType.StartObject;
 0877                        _bytePositionInLine = prevPosition;
 0878                        _lineNumber = prevLineNumber;
 0879                    }
 0880                    goto Done;
 881                }
 83882            }
 148158883            else if (_tokenType == JsonTokenType.StartArray)
 68791884            {
 68791885                if (first == JsonConstants.CloseBracket)
 28108886                {
 28108887                    EndArray();
 28108888                }
 889                else
 40683890                {
 40683891                    retVal = ConsumeValue(first);
 40351892                    goto Done;
 893                }
 28108894            }
 79367895            else if (_tokenType == JsonTokenType.PropertyName)
 2329896            {
 2329897                retVal = ConsumeValue(first);
 2216898                goto Done;
 899            }
 900            else
 77038901            {
 77038902                retVal = ConsumeNextTokenOrRollback(first);
 75008903                goto Done;
 904            }
 905
 28191906            retVal = true;
 907
 155414908        Done:
 155414909            return retVal;
 910
 4876911        ReadFirstToken:
 4876912            retVal = ReadFirstToken(first);
 4217913            goto Done;
 155414914        }
 915
 916        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 917        private bool HasMoreData()
 180497918        {
 180497919            if (_consumed >= (uint)_buffer.Length)
 626920            {
 626921                if (_isNotPrimitive && IsLastSpan)
 533922                {
 533923                    if (_bitStack.CurrentDepth != 0)
 466924                    {
 466925                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ZeroDepthAtEnd);
 926                    }
 927
 67928                    if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && _tokenType == JsonTokenType.Comme
 0929                    {
 0930                        return false;
 931                    }
 932
 67933                    if (_tokenType is not JsonTokenType.EndArray and not JsonTokenType.EndObject)
 0934                    {
 0935                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndOfJsonNonPrimitive);
 936                    }
 67937                }
 160938                return false;
 939            }
 179871940            return true;
 180031941        }
 942
 943        // Unlike the parameter-less overload of HasMoreData, if there is no more data when this method is called, we kn
 944        // This is because, this method is only called after a ',' (i.e. we expect a value/property name) or after
 945        // a property name, which means it must be followed by a value.
 946        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 947        private bool HasMoreData(ExceptionResource resource)
 18587948        {
 18587949            if (_consumed >= (uint)_buffer.Length)
 35950            {
 35951                if (IsLastSpan)
 35952                {
 35953                    ThrowHelper.ThrowJsonReaderException(ref this, resource);
 954                }
 0955                return false;
 956            }
 18552957            return true;
 18552958        }
 959
 960        private bool ReadFirstToken(byte first)
 4876961        {
 4876962            if (first == JsonConstants.OpenBrace)
 681963            {
 681964                _bitStack.SetFirstBit();
 681965                _tokenType = JsonTokenType.StartObject;
 681966                ValueSpan = _buffer.Slice(_consumed, 1);
 681967                _consumed++;
 681968                _bytePositionInLine++;
 681969                _inObject = true;
 681970                _isNotPrimitive = true;
 681971            }
 4195972            else if (first == JsonConstants.OpenBracket)
 2843973            {
 2843974                _bitStack.ResetFirstBit();
 2843975                _tokenType = JsonTokenType.StartArray;
 2843976                ValueSpan = _buffer.Slice(_consumed, 1);
 2843977                _consumed++;
 2843978                _bytePositionInLine++;
 2843979                _isNotPrimitive = true;
 2843980            }
 981            else
 1352982            {
 983                // Create local copy to avoid bounds checks.
 1352984                ReadOnlySpan<byte> localBuffer = _buffer;
 985
 1352986                if (JsonHelpers.IsDigit(first) || first == '-')
 180987                {
 180988                    if (!TryGetNumber(localBuffer.Slice(_consumed), out int numberOfBytes))
 0989                    {
 0990                        return false;
 991                    }
 91992                    _tokenType = JsonTokenType.Number;
 91993                    _consumed += numberOfBytes;
 91994                    _bytePositionInLine += numberOfBytes;
 91995                }
 1172996                else if (!ConsumeValue(first))
 396997                {
 396998                    return false;
 999                }
 1000
 2971001                _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray;
 1002                // Intentionally fall out of the if-block to return true
 2971003            }
 38211004            return true;
 42171005        }
 1006
 1007        private void SkipWhiteSpace()
 120701008        {
 1009            // Create local copy to avoid bounds checks.
 120701010            ReadOnlySpan<byte> localBuffer = _buffer;
 1011#if NET
 1012            // A vectorized SearchValues-based scan to the first non-whitespace byte is fastest on most
 1013            // runtimes, so we go straight to it. On Mono/WASM AOT (browser) the fixed per-call SIMD
 1014            // entry cost is high relative to the short inter-token whitespace runs typical of JSON,
 1015            // and reconstructing the line/byte-position bookkeeping needs up to two more vectorized
 1016            // passes (CountNewLines), so short runs regress there. The browser path therefore uses the
 1017            // plain scalar loop below instead. OperatingSystem.IsBrowser() folds to a constant per
 1018            // target, so only one path survives in codegen and non-browser output is unchanged from a
 1019            // direct vectorized scan.
 120701020            if (!OperatingSystem.IsBrowser())
 120701021            {
 120701022                ReadOnlySpan<byte> remaining = localBuffer.Slice(_consumed);
 120701023                int idx = remaining.IndexOfFirstNonWhiteSpace();
 120701024                if (idx > 0)
 118441025                {
 1026                    // Reproduce the scalar loop's line/byte-position bookkeeping for the skipped run.
 118441027                    (int newLines, int lastLineFeedIndex) = JsonReaderHelper.CountNewLines(remaining.Slice(0, idx));
 118441028                    _lineNumber += newLines;
 118441029                    if (lastLineFeedIndex >= 0)
 95091030                    {
 1031                        // Byte positions on the current line start after the last line feed character.
 95091032                        _bytePositionInLine = idx - lastLineFeedIndex - 1;
 95091033                    }
 1034                    else
 23351035                    {
 23351036                        _bytePositionInLine += idx;
 23351037                    }
 1038
 118441039                    _consumed += idx;
 118441040                }
 1041
 118441042                return;
 1043            }
 1044#endif
 01045            for (; _consumed < localBuffer.Length; _consumed++)
 01046            {
 01047                byte val = localBuffer[_consumed];
 1048
 1049                // JSON RFC 8259 section 2 says only these 4 characters count, not all of the Unicode definitions of whi
 01050                if (val is not JsonConstants.Space and
 01051                           not JsonConstants.CarriageReturn and
 01052                           not JsonConstants.LineFeed and
 01053                           not JsonConstants.Tab)
 01054                {
 01055                    break;
 1056                }
 1057
 01058                if (val == JsonConstants.LineFeed)
 01059                {
 01060                    _lineNumber++;
 01061                    _bytePositionInLine = 0;
 01062                }
 1063                else
 01064                {
 01065                    _bytePositionInLine++;
 01066                }
 01067            }
 120701068        }
 1069
 1070        /// <summary>
 1071        /// This method contains the logic for processing the next value token and determining
 1072        /// what type of data it is.
 1073        /// </summary>
 1074        private bool ConsumeValue(byte marker)
 1058891075        {
 1118061076            while (true)
 1118061077            {
 1118061078                Debug.Assert((_trailingCommaBeforeComment && _readerOptions.CommentHandling == JsonCommentHandling.Allow
 1118061079                Debug.Assert((_trailingCommaBeforeComment && marker != JsonConstants.Slash) || !_trailingCommaBeforeComm
 1118061080                _trailingCommaBeforeComment = false;
 1081
 1118061082                if (marker == JsonConstants.Quote)
 48181083                {
 48181084                    return ConsumeString();
 1085                }
 1069881086                else if (marker == JsonConstants.OpenBrace)
 23691087                {
 23691088                    StartObject();
 23611089                }
 1046191090                else if (marker == JsonConstants.OpenBracket)
 684621091                {
 684621092                    StartArray();
 684331093                }
 361571094                else if (JsonHelpers.IsDigit(marker) || marker == '-')
 268091095                {
 268091096                    return ConsumeNumber();
 1097                }
 93481098                else if (marker == 'f')
 3971099                {
 3971100                    return ConsumeLiteral(JsonConstants.FalseValue, JsonTokenType.False);
 1101                }
 89511102                else if (marker == 't')
 2451103                {
 2451104                    return ConsumeLiteral(JsonConstants.TrueValue, JsonTokenType.True);
 1105                }
 87061106                else if (marker == 'n')
 14411107                {
 14411108                    return ConsumeLiteral(JsonConstants.NullValue, JsonTokenType.Null);
 1109                }
 1110                else
 72651111                {
 72651112                    switch (_readerOptions.CommentHandling)
 1113                    {
 1114                        case JsonCommentHandling.Disallow:
 1951115                            break;
 1116                        case JsonCommentHandling.Allow:
 01117                            if (marker == JsonConstants.Slash)
 01118                            {
 01119                                return ConsumeComment();
 1120                            }
 01121                            break;
 1122                        default:
 70701123                            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 70701124                            if (marker == JsonConstants.Slash)
 65021125                            {
 65021126                                if (SkipComment())
 63131127                                {
 63131128                                    if (_consumed >= (uint)_buffer.Length)
 3941129                                    {
 3941130                                        if (_isNotPrimitive && IsLastSpan && _tokenType != JsonTokenType.EndArray && _to
 01131                                        {
 01132                                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndO
 1133                                        }
 3941134                                        return false;
 1135                                    }
 1136
 59191137                                    marker = _buffer[_consumed];
 1138
 1139                                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not ne
 59191140                                    if (marker <= JsonConstants.Space)
 28021141                                    {
 28021142                                        SkipWhiteSpace();
 28021143                                        if (!HasMoreData())
 21144                                        {
 21145                                            return false;
 1146                                        }
 28001147                                        marker = _buffer[_consumed];
 28001148                                    }
 1149
 59171150                                    TokenStartIndex = _consumed;
 1151
 1152                                    // Skip comments and consume the actual JSON value.
 59171153                                    continue;
 1154                                }
 01155                                return false;
 1156                            }
 5681157                            break;
 1158                    }
 7631159                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, marke
 1160                }
 707941161                break;
 1162            }
 707941163            return true;
 1035291164        }
 1165
 1166        // Consumes 'null', or 'true', or 'false'
 1167        private bool ConsumeLiteral(ReadOnlySpan<byte> literal, JsonTokenType tokenType)
 20831168        {
 20831169            ReadOnlySpan<byte> span = _buffer.Slice(_consumed);
 20831170            Debug.Assert(span.Length > 0);
 20831171            Debug.Assert(span[0] == 'n' || span[0] == 't' || span[0] == 'f');
 1172
 20831173            if (!span.StartsWith(literal))
 1351174            {
 1351175                return CheckLiteral(span, literal);
 1176            }
 1177
 19481178            ValueSpan = span.Slice(0, literal.Length);
 19481179            _tokenType = tokenType;
 19481180            _consumed += literal.Length;
 19481181            _bytePositionInLine += literal.Length;
 19481182            return true;
 19481183        }
 1184
 1185        private bool CheckLiteral(ReadOnlySpan<byte> span, ReadOnlySpan<byte> literal)
 1351186        {
 1351187            Debug.Assert(span.Length > 0 && span[0] == literal[0]);
 1188
 1351189            int indexOfFirstMismatch = 0;
 1190
 6561191            for (int i = 1; i < literal.Length; i++)
 3281192            {
 3281193                if (span.Length > i)
 2751194                {
 2751195                    if (span[i] != literal[i])
 821196                    {
 821197                        _bytePositionInLine += i;
 821198                        ThrowInvalidLiteral(span);
 01199                    }
 1931200                }
 1201                else
 531202                {
 531203                    indexOfFirstMismatch = i;
 531204                    break;
 1205                }
 1931206            }
 1207
 531208            Debug.Assert(indexOfFirstMismatch > 0 && indexOfFirstMismatch < literal.Length);
 1209
 531210            if (IsLastSpan)
 531211            {
 531212                _bytePositionInLine += indexOfFirstMismatch;
 531213                ThrowInvalidLiteral(span);
 01214            }
 01215            return false;
 01216        }
 1217
 1218        private void ThrowInvalidLiteral(ReadOnlySpan<byte> span)
 1351219        {
 1351220            byte firstByte = span[0];
 1221
 1222            ExceptionResource resource;
 1351223            switch (firstByte)
 1224            {
 1225                case (byte)'t':
 311226                    resource = ExceptionResource.ExpectedTrue;
 311227                    break;
 1228                case (byte)'f':
 401229                    resource = ExceptionResource.ExpectedFalse;
 401230                    break;
 1231                default:
 641232                    Debug.Assert(firstByte == 'n');
 641233                    resource = ExceptionResource.ExpectedNull;
 641234                    break;
 1235            }
 1351236            ThrowHelper.ThrowJsonReaderException(ref this, resource, bytes: span);
 1237        }
 1238
 1239        private bool ConsumeNumber()
 268091240        {
 268091241            if (!TryGetNumber(_buffer.Slice(_consumed), out int consumed))
 01242            {
 01243                return false;
 1244            }
 1245
 261481246            _tokenType = JsonTokenType.Number;
 261481247            _consumed += consumed;
 261481248            _bytePositionInLine += consumed;
 1249
 261481250            if (_consumed >= (uint)_buffer.Length)
 751251            {
 751252                Debug.Assert(IsLastSpan);
 1253
 1254                // If there is no more data, and the JSON is not a single value, throw.
 751255                if (_isNotPrimitive)
 641256                {
 641257                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, _buffer
 1258                }
 111259            }
 1260
 1261            // If there is more data and the JSON is not a single value, assert that there is an end of number delimiter
 1262            // Else, if either the JSON is a single value XOR if there is no more data, don't assert anything since ther
 260841263            Debug.Assert(
 260841264                ((_consumed < _buffer.Length) &&
 260841265                !_isNotPrimitive &&
 260841266                JsonConstants.Delimiters.Contains(_buffer[_consumed]))
 260841267                || (_isNotPrimitive ^ (_consumed >= (uint)_buffer.Length)));
 1268
 260841269            return true;
 260841270        }
 1271
 1272        private bool ConsumePropertyName()
 31551273        {
 31551274            _trailingCommaBeforeComment = false;
 1275
 31551276            if (!ConsumeString())
 01277            {
 01278                return false;
 1279            }
 1280
 30901281            if (!HasMoreData(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 01282            {
 01283                return false;
 1284            }
 1285
 30881286            byte first = _buffer[_consumed];
 1287
 1288            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 1289            // We do not validate if 'first' is an invalid JSON byte here (such as control characters).
 1290            // Those cases are captured below where we only accept ':'.
 30881291            if (first <= JsonConstants.Space)
 891292            {
 891293                SkipWhiteSpace();
 891294                if (!HasMoreData(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 01295                {
 01296                    return false;
 1297                }
 881298                first = _buffer[_consumed];
 881299            }
 1300
 1301            // The next character must be a key / value separator. Validate and skip.
 30871302            if (first != JsonConstants.KeyValueSeparator)
 411303            {
 411304                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedSeparatorAfterPropertyNameNotFo
 1305            }
 1306
 30461307            _consumed++;
 30461308            _bytePositionInLine++;
 30461309            _tokenType = JsonTokenType.PropertyName;
 30461310            return true;
 30461311        }
 1312
 1313        private bool ConsumeString()
 79731314        {
 79731315            Debug.Assert(_buffer.Length >= _consumed + 1);
 79731316            Debug.Assert(_buffer[_consumed] == JsonConstants.Quote);
 1317
 1318            // Create local copy to avoid bounds checks.
 79731319            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 1320
 1321            // Vectorized search for either quote, backslash, or any control character.
 1322            // If the first found byte is a quote, we have reached an end of string, and
 1323            // can avoid validation.
 1324            // Otherwise, in the uncommon case, iterate one character at a time and validate.
 79731325            int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 1326
 79731327            if (idx >= 0)
 78851328            {
 78851329                byte foundByte = localBuffer[idx];
 78851330                if (foundByte == JsonConstants.Quote)
 46421331                {
 46421332                    _bytePositionInLine += idx + 2; // Add 2 for the start and end quotes.
 46421333                    ValueSpan = localBuffer.Slice(0, idx);
 46421334                    ValueIsEscaped = false;
 46421335                    _tokenType = JsonTokenType.String;
 46421336                    _consumed += idx + 2;
 46421337                    return true;
 1338                }
 1339                else
 32431340                {
 32431341                    return ConsumeStringAndValidate(localBuffer, idx);
 1342                }
 1343            }
 1344            else
 881345            {
 881346                if (IsLastSpan)
 881347                {
 881348                    _bytePositionInLine += localBuffer.Length + 1;  // Account for the start quote
 881349                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1350                }
 01351                return false;
 1352            }
 73971353        }
 1354
 1355        // Found a backslash or control characters which are considered invalid within a string.
 1356        // Search through the rest of the string one byte at a time.
 1357        // https://tools.ietf.org/html/rfc8259#section-7
 1358        private bool ConsumeStringAndValidate(ReadOnlySpan<byte> data, int idx)
 32431359        {
 32431360            Debug.Assert(idx >= 0 && idx < data.Length);
 32431361            Debug.Assert(data[idx] != JsonConstants.Quote);
 32431362            Debug.Assert(data[idx] == JsonConstants.BackSlash || data[idx] < JsonConstants.Space);
 1363
 32431364            long prevLineBytePosition = _bytePositionInLine;
 32431365            long prevLineNumber = _lineNumber;
 1366
 32431367            _bytePositionInLine += idx + 1; // Add 1 for the first quote
 1368
 32431369            bool nextCharEscaped = false;
 741711370            for (; idx < data.Length; idx++)
 384851371            {
 384851372                byte currentByte = data[idx];
 384851373                if (currentByte == JsonConstants.Quote)
 28671374                {
 28671375                    if (!nextCharEscaped)
 27551376                    {
 27551377                        goto Done;
 1378                    }
 1121379                    nextCharEscaped = false;
 1121380                }
 356181381                else if (currentByte == JsonConstants.BackSlash)
 92191382                {
 92191383                    nextCharEscaped = !nextCharEscaped;
 92191384                }
 263991385                else if (nextCharEscaped)
 69431386                {
 69431387                    if (!JsonConstants.EscapableChars.Contains(currentByte))
 251388                    {
 251389                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAfterEscapeWith
 1390                    }
 1391
 69181392                    if (currentByte == 'u')
 56701393                    {
 1394                        // Expecting 4 hex digits to follow the escaped 'u'
 56701395                        _bytePositionInLine++;  // move past the 'u'
 56701396                        if (ValidateHexDigits(data, idx + 1))
 55631397                        {
 55631398                            idx += 4;   // Skip the 4 hex digits, the for loop accounts for idx incrementing past the 'u
 55631399                        }
 1400                        else
 231401                        {
 1402                            // We found less than 4 hex digits. Check if there is more data to follow, otherwise throw.
 231403                            idx = data.Length;
 231404                            break;
 1405                        }
 1406
 55631407                    }
 68111408                    nextCharEscaped = false;
 68111409                }
 194561410                else if (currentByte < JsonConstants.Space)
 1341411                {
 1341412                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWithinString, curre
 1413                }
 1414
 354641415                _bytePositionInLine++;
 354641416            }
 1417
 2451418            if (idx >= data.Length)
 2451419            {
 2451420                if (IsLastSpan)
 2451421                {
 2451422                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1423                }
 01424                _lineNumber = prevLineNumber;
 01425                _bytePositionInLine = prevLineBytePosition;
 01426                return false;
 1427            }
 1428
 27551429        Done:
 27551430            _bytePositionInLine++;  // Add 1 for the end quote
 27551431            ValueSpan = data.Slice(0, idx);
 27551432            ValueIsEscaped = true;
 27551433            _tokenType = JsonTokenType.String;
 27551434            _consumed += idx + 2;
 27551435            return true;
 27551436        }
 1437
 1438        private bool ValidateHexDigits(ReadOnlySpan<byte> data, int idx)
 56701439        {
 451201440            for (int j = idx; j < data.Length; j++)
 225371441            {
 225371442                byte nextByte = data[j];
 225371443                if (!JsonReaderHelper.IsHexDigit(nextByte))
 841444                {
 841445                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidHexCharacterWithinString, ne
 1446                }
 224531447                if (j - idx >= 3)
 55631448                {
 55631449                    return true;
 1450                }
 168901451                _bytePositionInLine++;
 168901452            }
 1453
 231454            return false;
 55861455        }
 1456
 1457        // https://tools.ietf.org/html/rfc7159#section-6
 1458        private bool TryGetNumber(ReadOnlySpan<byte> data, out int consumed)
 269891459        {
 1460            // TODO: https://github.com/dotnet/runtime/issues/27837
 269891461            Debug.Assert(data.Length > 0);
 1462
 269891463            consumed = 0;
 269891464            int i = 0;
 1465
 269891466            ConsumeNumberResult signResult = ConsumeNegativeSign(ref data, ref i);
 269661467            if (signResult == ConsumeNumberResult.NeedMoreData)
 01468            {
 01469                return false;
 1470            }
 1471
 269661472            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1473
 269661474            byte nextByte = data[i];
 269661475            Debug.Assert(nextByte is >= (byte)'0' and <= (byte)'9');
 1476
 269661477            if (nextByte == '0')
 26701478            {
 26701479                ConsumeNumberResult result = ConsumeZero(ref data, ref i);
 26021480                if (result == ConsumeNumberResult.NeedMoreData)
 01481                {
 01482                    return false;
 1483                }
 26021484                if (result == ConsumeNumberResult.Success)
 20721485                {
 20721486                    goto Done;
 1487                }
 1488
 5301489                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 5301490                nextByte = data[i];
 5301491            }
 1492            else
 242961493            {
 242961494                i++;
 242961495                ConsumeNumberResult result = ConsumeIntegerDigits(ref data, ref i);
 242961496                if (result == ConsumeNumberResult.NeedMoreData)
 01497                {
 01498                    return false;
 1499                }
 242961500                if (result == ConsumeNumberResult.Success)
 141841501                {
 141841502                    goto Done;
 1503                }
 1504
 101121505                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 101121506                nextByte = data[i];
 101121507                if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 1931508                {
 1931509                    _bytePositionInLine += i;
 1931510                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, nextByt
 1511                }
 99191512            }
 1513
 104491514            Debug.Assert(nextByte is (byte)'.' or (byte)'E' or (byte)'e');
 1515
 104491516            if (nextByte == '.')
 63221517            {
 63221518                i++;
 63221519                ConsumeNumberResult result = ConsumeDecimalDigits(ref data, ref i);
 62391520                if (result == ConsumeNumberResult.NeedMoreData)
 01521                {
 01522                    return false;
 1523                }
 62391524                if (result == ConsumeNumberResult.Success)
 37141525                {
 37141526                    goto Done;
 1527                }
 1528
 25251529                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 25251530                nextByte = data[i];
 25251531                if (nextByte is not ((byte)'E' or (byte)'e'))
 791532                {
 791533                    _bytePositionInLine += i;
 791534                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedNextDigitEValueNotFound, ne
 1535                }
 24461536            }
 1537
 65731538            Debug.Assert(nextByte is (byte)'E' or (byte)'e');
 65731539            i++;
 1540
 65731541            signResult = ConsumeSign(ref data, ref i);
 64251542            if (signResult == ConsumeNumberResult.NeedMoreData)
 01543            {
 01544                return false;
 1545            }
 1546
 64251547            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1548
 64251549            i++;
 64251550            ConsumeNumberResult resultExponent = ConsumeIntegerDigits(ref data, ref i);
 64251551            if (resultExponent == ConsumeNumberResult.NeedMoreData)
 01552            {
 01553                return false;
 1554            }
 64251555            if (resultExponent == ConsumeNumberResult.Success)
 62691556            {
 62691557                goto Done;
 1558            }
 1559
 1561560            Debug.Assert(resultExponent == ConsumeNumberResult.OperationIncomplete);
 1561
 1561562            _bytePositionInLine += i;
 1561563            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, data[i]);
 1564
 262391565        Done:
 262391566            ValueSpan = data.Slice(0, i);
 262391567            consumed = i;
 262391568            return true;
 262391569        }
 1570
 1571        private ConsumeNumberResult ConsumeNegativeSign(ref ReadOnlySpan<byte> data, scoped ref int i)
 269891572        {
 269891573            byte nextByte = data[i];
 1574
 269891575            if (nextByte == '-')
 6161576            {
 6161577                i++;
 6161578                if (i >= data.Length)
 71579                {
 71580                    if (IsLastSpan)
 71581                    {
 71582                        _bytePositionInLine += i;
 71583                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1584                    }
 01585                    return ConsumeNumberResult.NeedMoreData;
 1586                }
 1587
 6091588                nextByte = data[i];
 6091589                if (!JsonHelpers.IsDigit(nextByte))
 161590                {
 161591                    _bytePositionInLine += i;
 161592                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nex
 1593                }
 5931594            }
 269661595            return ConsumeNumberResult.OperationIncomplete;
 269661596        }
 1597
 1598        private ConsumeNumberResult ConsumeZero(ref ReadOnlySpan<byte> data, scoped ref int i)
 26701599        {
 26701600            Debug.Assert(data[i] == (byte)'0');
 26701601            i++;
 1602            byte nextByte;
 26701603            if (i < data.Length)
 26631604            {
 26631605                nextByte = data[i];
 26631606                if (JsonConstants.Delimiters.Contains(nextByte))
 20651607                {
 20651608                    return ConsumeNumberResult.Success;
 1609                }
 5981610            }
 1611            else
 71612            {
 71613                if (IsLastSpan)
 71614                {
 1615                    // A payload containing a single value: "0" is valid
 1616                    // If we are dealing with multi-value JSON,
 1617                    // ConsumeNumber will validate that we have a delimiter following the "0".
 71618                    return ConsumeNumberResult.Success;
 1619                }
 1620                else
 01621                {
 01622                    return ConsumeNumberResult.NeedMoreData;
 1623                }
 1624            }
 5981625            nextByte = data[i];
 5981626            if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 681627            {
 681628                _bytePositionInLine += i;
 681629                ThrowHelper.ThrowJsonReaderException(ref this,
 681630                    JsonHelpers.IsInRangeInclusive(nextByte, '0', '9') ? ExceptionResource.InvalidLeadingZeroInNumber : 
 681631                    nextByte);
 1632            }
 1633
 5301634            return ConsumeNumberResult.OperationIncomplete;
 26021635        }
 1636
 1637        private ConsumeNumberResult ConsumeIntegerDigits(ref ReadOnlySpan<byte> data, scoped ref int i)
 369601638        {
 369601639            byte nextByte = default;
 2090041640            for (; i < data.Length; i++)
 1228751641            {
 1228751642                nextByte = data[i];
 1228751643                if (!JsonHelpers.IsDigit(nextByte))
 368531644                {
 368531645                    break;
 1646                }
 860221647            }
 369601648            if (i >= data.Length)
 1071649            {
 1071650                if (IsLastSpan)
 1071651                {
 1652                    // A payload containing a single value of integers (e.g. "12") is valid
 1653                    // If we are dealing with multi-value JSON,
 1654                    // ConsumeNumber will validate that we have a delimiter following the integer.
 1071655                    return ConsumeNumberResult.Success;
 1656                }
 1657                else
 01658                {
 01659                    return ConsumeNumberResult.NeedMoreData;
 1660                }
 1661            }
 368531662            if (JsonConstants.Delimiters.Contains(nextByte))
 240601663            {
 240601664                return ConsumeNumberResult.Success;
 1665            }
 1666
 127931667            return ConsumeNumberResult.OperationIncomplete;
 369601668        }
 1669
 1670        private ConsumeNumberResult ConsumeDecimalDigits(ref ReadOnlySpan<byte> data, scoped ref int i)
 63221671        {
 63221672            if (i >= data.Length)
 81673            {
 81674                if (IsLastSpan)
 81675                {
 81676                    _bytePositionInLine += i;
 81677                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1678                }
 01679                return ConsumeNumberResult.NeedMoreData;
 1680            }
 63141681            byte nextByte = data[i];
 63141682            if (!JsonHelpers.IsDigit(nextByte))
 751683            {
 751684                _bytePositionInLine += i;
 751685                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterDecimal, next
 1686            }
 62391687            i++;
 1688
 62391689            return ConsumeIntegerDigits(ref data, ref i);
 62391690        }
 1691
 1692        private ConsumeNumberResult ConsumeSign(ref ReadOnlySpan<byte> data, scoped ref int i)
 65731693        {
 65731694            if (i >= data.Length)
 151695            {
 151696                if (IsLastSpan)
 151697                {
 151698                    _bytePositionInLine += i;
 151699                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1700                }
 01701                return ConsumeNumberResult.NeedMoreData;
 1702            }
 1703
 65581704            byte nextByte = data[i];
 65581705            if (nextByte is (byte)'+' or (byte)'-')
 58271706            {
 58271707                i++;
 58271708                if (i >= data.Length)
 111709                {
 111710                    if (IsLastSpan)
 111711                    {
 111712                        _bytePositionInLine += i;
 111713                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1714                    }
 01715                    return ConsumeNumberResult.NeedMoreData;
 1716                }
 58161717                nextByte = data[i];
 58161718            }
 1719
 65471720            if (!JsonHelpers.IsDigit(nextByte))
 1221721            {
 1221722                _bytePositionInLine += i;
 1221723                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nextByt
 1724            }
 1725
 64251726            return ConsumeNumberResult.OperationIncomplete;
 64251727        }
 1728
 1729        private bool ConsumeNextTokenOrRollback(byte marker)
 816981730        {
 816981731            int prevConsumed = _consumed;
 816981732            long prevPosition = _bytePositionInLine;
 816981733            long prevLineNumber = _lineNumber;
 816981734            JsonTokenType prevTokenType = _tokenType;
 816981735            bool prevTrailingCommaBeforeComment = _trailingCommaBeforeComment;
 816981736            ConsumeTokenResult result = ConsumeNextToken(marker);
 788841737            if (result == ConsumeTokenResult.Success)
 788621738            {
 788621739                return true;
 1740            }
 221741            if (result == ConsumeTokenResult.NotEnoughDataRollBackState)
 01742            {
 01743                _consumed = prevConsumed;
 01744                _tokenType = prevTokenType;
 01745                _bytePositionInLine = prevPosition;
 01746                _lineNumber = prevLineNumber;
 01747                _trailingCommaBeforeComment = prevTrailingCommaBeforeComment;
 01748            }
 221749            return false;
 788841750        }
 1751
 1752        /// <summary>
 1753        /// This method consumes the next token regardless of whether we are inside an object or an array.
 1754        /// For an object, it reads the next property name token. For an array, it just reads the next value.
 1755        /// </summary>
 1756        private ConsumeTokenResult ConsumeNextToken(byte marker)
 816981757        {
 816981758            if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
 494801759            {
 494801760                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
 01761                {
 01762                    if (marker == JsonConstants.Slash)
 01763                    {
 01764                        return ConsumeComment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackS
 1765                    }
 01766                    if (_tokenType == JsonTokenType.Comment)
 01767                    {
 01768                        return ConsumeNextTokenFromLastNonCommentToken();
 1769                    }
 01770                }
 1771                else
 494801772                {
 494801773                    Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 494801774                    return ConsumeNextTokenUntilAfterAllCommentsAreSkipped(marker);
 1775                }
 01776            }
 1777
 322181778            if (_bitStack.CurrentDepth == 0)
 671779            {
 671780                if (_readerOptions.AllowMultipleValues)
 01781                {
 01782                    return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBac
 1783                }
 1784
 671785                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 1786            }
 1787
 321511788            if (marker == JsonConstants.ListSeparator)
 261621789            {
 261621790                _consumed++;
 261621791                _bytePositionInLine++;
 1792
 261621793                if (_consumed >= (uint)_buffer.Length)
 71794                {
 71795                    if (IsLastSpan)
 71796                    {
 71797                        _consumed--;
 71798                        _bytePositionInLine--;
 71799                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1800                    }
 01801                    return ConsumeTokenResult.NotEnoughDataRollBackState;
 1802                }
 261551803                byte first = _buffer[_consumed];
 1804
 1805                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 261551806                if (first <= JsonConstants.Space)
 4971807                {
 4971808                    SkipWhiteSpace();
 1809                    // The next character must be a start of a property name or value.
 4971810                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01811                    {
 01812                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1813                    }
 4961814                    first = _buffer[_consumed];
 4961815                }
 1816
 261541817                TokenStartIndex = _consumed;
 1818
 261541819                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && first == JsonConstants.Slash)
 01820                {
 01821                    _trailingCommaBeforeComment = true;
 01822                    return ConsumeComment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState
 1823                }
 1824
 261541825                if (_inObject)
 3621826                {
 3621827                    if (first != JsonConstants.Quote)
 361828                    {
 361829                        if (first == JsonConstants.CloseBrace)
 91830                        {
 91831                            if (_readerOptions.AllowTrailingCommas)
 41832                            {
 41833                                EndObject();
 41834                                return ConsumeTokenResult.Success;
 1835                            }
 51836                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1837                        }
 271838                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1839                    }
 3261840                    return ConsumePropertyName() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 1841                }
 1842                else
 257921843                {
 257921844                    if (first == JsonConstants.CloseBracket)
 15031845                    {
 15031846                        if (_readerOptions.AllowTrailingCommas)
 14851847                        {
 14851848                            EndArray();
 14851849                            return ConsumeTokenResult.Success;
 1850                        }
 181851                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1852                    }
 242891853                    return ConsumeValue(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackSt
 1854                }
 1855            }
 59891856            else if (marker == JsonConstants.CloseBrace)
 251857            {
 251858                EndObject();
 141859            }
 59641860            else if (marker == JsonConstants.CloseBracket)
 58261861            {
 58261862                EndArray();
 58251863            }
 1864            else
 1381865            {
 1381866                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 1867            }
 58391868            return ConsumeTokenResult.Success;
 788841869        }
 1870
 1871        private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentToken()
 01872        {
 01873            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Allow);
 01874            Debug.Assert(_tokenType == JsonTokenType.Comment);
 1875
 01876            if (JsonReaderHelper.IsTokenTypePrimitive(_previousTokenType))
 01877            {
 01878                _tokenType = _inObject ? JsonTokenType.StartObject : JsonTokenType.StartArray;
 01879            }
 1880            else
 01881            {
 01882                _tokenType = _previousTokenType;
 01883            }
 1884
 01885            Debug.Assert(_tokenType != JsonTokenType.Comment);
 1886
 01887            if (!HasMoreData())
 01888            {
 01889                goto RollBack;
 1890            }
 1891
 01892            byte first = _buffer[_consumed];
 1893
 1894            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01895            if (first <= JsonConstants.Space)
 01896            {
 01897                SkipWhiteSpace();
 01898                if (!HasMoreData())
 01899                {
 01900                    goto RollBack;
 1901                }
 01902                first = _buffer[_consumed];
 01903            }
 1904
 01905            if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None)
 01906            {
 01907                if (_readerOptions.AllowMultipleValues)
 01908                {
 01909                    return ReadFirstToken(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 1910                }
 1911
 01912                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
 1913            }
 1914
 01915            Debug.Assert(first != JsonConstants.Slash);
 1916
 01917            TokenStartIndex = _consumed;
 1918
 01919            if (first == JsonConstants.ListSeparator)
 01920            {
 1921                // A comma without some JSON value preceding it is invalid
 01922                if (_previousTokenType <= JsonTokenType.StartObject || _previousTokenType == JsonTokenType.StartArray ||
 01923                {
 01924                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueAfter
 1925                }
 1926
 01927                _consumed++;
 01928                _bytePositionInLine++;
 1929
 01930                if (_consumed >= (uint)_buffer.Length)
 01931                {
 01932                    if (IsLastSpan)
 01933                    {
 01934                        _consumed--;
 01935                        _bytePositionInLine--;
 01936                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1937                    }
 01938                    goto RollBack;
 1939                }
 01940                first = _buffer[_consumed];
 1941
 1942                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01943                if (first <= JsonConstants.Space)
 01944                {
 01945                    SkipWhiteSpace();
 1946                    // The next character must be a start of a property name or value.
 01947                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01948                    {
 01949                        goto RollBack;
 1950                    }
 01951                    first = _buffer[_consumed];
 01952                }
 1953
 01954                TokenStartIndex = _consumed;
 1955
 01956                if (first == JsonConstants.Slash)
 01957                {
 01958                    _trailingCommaBeforeComment = true;
 01959                    if (ConsumeComment())
 01960                    {
 01961                        goto Done;
 1962                    }
 1963                    else
 01964                    {
 01965                        goto RollBack;
 1966                    }
 1967                }
 1968
 01969                if (_inObject)
 01970                {
 01971                    if (first != JsonConstants.Quote)
 01972                    {
 01973                        if (first == JsonConstants.CloseBrace)
 01974                        {
 01975                            if (_readerOptions.AllowTrailingCommas)
 01976                            {
 01977                                EndObject();
 01978                                goto Done;
 1979                            }
 01980                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1981                        }
 1982
 01983                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1984                    }
 01985                    if (ConsumePropertyName())
 01986                    {
 01987                        goto Done;
 1988                    }
 1989                    else
 01990                    {
 01991                        goto RollBack;
 1992                    }
 1993                }
 1994                else
 01995                {
 01996                    if (first == JsonConstants.CloseBracket)
 01997                    {
 01998                        if (_readerOptions.AllowTrailingCommas)
 01999                        {
 02000                            EndArray();
 02001                            goto Done;
 2002                        }
 02003                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2004                    }
 2005
 02006                    if (ConsumeValue(first))
 02007                    {
 02008                        goto Done;
 2009                    }
 2010                    else
 02011                    {
 02012                        goto RollBack;
 2013                    }
 2014                }
 2015            }
 02016            else if (first == JsonConstants.CloseBrace)
 02017            {
 02018                EndObject();
 02019            }
 02020            else if (first == JsonConstants.CloseBracket)
 02021            {
 02022                EndArray();
 02023            }
 02024            else if (_tokenType == JsonTokenType.None)
 02025            {
 02026                if (ReadFirstToken(first))
 02027                {
 02028                    goto Done;
 2029                }
 2030                else
 02031                {
 02032                    goto RollBack;
 2033                }
 2034            }
 02035            else if (_tokenType == JsonTokenType.StartObject)
 02036            {
 02037                Debug.Assert(first != JsonConstants.CloseBrace);
 02038                if (first != JsonConstants.Quote)
 02039                {
 02040                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound, fi
 2041                }
 2042
 02043                int prevConsumed = _consumed;
 02044                long prevPosition = _bytePositionInLine;
 02045                long prevLineNumber = _lineNumber;
 02046                if (!ConsumePropertyName())
 02047                {
 2048                    // roll back potential changes
 02049                    _consumed = prevConsumed;
 02050                    _tokenType = JsonTokenType.StartObject;
 02051                    _bytePositionInLine = prevPosition;
 02052                    _lineNumber = prevLineNumber;
 02053                    goto RollBack;
 2054                }
 02055                goto Done;
 2056            }
 02057            else if (_tokenType == JsonTokenType.StartArray)
 02058            {
 02059                Debug.Assert(first != JsonConstants.CloseBracket);
 02060                if (!ConsumeValue(first))
 02061                {
 02062                    goto RollBack;
 2063                }
 02064                goto Done;
 2065            }
 02066            else if (_tokenType == JsonTokenType.PropertyName)
 02067            {
 02068                if (!ConsumeValue(first))
 02069                {
 02070                    goto RollBack;
 2071                }
 02072                goto Done;
 2073            }
 2074            else
 02075            {
 02076                Debug.Assert(_tokenType is JsonTokenType.EndArray or JsonTokenType.EndObject);
 02077                if (_inObject)
 02078                {
 02079                    Debug.Assert(first != JsonConstants.CloseBrace);
 02080                    if (first != JsonConstants.Quote)
 02081                    {
 02082                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2083                    }
 2084
 02085                    if (ConsumePropertyName())
 02086                    {
 02087                        goto Done;
 2088                    }
 2089                    else
 02090                    {
 02091                        goto RollBack;
 2092                    }
 2093                }
 2094                else
 02095                {
 02096                    Debug.Assert(first != JsonConstants.CloseBracket);
 2097
 02098                    if (ConsumeValue(first))
 02099                    {
 02100                        goto Done;
 2101                    }
 2102                    else
 02103                    {
 02104                        goto RollBack;
 2105                    }
 2106                }
 2107            }
 2108
 02109        Done:
 02110            return ConsumeTokenResult.Success;
 2111
 02112        RollBack:
 02113            return ConsumeTokenResult.NotEnoughDataRollBackState;
 02114        }
 2115
 2116        private bool SkipAllComments(scoped ref byte marker)
 494802117        {
 633042118            while (marker == JsonConstants.Slash)
 143552119            {
 143552120                if (SkipComment())
 139712121                {
 139712122                    if (!HasMoreData())
 192123                    {
 192124                        goto IncompleteNoRollback;
 2125                    }
 2126
 138302127                    marker = _buffer[_consumed];
 2128
 2129                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 138302130                    if (marker <= JsonConstants.Space)
 13772131                    {
 13772132                        SkipWhiteSpace();
 13772133                        if (!HasMoreData())
 32134                        {
 32135                            goto IncompleteNoRollback;
 2136                        }
 13712137                        marker = _buffer[_consumed];
 13712138                    }
 138242139                }
 2140                else
 02141                {
 02142                    goto IncompleteNoRollback;
 2143                }
 138242144            }
 489492145            return true;
 2146
 222147        IncompleteNoRollback:
 222148            return false;
 489712149        }
 2150
 2151        private bool SkipAllComments(scoped ref byte marker, ExceptionResource resource)
 367802152        {
 469582153            while (marker == JsonConstants.Slash)
 102902154            {
 102902155                if (SkipComment())
 102092156                {
 2157                    // The next character must be a start of a property name or value.
 102092158                    if (!HasMoreData(resource))
 02159                    {
 02160                        goto IncompleteRollback;
 2161                    }
 2162
 101792163                    marker = _buffer[_consumed];
 2164
 2165                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 101792166                    if (marker <= JsonConstants.Space)
 26962167                    {
 26962168                        SkipWhiteSpace();
 2169                        // The next character must be a start of a property name or value.
 26962170                        if (!HasMoreData(resource))
 02171                        {
 02172                            goto IncompleteRollback;
 2173                        }
 26952174                        marker = _buffer[_consumed];
 26952175                    }
 101782176                }
 2177                else
 02178                {
 02179                    goto IncompleteRollback;
 2180                }
 101782181            }
 366682182            return true;
 2183
 02184        IncompleteRollback:
 02185            return false;
 366682186        }
 2187
 2188        private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkipped(byte marker)
 494802189        {
 494802190            if (!SkipAllComments(ref marker))
 222191            {
 222192                goto IncompleteNoRollback;
 2193            }
 2194
 489492195            TokenStartIndex = _consumed;
 2196
 489492197            if (_tokenType == JsonTokenType.StartObject)
 11312198            {
 11312199                if (marker == JsonConstants.CloseBrace)
 142200                {
 142201                    EndObject();
 142202                }
 2203                else
 11172204                {
 11172205                    if (marker != JsonConstants.Quote)
 552206                    {
 552207                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2208                    }
 2209
 10622210                    int prevConsumed = _consumed;
 10622211                    long prevPosition = _bytePositionInLine;
 10622212                    long prevLineNumber = _lineNumber;
 10622213                    if (!ConsumePropertyName())
 02214                    {
 2215                        // roll back potential changes
 02216                        _consumed = prevConsumed;
 02217                        _tokenType = JsonTokenType.StartObject;
 02218                        _bytePositionInLine = prevPosition;
 02219                        _lineNumber = prevLineNumber;
 02220                        goto IncompleteNoRollback;
 2221                    }
 02222                    goto Done;
 2223                }
 142224            }
 478182225            else if (_tokenType == JsonTokenType.StartArray)
 22672226            {
 22672227                if (marker == JsonConstants.CloseBracket)
 1262228                {
 1262229                    EndArray();
 1262230                }
 2231                else
 21412232                {
 21412233                    if (!ConsumeValue(marker))
 02234                    {
 02235                        goto IncompleteNoRollback;
 2236                    }
 02237                    goto Done;
 2238                }
 1262239            }
 455512240            else if (_tokenType == JsonTokenType.PropertyName)
 6912241            {
 6912242                if (!ConsumeValue(marker))
 02243                {
 02244                    goto IncompleteNoRollback;
 2245                }
 02246                goto Done;
 2247            }
 448602248            else if (_bitStack.CurrentDepth == 0)
 1012249            {
 1012250                if (_readerOptions.AllowMultipleValues)
 02251                {
 02252                    return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBac
 2253                }
 2254
 1012255                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 2256            }
 447592257            else if (marker == JsonConstants.ListSeparator)
 368492258            {
 368492259                _consumed++;
 368492260                _bytePositionInLine++;
 2261
 368492262                if (_consumed >= (uint)_buffer.Length)
 692263                {
 692264                    if (IsLastSpan)
 692265                    {
 692266                        _consumed--;
 692267                        _bytePositionInLine--;
 692268                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 2269                    }
 02270                    return ConsumeTokenResult.NotEnoughDataRollBackState;
 2271                }
 367802272                marker = _buffer[_consumed];
 2273
 2274                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 367802275                if (marker <= JsonConstants.Space)
 20062276                {
 20062277                    SkipWhiteSpace();
 2278                    // The next character must be a start of a property name or value.
 20062279                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02280                    {
 02281                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2282                    }
 20062283                    marker = _buffer[_consumed];
 20062284                }
 2285
 367802286                if (!SkipAllComments(ref marker, ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02287                {
 02288                    goto IncompleteRollback;
 2289                }
 2290
 366682291                TokenStartIndex = _consumed;
 2292
 366682293                if (_inObject)
 3572294                {
 3572295                    if (marker != JsonConstants.Quote)
 452296                    {
 452297                        if (marker == JsonConstants.CloseBrace)
 162298                        {
 162299                            if (_readerOptions.AllowTrailingCommas)
 122300                            {
 122301                                EndObject();
 122302                                goto Done;
 2303                            }
 42304                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 2305                        }
 2306
 292307                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2308                    }
 3122309                    return ConsumePropertyName() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 2310                }
 2311                else
 363112312                {
 363112313                    if (marker == JsonConstants.CloseBracket)
 17272314                    {
 17272315                        if (_readerOptions.AllowTrailingCommas)
 17212316                        {
 17212317                            EndArray();
 17212318                            goto Done;
 2319                        }
 62320                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2321                    }
 2322
 345842323                    return ConsumeValue(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackS
 2324                }
 2325            }
 79102326            else if (marker == JsonConstants.CloseBrace)
 772327            {
 772328                EndObject();
 422329            }
 78332330            else if (marker == JsonConstants.CloseBracket)
 76362331            {
 76362332                EndArray();
 76322333            }
 2334            else
 1972335            {
 1972336                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 2337            }
 2338
 132612339        Done:
 132612340            return ConsumeTokenResult.Success;
 222341        IncompleteNoRollback:
 222342            return ConsumeTokenResult.IncompleteNoRollBackNecessary;
 02343        IncompleteRollback:
 02344            return ConsumeTokenResult.NotEnoughDataRollBackState;
 474522345        }
 2346
 2347        private bool SkipComment()
 311472348        {
 2349            // Create local copy to avoid bounds checks.
 311472350            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 2351
 311472352            if (localBuffer.Length > 0)
 309392353            {
 309392354                byte marker = localBuffer[0];
 309392355                if (marker == JsonConstants.Slash)
 239522356                {
 239522357                    return SkipSingleLineComment(localBuffer.Slice(1), out _);
 2358                }
 69872359                else if (marker == JsonConstants.Asterisk)
 68192360                {
 68192361                    return SkipMultiLineComment(localBuffer.Slice(1), out _);
 2362                }
 2363                else
 1682364                {
 1682365                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, JsonC
 2366                }
 2367            }
 2368
 2082369            if (IsLastSpan)
 2082370            {
 2082371                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, JsonConst
 2372            }
 02373            return false;
 304932374        }
 2375
 2376        private bool SkipSingleLineComment(ReadOnlySpan<byte> localBuffer, out int idx)
 239522377        {
 239522378            idx = FindLineSeparator(localBuffer);
 2379            int toConsume;
 239042380            if (idx != -1)
 233982381            {
 233982382                toConsume = idx;
 233982383                if (localBuffer[idx] == JsonConstants.LineFeed)
 180232384                {
 180232385                    goto EndOfComment;
 2386                }
 2387
 2388                // If we are here, we have definintely found a \r. So now to check if \n follows.
 53752389                Debug.Assert(localBuffer[idx] == JsonConstants.CarriageReturn);
 2390
 53752391                if (idx < localBuffer.Length - 1)
 53592392                {
 53592393                    if (localBuffer[idx + 1] == JsonConstants.LineFeed)
 38472394                    {
 38472395                        toConsume++;
 38472396                    }
 2397
 53592398                    goto EndOfComment;
 2399                }
 2400
 162401                if (IsLastSpan)
 162402                {
 162403                    goto EndOfComment;
 2404                }
 2405                else
 02406                {
 2407                    // there might be LF in the next segment
 02408                    return false;
 2409                }
 2410            }
 2411
 5062412            if (IsLastSpan)
 5062413            {
 5062414                idx = localBuffer.Length;
 5062415                toConsume = idx;
 2416                // Assume everything on this line is a comment and there is no more data.
 5062417                _bytePositionInLine += 2 + localBuffer.Length;
 5062418                goto Done;
 2419            }
 2420            else
 02421            {
 02422                return false;
 2423            }
 2424
 233982425        EndOfComment:
 233982426            toConsume++;
 233982427            _bytePositionInLine = 0;
 233982428            _lineNumber++;
 2429
 239042430        Done:
 239042431            _consumed += 2 + toConsume;
 239042432            return true;
 239042433        }
 2434
 2435        private int FindLineSeparator(ReadOnlySpan<byte> localBuffer)
 239522436        {
 239522437            int totalIdx = 0;
 321522438            while (true)
 321522439            {
 321522440                int idx = localBuffer.IndexOfAny(JsonConstants.LineFeed, JsonConstants.CarriageReturn, JsonConstants.Sta
 2441
 321522442                if (idx == -1)
 5062443                {
 5062444                    return -1;
 2445                }
 2446
 316462447                totalIdx += idx;
 2448
 316462449                if (localBuffer[idx] != JsonConstants.StartingByteOfNonStandardSeparator)
 233982450                {
 233982451                    return totalIdx;
 2452                }
 2453
 82482454                totalIdx++;
 82482455                localBuffer = localBuffer.Slice(idx + 1);
 2456
 82482457                ThrowOnDangerousLineSeparator(localBuffer);
 82002458            }
 239042459        }
 2460
 2461        // assumes first byte (JsonConstants.StartingByteOfNonStandardSeparator) is already read
 2462        private void ThrowOnDangerousLineSeparator(ReadOnlySpan<byte> localBuffer)
 82482463        {
 2464            // \u2028 and \u2029 are considered respectively line and paragraph separators
 2465            // UTF-8 representation for them is E2, 80, A8/A9
 2466            // we have already read E2, we need to check for remaining 2 bytes
 2467
 82482468            if (localBuffer.Length < 2)
 1562469            {
 1562470                return;
 2471            }
 2472
 80922473            byte next = localBuffer[1];
 80922474            if (localBuffer[0] == 0x80 && (next is 0xA8 or 0xA9))
 482475            {
 482476                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfLineSeparator);
 2477            }
 82002478        }
 2479
 2480        private bool SkipMultiLineComment(ReadOnlySpan<byte> localBuffer, out int idx)
 68192481        {
 68192482            idx = 0;
 229492483            while (true)
 229492484            {
 229492485                int foundIdx = localBuffer.Slice(idx).IndexOf(JsonConstants.Slash);
 229492486                if (foundIdx == -1)
 2302487                {
 2302488                    if (IsLastSpan)
 2302489                    {
 2302490                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfCommentNotFound);
 2491                    }
 02492                    return false;
 2493                }
 227192494                if (foundIdx != 0 && localBuffer[foundIdx + idx - 1] == JsonConstants.Asterisk)
 65892495                {
 2496                    // foundIdx points just after '*' in the end-of-comment delimiter. Hence increment idx by one
 2497                    // position less to make it point right before beginning of end-of-comment delimiter i.e. */
 65892498                    idx += foundIdx - 1;
 65892499                    break;
 2500                }
 161302501                idx += foundIdx + 1;
 161302502            }
 2503
 2504            // Consume the /* and */ characters that are part of the multi-line comment.
 2505            // idx points right before the final '*' (which is right before the last '/'). Hence increment _consumed
 2506            // by 4 to exclude the start/end-of-comment delimiters.
 65892507            _consumed += 4 + idx;
 2508
 65892509            (int newLines, int newLineIndex) = JsonReaderHelper.CountNewLines(localBuffer.Slice(0, idx));
 65892510            _lineNumber += newLines;
 65892511            if (newLineIndex != -1)
 29122512            {
 2513                // newLineIndex points at last newline character and byte positions in the new line start
 2514                // after that. Hence add 1 to skip the newline character.
 29122515                _bytePositionInLine = idx - newLineIndex + 1;
 29122516            }
 2517            else
 36772518            {
 36772519                _bytePositionInLine += 4 + idx;
 36772520            }
 65892521            return true;
 65892522        }
 2523
 2524        private bool ConsumeComment()
 02525        {
 2526            // Create local copy to avoid bounds checks.
 02527            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 2528
 02529            if (localBuffer.Length > 0)
 02530            {
 02531                byte marker = localBuffer[0];
 02532                if (marker == JsonConstants.Slash)
 02533                {
 02534                    return ConsumeSingleLineComment(localBuffer.Slice(1), _consumed);
 2535                }
 02536                else if (marker == JsonConstants.Asterisk)
 02537                {
 02538                    return ConsumeMultiLineComment(localBuffer.Slice(1), _consumed);
 2539                }
 2540                else
 02541                {
 02542                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAtStartOfComment, m
 2543                }
 2544            }
 2545
 02546            if (IsLastSpan)
 02547            {
 02548                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReadingComment)
 2549            }
 02550            return false;
 02551        }
 2552
 2553        private bool ConsumeSingleLineComment(ReadOnlySpan<byte> localBuffer, int previousConsumed)
 02554        {
 02555            if (!SkipSingleLineComment(localBuffer, out int idx))
 02556            {
 02557                return false;
 2558            }
 2559
 2560            // Exclude the // at start of the comment. idx points right before the line separator
 2561            // at the end of the comment.
 02562            ValueSpan = _buffer.Slice(previousConsumed + 2, idx);
 02563            if (_tokenType != JsonTokenType.Comment)
 02564            {
 02565                _previousTokenType = _tokenType;
 02566            }
 02567            _tokenType = JsonTokenType.Comment;
 02568            return true;
 02569        }
 2570
 2571        private bool ConsumeMultiLineComment(ReadOnlySpan<byte> localBuffer, int previousConsumed)
 02572        {
 02573            if (!SkipMultiLineComment(localBuffer, out int idx))
 02574            {
 02575                return false;
 2576            }
 2577
 2578            // Exclude the /* at start of the comment. idx already points right before the terminal '*/'
 2579            // for the end of multiline comment.
 02580            ValueSpan = _buffer.Slice(previousConsumed + 2, idx);
 02581            if (_tokenType != JsonTokenType.Comment)
 02582            {
 02583                _previousTokenType = _tokenType;
 02584            }
 02585            _tokenType = JsonTokenType.Comment;
 02586            return true;
 02587        }
 2588
 2589        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 02590        private string DebuggerDisplay => $"TokenType = {DebugTokenType}, TokenStartIndex = {TokenStartIndex}, Consumed 
 2591
 2592        // Using TokenType.ToString() (or {TokenType}) fails to render in the debug window. The
 2593        // message "The runtime refused to evaluate the expression at this time." is shown. This
 2594        // is a workaround until we root cause and fix the issue.
 2595        private string DebugTokenType
 02596            => TokenType switch
 02597            {
 02598                JsonTokenType.Comment => nameof(JsonTokenType.Comment),
 02599                JsonTokenType.EndArray => nameof(JsonTokenType.EndArray),
 02600                JsonTokenType.EndObject => nameof(JsonTokenType.EndObject),
 02601                JsonTokenType.False => nameof(JsonTokenType.False),
 02602                JsonTokenType.None => nameof(JsonTokenType.None),
 02603                JsonTokenType.Null => nameof(JsonTokenType.Null),
 02604                JsonTokenType.Number => nameof(JsonTokenType.Number),
 02605                JsonTokenType.PropertyName => nameof(JsonTokenType.PropertyName),
 02606                JsonTokenType.StartArray => nameof(JsonTokenType.StartArray),
 02607                JsonTokenType.StartObject => nameof(JsonTokenType.StartObject),
 02608                JsonTokenType.String => nameof(JsonTokenType.String),
 02609                JsonTokenType.True => nameof(JsonTokenType.True),
 02610                _ => ((byte)TokenType).ToString()
 02611            };
 2612
 2613        private ReadOnlySpan<byte> GetUnescapedSpan()
 02614        {
 02615            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 02616            if (ValueIsEscaped)
 02617            {
 02618                span = JsonReaderHelper.GetUnescaped(span);
 02619            }
 2620
 02621            return span;
 02622        }
 2623    }
 2624}

D:\runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Reader\Utf8JsonReader.MultiSegment.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;
 7
 8namespace System.Text.Json
 9{
 10    public ref partial struct Utf8JsonReader
 11    {
 12        /// <summary>
 13        /// Constructs a new <see cref="Utf8JsonReader"/> instance.
 14        /// </summary>
 15        /// <param name="jsonData">The ReadOnlySequence&lt;byte&gt; containing the UTF-8 encoded JSON text to process.</
 16        /// <param name="isFinalBlock">True when the input span contains the entire data to process.
 17        /// Set to false only if it is known that the input span contains partial data with more data to follow.</param>
 18        /// <param name="state">If this is the first call to the ctor, pass in a default state. Otherwise,
 19        /// capture the state from the previous instance of the <see cref="Utf8JsonReader"/> and pass that back.</param>
 20        /// <remarks>
 21        /// Since this type is a ref struct, it is a stack-only type and all the limitations of ref structs apply to it.
 22        /// This is the reason why the ctor accepts a <see cref="JsonReaderState"/>.
 23        /// </remarks>
 24        public Utf8JsonReader(ReadOnlySequence<byte> jsonData, bool isFinalBlock, JsonReaderState state)
 025        {
 026            _buffer = jsonData.FirstSpan;
 27
 028            _isFinalBlock = isFinalBlock;
 029            _isInputSequence = true;
 30
 031            _lineNumber = state._lineNumber;
 032            _bytePositionInLine = state._bytePositionInLine;
 033            _inObject = state._inObject;
 034            _isNotPrimitive = state._isNotPrimitive;
 035            ValueIsEscaped = state._valueIsEscaped;
 036            _trailingCommaBeforeComment = state._trailingCommaBeforeComment;
 037            _tokenType = state._tokenType;
 038            _previousTokenType = state._previousTokenType;
 039            _readerOptions = state._readerOptions;
 040            if (_readerOptions.MaxDepth == 0)
 041            {
 042                _readerOptions.MaxDepth = JsonReaderOptions.DefaultMaxDepth;  // If max depth is not set, revert to the 
 043            }
 044            _bitStack = state._bitStack;
 45
 046            _consumed = 0;
 047            TokenStartIndex = 0;
 048            _totalConsumed = 0;
 49
 050            ValueSpan = ReadOnlySpan<byte>.Empty;
 51
 052            _sequence = jsonData;
 053            HasValueSequence = false;
 054            ValueSequence = ReadOnlySequence<byte>.Empty;
 55
 056            if (jsonData.IsSingleSegment)
 057            {
 058                _nextPosition = default;
 059                _currentPosition = jsonData.Start;
 060                _isLastSegment = isFinalBlock;
 061                _isMultiSegment = false;
 062            }
 63            else
 064            {
 065                _currentPosition = jsonData.Start;
 066                _nextPosition = _currentPosition;
 67
 068                bool firstSegmentIsEmpty = _buffer.Length == 0;
 069                if (firstSegmentIsEmpty)
 070                {
 71                    // Once we find a non-empty segment, we need to set current position to it.
 72                    // Therefore, track the next position in a copy before it gets advanced to the next segment.
 073                    SequencePosition previousNextPosition = _nextPosition;
 074                    while (jsonData.TryGet(ref _nextPosition, out ReadOnlyMemory<byte> memory, advance: true))
 075                    {
 76                        // _currentPosition should point to the segment right befor the segment that _nextPosition point
 077                        _currentPosition = previousNextPosition;
 078                        if (memory.Length != 0)
 079                        {
 080                            _buffer = memory.Span;
 081                            break;
 82                        }
 083                        previousNextPosition = _nextPosition;
 084                    }
 085                }
 86
 87                // If firstSegmentIsEmpty is true,
 88                //    only check if we have reached the last segment but do not advance _nextPosition. The while loop ab
 89                //    Otherwise, we would end up skipping a segment (i.e. advance = false).
 90                // If firstSegmentIsEmpty is false,
 91                //    make sure to advance _nextPosition so that it is no longer the same as _currentPosition (i.e. adva
 092                _isLastSegment = !jsonData.TryGet(ref _nextPosition, out _, advance: !firstSegmentIsEmpty) && isFinalBlo
 93
 094                Debug.Assert(!_nextPosition.Equals(_currentPosition));
 95
 096                _isMultiSegment = true;
 097            }
 098        }
 99
 100        /// <summary>
 101        /// Constructs a new <see cref="Utf8JsonReader"/> instance.
 102        /// </summary>
 103        /// <param name="jsonData">The ReadOnlySequence&lt;byte&gt; containing the UTF-8 encoded JSON text to process.</
 104        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonReader"/>
 105        /// that is different from the JSON RFC (for example how to handle comments or maximum depth allowed when readin
 106        /// By default, the <see cref="Utf8JsonReader"/> follows the JSON RFC strictly (i.e. comments within the JSON ar
 107        /// <remarks>
 108        ///   <para>
 109        ///     Since this type is a ref struct, it is a stack-only type and all the limitations of ref structs apply to
 110        ///   </para>
 111        ///   <para>
 112        ///     This assumes that the entire JSON payload is passed in (equivalent to <see cref="IsFinalBlock"/> = true)
 113        ///   </para>
 114        /// </remarks>
 115        public Utf8JsonReader(ReadOnlySequence<byte> jsonData, JsonReaderOptions options = default)
 0116            : this(jsonData, isFinalBlock: true, new JsonReaderState(options))
 0117        {
 0118        }
 119
 120        private bool ReadMultiSegment()
 0121        {
 0122            bool retVal = false;
 0123            HasValueSequence = false;
 0124            ValueIsEscaped = false;
 0125            ValueSpan = default;
 0126            ValueSequence = default;
 127
 0128            if (!HasMoreDataMultiSegment())
 0129            {
 0130                goto Done;
 131            }
 132
 0133            byte first = _buffer[_consumed];
 134
 135            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 136            // SkipWhiteSpace only skips the whitespace characters as defined by JSON RFC 8259 section 2.
 137            // We do not validate if 'first' is an invalid JSON byte here (such as control characters).
 138            // Those cases are captured in ConsumeNextToken and ConsumeValue.
 0139            if (first <= JsonConstants.Space)
 0140            {
 0141                SkipWhiteSpaceMultiSegment();
 0142                if (!HasMoreDataMultiSegment())
 0143                {
 0144                    goto Done;
 145                }
 0146                first = _buffer[_consumed];
 0147            }
 148
 0149            TokenStartIndex = BytesConsumed;
 150
 0151            if (_tokenType == JsonTokenType.None)
 0152            {
 0153                goto ReadFirstToken;
 154            }
 155
 0156            if (first == JsonConstants.Slash)
 0157            {
 0158                retVal = ConsumeNextTokenOrRollbackMultiSegment(first);
 0159                goto Done;
 160            }
 161
 0162            if (_tokenType == JsonTokenType.StartObject)
 0163            {
 0164                if (first == JsonConstants.CloseBrace)
 0165                {
 0166                    EndObject();
 0167                }
 168                else
 0169                {
 0170                    if (first != JsonConstants.Quote)
 0171                    {
 0172                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 173                    }
 174
 0175                    long prevTotalConsumed = _totalConsumed;
 0176                    int prevConsumed = _consumed;
 0177                    long prevPosition = _bytePositionInLine;
 0178                    long prevLineNumber = _lineNumber;
 0179                    SequencePosition copy = _currentPosition;
 0180                    retVal = ConsumePropertyNameMultiSegment();
 0181                    if (!retVal)
 0182                    {
 183                        // roll back potential changes
 0184                        _consumed = prevConsumed;
 0185                        _tokenType = JsonTokenType.StartObject;
 0186                        _bytePositionInLine = prevPosition;
 0187                        _lineNumber = prevLineNumber;
 0188                        _totalConsumed = prevTotalConsumed;
 0189                        _currentPosition = copy;
 0190                    }
 0191                    goto Done;
 192                }
 0193            }
 0194            else if (_tokenType == JsonTokenType.StartArray)
 0195            {
 0196                if (first == JsonConstants.CloseBracket)
 0197                {
 0198                    EndArray();
 0199                }
 200                else
 0201                {
 0202                    retVal = ConsumeValueMultiSegment(first);
 0203                    goto Done;
 204                }
 0205            }
 0206            else if (_tokenType == JsonTokenType.PropertyName)
 0207            {
 0208                retVal = ConsumeValueMultiSegment(first);
 0209                goto Done;
 210            }
 211            else
 0212            {
 0213                retVal = ConsumeNextTokenOrRollbackMultiSegment(first);
 0214                goto Done;
 215            }
 216
 0217            retVal = true;
 218
 0219        Done:
 0220            return retVal;
 221
 0222        ReadFirstToken:
 0223            retVal = ReadFirstTokenMultiSegment(first);
 0224            goto Done;
 0225        }
 226
 227        private bool ValidateStateAtEndOfData()
 0228        {
 0229            Debug.Assert(_isNotPrimitive && IsLastSpan);
 230
 0231            if (_bitStack.CurrentDepth != 0)
 0232            {
 0233                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ZeroDepthAtEnd);
 234            }
 235
 0236            if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && _tokenType == JsonTokenType.Comment)
 0237            {
 0238                return false;
 239            }
 240
 0241            if (_tokenType != JsonTokenType.EndArray && _tokenType != JsonTokenType.EndObject)
 0242            {
 0243                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndOfJsonNonPrimitive);
 244            }
 245
 0246            return true;
 0247        }
 248
 249        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 250        private bool HasMoreDataMultiSegment()
 0251        {
 0252            if (_consumed >= (uint)_buffer.Length)
 0253            {
 0254                if (_isNotPrimitive && IsLastSpan)
 0255                {
 0256                    if (!ValidateStateAtEndOfData())
 0257                    {
 0258                        return false;
 259                    }
 0260                }
 261
 0262                if (!GetNextSpan())
 0263                {
 0264                    if (_isNotPrimitive && IsLastSpan)
 0265                    {
 0266                        ValidateStateAtEndOfData();
 0267                    }
 0268                    return false;
 269                }
 0270            }
 0271            return true;
 0272        }
 273
 274        // Unlike the parameter-less overload of HasMoreData, if there is no more data when this method is called, we kn
 275        // This is because, this method is only called after a ',' (i.e. we expect a value/property name) or after
 276        // a property name, which means it must be followed by a value.
 277        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 278        private bool HasMoreDataMultiSegment(ExceptionResource resource)
 0279        {
 0280            if (_consumed >= (uint)_buffer.Length)
 0281            {
 0282                if (IsLastSpan)
 0283                {
 0284                    ThrowHelper.ThrowJsonReaderException(ref this, resource);
 285                }
 0286                if (!GetNextSpan())
 0287                {
 0288                    if (IsLastSpan)
 0289                    {
 0290                        ThrowHelper.ThrowJsonReaderException(ref this, resource);
 291                    }
 0292                    return false;
 293                }
 0294            }
 0295            return true;
 0296        }
 297
 298        private bool GetNextSpan()
 0299        {
 300            ReadOnlyMemory<byte> memory;
 0301            while (true)
 0302            {
 0303                Debug.Assert(!_isMultiSegment || _currentPosition.GetObject() is not null);
 0304                SequencePosition copy = _currentPosition;
 0305                _currentPosition = _nextPosition;
 0306                bool noMoreData = !_sequence.TryGet(ref _nextPosition, out memory, advance: true);
 0307                if (noMoreData)
 0308                {
 0309                    _currentPosition = copy;
 0310                    _isLastSegment = true;
 0311                    return false;
 312                }
 0313                if (memory.Length != 0)
 0314                {
 0315                    break;
 316                }
 317                // _currentPosition needs to point to last non-empty segment
 318                // Since memory.Length == 0, we need to revert back to previous.
 0319                _currentPosition = copy;
 0320                Debug.Assert(!_isMultiSegment || _currentPosition.GetObject() is not null);
 0321            }
 322
 0323            if (_isFinalBlock)
 0324            {
 0325                _isLastSegment = !_sequence.TryGet(ref _nextPosition, out _, advance: false);
 0326            }
 327
 0328            _buffer = memory.Span;
 0329            _totalConsumed += _consumed;
 0330            _consumed = 0;
 331
 0332            return true;
 0333        }
 334
 335        private bool ReadFirstTokenMultiSegment(byte first)
 0336        {
 0337            if (first == JsonConstants.OpenBrace)
 0338            {
 0339                _bitStack.SetFirstBit();
 0340                _tokenType = JsonTokenType.StartObject;
 0341                ValueSpan = _buffer.Slice(_consumed, 1);
 0342                _consumed++;
 0343                _bytePositionInLine++;
 0344                _inObject = true;
 0345                _isNotPrimitive = true;
 0346            }
 0347            else if (first == JsonConstants.OpenBracket)
 0348            {
 0349                _bitStack.ResetFirstBit();
 0350                _tokenType = JsonTokenType.StartArray;
 0351                ValueSpan = _buffer.Slice(_consumed, 1);
 0352                _consumed++;
 0353                _bytePositionInLine++;
 0354                _isNotPrimitive = true;
 0355            }
 356            else
 0357            {
 0358                if (JsonHelpers.IsDigit(first) || first == '-')
 0359                {
 0360                    if (!TryGetNumberMultiSegment(_buffer.Slice(_consumed), out int numberOfBytes))
 0361                    {
 0362                        return false;
 363                    }
 0364                    _tokenType = JsonTokenType.Number;
 0365                    _consumed += numberOfBytes;
 0366                }
 0367                else if (!ConsumeValueMultiSegment(first))
 0368                {
 0369                    return false;
 370                }
 371
 0372                _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray;
 373                // Intentionally fall out of the if-block to return true
 0374            }
 0375            return true;
 0376        }
 377
 378        private void SkipWhiteSpaceMultiSegment()
 0379        {
 0380            while (true)
 0381            {
 0382                SkipWhiteSpace();
 383
 0384                if (_consumed < _buffer.Length)
 0385                {
 0386                    break;
 387                }
 388
 0389                if (!GetNextSpan())
 0390                {
 0391                    break;
 392                }
 0393            }
 0394        }
 395
 396        /// <summary>
 397        /// This method contains the logic for processing the next value token and determining
 398        /// what type of data it is.
 399        /// </summary>
 400        private bool ConsumeValueMultiSegment(byte marker)
 0401        {
 0402            while (true)
 0403            {
 0404                Debug.Assert((_trailingCommaBeforeComment && _readerOptions.CommentHandling == JsonCommentHandling.Allow
 0405                Debug.Assert((_trailingCommaBeforeComment && marker != JsonConstants.Slash) || !_trailingCommaBeforeComm
 0406                _trailingCommaBeforeComment = false;
 407
 0408                if (marker == JsonConstants.Quote)
 0409                {
 0410                    return ConsumeStringMultiSegment();
 411                }
 0412                else if (marker == JsonConstants.OpenBrace)
 0413                {
 0414                    StartObject();
 0415                }
 0416                else if (marker == JsonConstants.OpenBracket)
 0417                {
 0418                    StartArray();
 0419                }
 0420                else if (JsonHelpers.IsDigit(marker) || marker == '-')
 0421                {
 0422                    return ConsumeNumberMultiSegment();
 423                }
 0424                else if (marker == 'f')
 0425                {
 0426                    return ConsumeLiteralMultiSegment(JsonConstants.FalseValue, JsonTokenType.False);
 427                }
 0428                else if (marker == 't')
 0429                {
 0430                    return ConsumeLiteralMultiSegment(JsonConstants.TrueValue, JsonTokenType.True);
 431                }
 0432                else if (marker == 'n')
 0433                {
 0434                    return ConsumeLiteralMultiSegment(JsonConstants.NullValue, JsonTokenType.Null);
 435                }
 436                else
 0437                {
 0438                    switch (_readerOptions.CommentHandling)
 439                    {
 440                        case JsonCommentHandling.Disallow:
 0441                            break;
 442                        case JsonCommentHandling.Allow:
 0443                            if (marker == JsonConstants.Slash)
 0444                            {
 0445                                SequencePosition copy = _currentPosition;
 0446                                if (!SkipOrConsumeCommentMultiSegmentWithRollback())
 0447                                {
 0448                                    _currentPosition = copy;
 0449                                    return false;
 450                                }
 0451                                return true;
 452                            }
 0453                            break;
 454                        default:
 0455                            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 0456                            if (marker == JsonConstants.Slash)
 0457                            {
 0458                                SequencePosition copy = _currentPosition;
 0459                                if (SkipCommentMultiSegment(out _))
 0460                                {
 0461                                    if (_consumed >= (uint)_buffer.Length)
 0462                                    {
 0463                                        if (_isNotPrimitive && IsLastSpan && _tokenType != JsonTokenType.EndArray && _to
 0464                                        {
 0465                                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndO
 466                                        }
 0467                                        if (!GetNextSpan())
 0468                                        {
 0469                                            if (_isNotPrimitive && IsLastSpan && _tokenType != JsonTokenType.EndArray &&
 0470                                            {
 0471                                                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.Invalid
 472                                            }
 0473                                            _currentPosition = copy;
 0474                                            return false;
 475                                        }
 0476                                    }
 477
 0478                                    marker = _buffer[_consumed];
 479
 480                                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not ne
 0481                                    if (marker <= JsonConstants.Space)
 0482                                    {
 0483                                        SkipWhiteSpaceMultiSegment();
 0484                                        if (!HasMoreDataMultiSegment())
 0485                                        {
 0486                                            _currentPosition = copy;
 0487                                            return false;
 488                                        }
 0489                                        marker = _buffer[_consumed];
 0490                                    }
 491
 0492                                    TokenStartIndex = BytesConsumed;
 493
 494                                    // Skip comments and consume the actual JSON value.
 0495                                    continue;
 496                                }
 0497                                _currentPosition = copy;
 0498                                return false;
 499                            }
 0500                            break;
 501                    }
 0502                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, marke
 503                }
 0504                break;
 505            }
 0506            return true;
 0507        }
 508
 509        // Consumes 'null', or 'true', or 'false'
 510        private bool ConsumeLiteralMultiSegment(ReadOnlySpan<byte> literal, JsonTokenType tokenType)
 0511        {
 0512            ReadOnlySpan<byte> span = _buffer.Slice(_consumed);
 0513            Debug.Assert(span.Length > 0);
 0514            Debug.Assert(span[0] == 'n' || span[0] == 't' || span[0] == 'f');
 515
 0516            int consumed = literal.Length;
 517
 0518            if (!span.StartsWith(literal))
 0519            {
 0520                int prevConsumed = _consumed;
 0521                if (CheckLiteralMultiSegment(span, literal, out consumed))
 0522                {
 0523                    goto Done;
 524                }
 0525                _consumed = prevConsumed;
 0526                return false;
 527            }
 528
 0529            ValueSpan = span.Slice(0, literal.Length);
 0530            HasValueSequence = false;
 0531        Done:
 0532            _tokenType = tokenType;
 0533            _consumed += consumed;
 0534            _bytePositionInLine += consumed;
 0535            return true;
 0536        }
 537
 538        private unsafe bool CheckLiteralMultiSegment(ReadOnlySpan<byte> span, ReadOnlySpan<byte> literal, out int consum
 0539        {
 0540            Debug.Assert(span.Length > 0 && span[0] == literal[0] && literal.Length <= JsonConstants.MaximumLiteralLengt
 541
 0542            Span<byte> readSoFar = stackalloc byte[JsonConstants.MaximumLiteralLength];
 0543            int written = 0;
 544
 0545            long prevTotalConsumed = _totalConsumed;
 0546            SequencePosition copy = _currentPosition;
 0547            if (span.Length >= literal.Length || IsLastSpan)
 0548            {
 0549                _bytePositionInLine += FindMismatch(span, literal);
 550
 0551                int amountToWrite = AmountToWrite(span, _bytePositionInLine, readSoFar, written);
 0552                span.Slice(0, amountToWrite).CopyTo(readSoFar);
 0553                written += amountToWrite;
 0554                goto Throw;
 555            }
 556            else
 0557            {
 0558                if (!literal.StartsWith(span))
 0559                {
 0560                    _bytePositionInLine += FindMismatch(span, literal);
 0561                    int amountToWrite = AmountToWrite(span, _bytePositionInLine, readSoFar, written);
 0562                    span.Slice(0, amountToWrite).CopyTo(readSoFar);
 0563                    written += amountToWrite;
 0564                    goto Throw;
 565                }
 566
 0567                ReadOnlySpan<byte> leftToMatch = literal.Slice(span.Length);
 568
 0569                SequencePosition startPosition = _currentPosition;
 0570                int startConsumed = _consumed;
 0571                int alreadyMatched = literal.Length - leftToMatch.Length;
 0572                while (true)
 0573                {
 0574                    _totalConsumed += alreadyMatched;
 0575                    _bytePositionInLine += alreadyMatched;
 0576                    if (!GetNextSpan())
 0577                    {
 0578                        _totalConsumed = prevTotalConsumed;
 0579                        consumed = default;
 0580                        _currentPosition = copy;
 0581                        if (IsLastSpan)
 0582                        {
 0583                            goto Throw;
 584                        }
 0585                        return false;
 586                    }
 587
 0588                    int amountToWrite = Math.Min(span.Length, readSoFar.Length - written);
 0589                    span.Slice(0, amountToWrite).CopyTo(readSoFar.Slice(written));
 0590                    written += amountToWrite;
 591
 0592                    span = _buffer;
 593
 0594                    if (span.StartsWith(leftToMatch))
 0595                    {
 0596                        HasValueSequence = true;
 0597                        SequencePosition start = new SequencePosition(startPosition.GetObject(), startPosition.GetIntege
 0598                        SequencePosition end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetIn
 0599                        ValueSequence = _sequence.Slice(start, end);
 0600                        consumed = leftToMatch.Length;
 0601                        return true;
 602                    }
 603
 0604                    if (!leftToMatch.StartsWith(span))
 0605                    {
 0606                        _bytePositionInLine += FindMismatch(span, leftToMatch);
 607
 0608                        amountToWrite = AmountToWrite(span, _bytePositionInLine, readSoFar, written);
 0609                        span.Slice(0, amountToWrite).CopyTo(readSoFar.Slice(written));
 0610                        written += amountToWrite;
 611
 0612                        goto Throw;
 613                    }
 614
 0615                    leftToMatch = leftToMatch.Slice(span.Length);
 0616                    alreadyMatched = span.Length;
 0617                }
 618            }
 619
 620            static int AmountToWrite(ReadOnlySpan<byte> span, long bytePositionInLine, ReadOnlySpan<byte> readSoFar, int
 0621            {
 0622                return Math.Min(
 0623                    readSoFar.Length - written,
 0624                    Math.Min(span.Length, (int)bytePositionInLine + 1));
 0625            }
 0626        Throw:
 0627            _totalConsumed = prevTotalConsumed;
 0628            consumed = default;
 0629            _currentPosition = copy;
 0630            throw GetInvalidLiteralMultiSegment(readSoFar.Slice(0, written).ToArray());
 0631        }
 632
 633        private static int FindMismatch(ReadOnlySpan<byte> span, ReadOnlySpan<byte> literal)
 0634        {
 0635            Debug.Assert(span.Length > 0);
 636
 0637            int indexOfFirstMismatch = span.CommonPrefixLength(literal);
 638
 0639            Debug.Assert(indexOfFirstMismatch >= 0 && indexOfFirstMismatch < literal.Length);
 640
 0641            return indexOfFirstMismatch;
 0642        }
 643
 644        private JsonException GetInvalidLiteralMultiSegment(ReadOnlySpan<byte> span)
 0645        {
 0646            byte firstByte = span[0];
 647
 648            ExceptionResource resource;
 0649            switch (firstByte)
 650            {
 651                case (byte)'t':
 0652                    resource = ExceptionResource.ExpectedTrue;
 0653                    break;
 654                case (byte)'f':
 0655                    resource = ExceptionResource.ExpectedFalse;
 0656                    break;
 657                default:
 0658                    Debug.Assert(firstByte == 'n');
 0659                    resource = ExceptionResource.ExpectedNull;
 0660                    break;
 661            }
 0662            return ThrowHelper.GetJsonReaderException(ref this, resource, nextByte: default, bytes: span);
 0663        }
 664
 665        private bool ConsumeNumberMultiSegment()
 0666        {
 0667            if (!TryGetNumberMultiSegment(_buffer.Slice(_consumed), out int consumed))
 0668            {
 0669                return false;
 670            }
 671
 0672            _tokenType = JsonTokenType.Number;
 0673            _consumed += consumed;
 674
 0675            if (_consumed >= (uint)_buffer.Length)
 0676            {
 0677                Debug.Assert(IsLastSpan);
 678
 679                // If there is no more data, and the JSON is not a single value, throw.
 0680                if (_isNotPrimitive)
 0681                {
 0682                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, _buffer
 683                }
 0684            }
 685
 686            // If there is more data and the JSON is not a single value, assert that there is an end of number delimiter
 687            // Else, if either the JSON is a single value XOR if there is no more data, don't assert anything since ther
 0688            Debug.Assert(
 0689                ((_consumed < _buffer.Length) &&
 0690                !_isNotPrimitive &&
 0691                JsonConstants.Delimiters.Contains(_buffer[_consumed]))
 0692                || (_isNotPrimitive ^ (_consumed >= (uint)_buffer.Length)));
 693
 0694            return true;
 0695        }
 696
 697        private bool ConsumePropertyNameMultiSegment()
 0698        {
 0699            _trailingCommaBeforeComment = false;
 700
 0701            if (!ConsumeStringMultiSegment())
 0702            {
 0703                return false;
 704            }
 705
 0706            if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 0707            {
 0708                return false;
 709            }
 710
 0711            byte first = _buffer[_consumed];
 712
 713            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 714            // We do not validate if 'first' is an invalid JSON byte here (such as control characters).
 715            // Those cases are captured below where we only accept ':'.
 0716            if (first <= JsonConstants.Space)
 0717            {
 0718                SkipWhiteSpaceMultiSegment();
 0719                if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 0720                {
 0721                    return false;
 722                }
 0723                first = _buffer[_consumed];
 0724            }
 725
 726            // The next character must be a key / value separator. Validate and skip.
 0727            if (first != JsonConstants.KeyValueSeparator)
 0728            {
 0729                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedSeparatorAfterPropertyNameNotFo
 730            }
 731
 0732            _consumed++;
 0733            _bytePositionInLine++;
 0734            _tokenType = JsonTokenType.PropertyName;
 0735            return true;
 0736        }
 737
 738        private bool ConsumeStringMultiSegment()
 0739        {
 0740            Debug.Assert(_buffer.Length >= _consumed + 1);
 0741            Debug.Assert(_buffer[_consumed] == JsonConstants.Quote);
 742
 743            // Create local copy to avoid bounds checks.
 0744            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 745
 746            // Vectorized search for either quote, backslash, or any control character.
 747            // If the first found byte is a quote, we have reached an end of string, and
 748            // can avoid validation.
 749            // Otherwise, in the uncommon case, iterate one character at a time and validate.
 0750            int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 751
 0752            if (idx >= 0)
 0753            {
 0754                byte foundByte = localBuffer[idx];
 0755                if (foundByte == JsonConstants.Quote)
 0756                {
 0757                    _bytePositionInLine += idx + 2; // Add 2 for the start and end quotes.
 0758                    ValueSpan = localBuffer.Slice(0, idx);
 0759                    HasValueSequence = false;
 0760                    ValueIsEscaped = false;
 0761                    _tokenType = JsonTokenType.String;
 0762                    _consumed += idx + 2;
 0763                    return true;
 764                }
 765                else
 0766                {
 0767                    return ConsumeStringAndValidateMultiSegment(localBuffer, idx);
 768                }
 769            }
 770            else
 0771            {
 0772                if (IsLastSpan)
 0773                {
 0774                    _bytePositionInLine += localBuffer.Length + 1;  // Account for the start quote
 0775                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 776                }
 0777                return ConsumeStringNextSegment();
 778            }
 0779        }
 780
 781        private bool ConsumeStringNextSegment()
 0782        {
 0783            PartialStateForRollback rollBackState = CaptureState();
 784
 785            SequencePosition end;
 0786            HasValueSequence = true;
 0787            int leftOver = _buffer.Length - _consumed;
 788
 0789            while (true)
 0790            {
 0791                if (!GetNextSpan())
 0792                {
 0793                    if (IsLastSpan)
 0794                    {
 0795                        _bytePositionInLine += leftOver;
 0796                        RollBackState(rollBackState, isError: true);
 0797                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 798                    }
 0799                    RollBackState(rollBackState);
 0800                    return false;
 801                }
 802
 803                //Create local copy to avoid bounds checks.
 0804                ReadOnlySpan<byte> localBuffer = _buffer;
 0805                int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 806
 0807                if (idx >= 0)
 0808                {
 0809                    byte foundByte = localBuffer[idx];
 0810                    if (foundByte == JsonConstants.Quote)
 0811                    {
 0812                        end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + idx);
 0813                        _bytePositionInLine += leftOver + idx + 1;  // Add 1 for the end quote of the string.
 0814                        _totalConsumed += leftOver;
 0815                        _consumed = idx + 1;    // Add 1 for the end quote of the string.
 0816                        ValueIsEscaped = false;
 0817                        break;
 818                    }
 819                    else
 0820                    {
 0821                        _bytePositionInLine += leftOver + idx;
 0822                        ValueIsEscaped = true;
 823
 0824                        bool nextCharEscaped = false;
 0825                        while (true)
 0826                        {
 0827                        StartOfLoop:
 0828                            for (; idx < localBuffer.Length; idx++)
 0829                            {
 0830                                byte currentByte = localBuffer[idx];
 0831                                if (currentByte == JsonConstants.Quote)
 0832                                {
 0833                                    if (!nextCharEscaped)
 0834                                    {
 0835                                        goto Done;
 836                                    }
 0837                                    nextCharEscaped = false;
 0838                                }
 0839                                else if (currentByte == JsonConstants.BackSlash)
 0840                                {
 0841                                    nextCharEscaped = !nextCharEscaped;
 0842                                }
 0843                                else if (nextCharEscaped)
 0844                                {
 0845                                    if (!JsonConstants.EscapableChars.Contains(currentByte))
 0846                                    {
 0847                                        RollBackState(rollBackState, isError: true);
 0848                                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacte
 849                                    }
 850
 0851                                    if (currentByte == 'u')
 0852                                    {
 853                                        // Expecting 4 hex digits to follow the escaped 'u'
 0854                                        _bytePositionInLine++;  // move past the 'u'
 855
 0856                                        int numberOfHexDigits = 0;
 0857                                        int j = idx + 1;
 0858                                        while (true)
 0859                                        {
 0860                                            for (; j < localBuffer.Length; j++)
 0861                                            {
 0862                                                byte nextByte = localBuffer[j];
 0863                                                if (!JsonReaderHelper.IsHexDigit(nextByte))
 0864                                                {
 0865                                                    RollBackState(rollBackState, isError: true);
 0866                                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.Inv
 867                                                }
 0868                                                numberOfHexDigits++;
 0869                                                _bytePositionInLine++;
 0870                                                if (numberOfHexDigits >= 4)
 0871                                                {
 0872                                                    nextCharEscaped = false;
 0873                                                    idx = j + 1; // Skip the 4 hex digits, the for loop accounts for idx
 0874                                                    goto StartOfLoop;
 875                                                }
 0876                                            }
 877
 0878                                            if (!GetNextSpan())
 0879                                            {
 0880                                                if (IsLastSpan)
 0881                                                {
 0882                                                    RollBackState(rollBackState, isError: true);
 0883                                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.End
 884                                                }
 885
 886                                                // We found less than 4 hex digits.
 0887                                                RollBackState(rollBackState);
 0888                                                return false;
 889                                            }
 890
 0891                                            _totalConsumed += localBuffer.Length;
 892
 0893                                            localBuffer = _buffer;
 0894                                            j = 0;
 0895                                        }
 896                                    }
 0897                                    nextCharEscaped = false;
 0898                                }
 0899                                else if (currentByte < JsonConstants.Space)
 0900                                {
 0901                                    RollBackState(rollBackState, isError: true);
 0902                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWit
 903                                }
 904
 0905                                _bytePositionInLine++;
 0906                            }
 907
 0908                            if (!GetNextSpan())
 0909                            {
 0910                                if (IsLastSpan)
 0911                                {
 0912                                    RollBackState(rollBackState, isError: true);
 0913                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound
 914                                }
 0915                                RollBackState(rollBackState);
 0916                                return false;
 917                            }
 918
 0919                            _totalConsumed += localBuffer.Length;
 0920                            localBuffer = _buffer;
 0921                            idx = 0;
 0922                        }
 923
 0924                    Done:
 0925                        _bytePositionInLine++;  // Add 1 for the end quote of the string.
 0926                        _consumed = idx + 1;    // Add 1 for the end quote of the string.
 0927                        _totalConsumed += leftOver;
 0928                        end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + idx);
 0929                        break;
 930                    }
 931                }
 932
 0933                _totalConsumed += localBuffer.Length;
 0934                _bytePositionInLine += localBuffer.Length;
 0935            }
 936
 0937            SequencePosition start = rollBackState.GetStartPosition(offset: 1); // Offset for the starting quote
 0938            ValueSequence = _sequence.Slice(start, end);
 0939            _tokenType = JsonTokenType.String;
 0940            return true;
 0941        }
 942
 943        // Found a backslash or control characters which are considered invalid within a string.
 944        // Search through the rest of the string one byte at a time.
 945        // https://tools.ietf.org/html/rfc8259#section-7
 946        private bool ConsumeStringAndValidateMultiSegment(ReadOnlySpan<byte> data, int idx)
 0947        {
 0948            Debug.Assert(idx >= 0 && idx < data.Length);
 0949            Debug.Assert(data[idx] != JsonConstants.Quote);
 0950            Debug.Assert(data[idx] == JsonConstants.BackSlash || data[idx] < JsonConstants.Space);
 951
 0952            PartialStateForRollback rollBackState = CaptureState();
 953
 954            SequencePosition end;
 0955            HasValueSequence = false;
 0956            int leftOverFromConsumed = _buffer.Length - _consumed;
 957
 0958            _bytePositionInLine += idx + 1; // Add 1 for the first quote
 959
 0960            bool nextCharEscaped = false;
 0961            while (true)
 0962            {
 0963            StartOfLoop:
 0964                for (; idx < data.Length; idx++)
 0965                {
 0966                    byte currentByte = data[idx];
 0967                    if (currentByte == JsonConstants.Quote)
 0968                    {
 0969                        if (!nextCharEscaped)
 0970                        {
 0971                            goto Done;
 972                        }
 0973                        nextCharEscaped = false;
 0974                    }
 0975                    else if (currentByte == JsonConstants.BackSlash)
 0976                    {
 0977                        nextCharEscaped = !nextCharEscaped;
 0978                    }
 0979                    else if (nextCharEscaped)
 0980                    {
 0981                        if (!JsonConstants.EscapableChars.Contains(currentByte))
 0982                        {
 0983                            RollBackState(rollBackState, isError: true);
 0984                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAfterEscape
 985                        }
 986
 0987                        if (currentByte == 'u')
 0988                        {
 989                            // Expecting 4 hex digits to follow the escaped 'u'
 0990                            _bytePositionInLine++;  // move past the 'u'
 991
 0992                            int numberOfHexDigits = 0;
 0993                            int j = idx + 1;
 0994                            while (true)
 0995                            {
 0996                                for (; j < data.Length; j++)
 0997                                {
 0998                                    byte nextByte = data[j];
 0999                                    if (!JsonReaderHelper.IsHexDigit(nextByte))
 01000                                    {
 01001                                        RollBackState(rollBackState, isError: true);
 01002                                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidHexChara
 1003                                    }
 01004                                    numberOfHexDigits++;
 01005                                    _bytePositionInLine++;
 01006                                    if (numberOfHexDigits >= 4)
 01007                                    {
 01008                                        nextCharEscaped = false;
 01009                                        idx = j + 1; // Skip the 4 hex digits, the for loop accounts for idx incrementin
 01010                                        goto StartOfLoop;
 1011                                    }
 01012                                }
 1013
 01014                                if (!GetNextSpan())
 01015                                {
 01016                                    if (IsLastSpan)
 01017                                    {
 01018                                        RollBackState(rollBackState, isError: true);
 01019                                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotF
 1020                                    }
 1021
 1022                                    // We found less than 4 hex digits.
 01023                                    RollBackState(rollBackState);
 01024                                    return false;
 1025                                }
 1026
 1027                                // Do not add the left over for the first segment to total consumed
 01028                                if (HasValueSequence)
 01029                                {
 01030                                    _totalConsumed += data.Length;
 01031                                }
 1032
 01033                                data = _buffer;
 01034                                j = 0;
 01035                                HasValueSequence = true;
 01036                            }
 1037                        }
 01038                        nextCharEscaped = false;
 01039                    }
 01040                    else if (currentByte < JsonConstants.Space)
 01041                    {
 01042                        RollBackState(rollBackState, isError: true);
 01043                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWithinString, c
 1044                    }
 1045
 01046                    _bytePositionInLine++;
 01047                }
 1048
 01049                if (!GetNextSpan())
 01050                {
 01051                    if (IsLastSpan)
 01052                    {
 01053                        RollBackState(rollBackState, isError: true);
 01054                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1055                    }
 01056                    RollBackState(rollBackState);
 01057                    return false;
 1058                }
 1059
 1060                // Do not add the left over for the first segment to total consumed
 01061                if (HasValueSequence)
 01062                {
 01063                    _totalConsumed += data.Length;
 01064                }
 1065
 01066                data = _buffer;
 01067                idx = 0;
 01068                HasValueSequence = true;
 01069            }
 1070
 01071        Done:
 01072            if (HasValueSequence)
 01073            {
 01074                _bytePositionInLine++;  // Add 1 for the end quote of the string.
 01075                _consumed = idx + 1;    // Add 1 for the end quote of the string.
 01076                _totalConsumed += leftOverFromConsumed;
 01077                end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + idx);
 01078                SequencePosition start = rollBackState.GetStartPosition(offset: 1); // Offset for the starting quote
 01079                ValueSequence = _sequence.Slice(start, end);
 01080            }
 1081            else
 01082            {
 01083                _bytePositionInLine++;  // Add 1 for the end quote
 01084                _consumed += idx + 2;
 01085                ValueSpan = data.Slice(0, idx);
 01086            }
 1087
 01088            ValueIsEscaped = true;
 01089            _tokenType = JsonTokenType.String;
 01090            return true;
 01091        }
 1092
 1093        private void RollBackState(scoped in PartialStateForRollback state, bool isError = false)
 01094        {
 01095            _totalConsumed = state._prevTotalConsumed;
 1096
 1097            // Don't roll back byte position in line for invalid JSON since that is provided
 1098            // to the user within the exception.
 01099            if (!isError)
 01100            {
 01101                _bytePositionInLine = state._prevBytePositionInLine;
 01102            }
 1103
 01104            _consumed = state._prevConsumed;
 01105            _currentPosition = state._prevCurrentPosition;
 01106        }
 1107
 1108        // https://tools.ietf.org/html/rfc7159#section-6
 1109        private bool TryGetNumberMultiSegment(ReadOnlySpan<byte> data, out int consumed)
 01110        {
 1111            // TODO: https://github.com/dotnet/runtime/issues/27837
 01112            Debug.Assert(data.Length > 0);
 1113
 01114            PartialStateForRollback rollBackState = CaptureState();
 1115
 01116            consumed = 0;
 01117            int i = 0;
 1118
 01119            ConsumeNumberResult signResult = ConsumeNegativeSignMultiSegment(ref data, ref i, rollBackState);
 01120            if (signResult == ConsumeNumberResult.NeedMoreData)
 01121            {
 01122                RollBackState(rollBackState);
 01123                return false;
 1124            }
 1125
 01126            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1127
 01128            byte nextByte = data[i];
 01129            Debug.Assert(nextByte is >= (byte)'0' and <= (byte)'9');
 1130
 01131            if (nextByte == '0')
 01132            {
 01133                ConsumeNumberResult result = ConsumeZeroMultiSegment(ref data, ref i, rollBackState);
 01134                if (result == ConsumeNumberResult.NeedMoreData)
 01135                {
 01136                    RollBackState(rollBackState);
 01137                    return false;
 1138                }
 01139                if (result == ConsumeNumberResult.Success)
 01140                {
 01141                    goto Done;
 1142                }
 1143
 01144                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01145                nextByte = data[i];
 01146            }
 1147            else
 01148            {
 01149                ConsumeNumberResult result = ConsumeIntegerDigitsMultiSegment(ref data, ref i);
 01150                if (result == ConsumeNumberResult.NeedMoreData)
 01151                {
 01152                    RollBackState(rollBackState);
 01153                    return false;
 1154                }
 01155                if (result == ConsumeNumberResult.Success)
 01156                {
 01157                    goto Done;
 1158                }
 1159
 01160                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01161                nextByte = data[i];
 01162                if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 01163                {
 01164                    RollBackState(rollBackState, isError: true);
 01165                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, nextByt
 1166                }
 01167            }
 1168
 01169            Debug.Assert(nextByte is (byte)'.' or (byte)'E' or (byte)'e');
 1170
 01171            if (nextByte == '.')
 01172            {
 01173                i++;
 01174                _bytePositionInLine++;
 01175                ConsumeNumberResult result = ConsumeDecimalDigitsMultiSegment(ref data, ref i, rollBackState);
 01176                if (result == ConsumeNumberResult.NeedMoreData)
 01177                {
 01178                    RollBackState(rollBackState);
 01179                    return false;
 1180                }
 01181                if (result == ConsumeNumberResult.Success)
 01182                {
 01183                    goto Done;
 1184                }
 1185
 01186                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01187                nextByte = data[i];
 01188                if (nextByte is not ((byte)'E' or (byte)'e'))
 01189                {
 01190                    RollBackState(rollBackState, isError: true);
 01191                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedNextDigitEValueNotFound, ne
 1192                }
 01193            }
 1194
 01195            Debug.Assert(nextByte is (byte)'E' or (byte)'e');
 01196            i++;
 01197            _bytePositionInLine++;
 1198
 01199            signResult = ConsumeSignMultiSegment(ref data, ref i, rollBackState);
 01200            if (signResult == ConsumeNumberResult.NeedMoreData)
 01201            {
 01202                RollBackState(rollBackState);
 01203                return false;
 1204            }
 1205
 01206            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1207
 01208            i++;
 01209            _bytePositionInLine++;
 01210            ConsumeNumberResult resultExponent = ConsumeIntegerDigitsMultiSegment(ref data, ref i);
 01211            if (resultExponent == ConsumeNumberResult.NeedMoreData)
 01212            {
 01213                RollBackState(rollBackState);
 01214                return false;
 1215            }
 01216            if (resultExponent == ConsumeNumberResult.Success)
 01217            {
 01218                goto Done;
 1219            }
 1220
 01221            Debug.Assert(resultExponent == ConsumeNumberResult.OperationIncomplete);
 1222
 01223            RollBackState(rollBackState, isError: true);
 01224            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, data[i]);
 1225
 01226        Done:
 01227            if (HasValueSequence)
 01228            {
 01229                SequencePosition start = rollBackState.GetStartPosition();
 01230                SequencePosition end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() 
 01231                ValueSequence = _sequence.Slice(start, end);
 01232                consumed = i;
 01233            }
 1234            else
 01235            {
 01236                ValueSpan = data.Slice(0, i);
 01237                consumed = i;
 01238            }
 01239            return true;
 01240        }
 1241
 1242        private ConsumeNumberResult ConsumeNegativeSignMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scope
 01243        {
 01244            Debug.Assert(i == 0);
 01245            byte nextByte = data[i];
 1246
 01247            if (nextByte == '-')
 01248            {
 01249                i++;
 01250                _bytePositionInLine++;
 01251                if (i >= data.Length)
 01252                {
 01253                    if (IsLastSpan)
 01254                    {
 01255                        RollBackState(rollBackState, isError: true);
 01256                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1257                    }
 01258                    if (!GetNextSpan())
 01259                    {
 01260                        if (IsLastSpan)
 01261                        {
 01262                            RollBackState(rollBackState, isError: true);
 01263                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfD
 1264                        }
 01265                        return ConsumeNumberResult.NeedMoreData;
 1266                    }
 01267                    Debug.Assert(i == 1);
 01268                    _totalConsumed += i;
 01269                    HasValueSequence = true;
 01270                    i = 0;
 01271                    data = _buffer;
 01272                }
 1273
 01274                nextByte = data[i];
 01275                if (!JsonHelpers.IsDigit(nextByte))
 01276                {
 01277                    RollBackState(rollBackState, isError: true);
 01278                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nex
 1279                }
 01280            }
 01281            return ConsumeNumberResult.OperationIncomplete;
 01282        }
 1283
 1284        private ConsumeNumberResult ConsumeZeroMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scoped in Par
 01285        {
 01286            Debug.Assert(data[i] == (byte)'0');
 01287            Debug.Assert(i == 0 || i == 1);
 01288            i++;
 01289            _bytePositionInLine++;
 1290            byte nextByte;
 01291            if (i < data.Length)
 01292            {
 01293                nextByte = data[i];
 01294                if (JsonConstants.Delimiters.Contains(nextByte))
 01295                {
 01296                    return ConsumeNumberResult.Success;
 1297                }
 01298            }
 1299            else
 01300            {
 01301                if (IsLastSpan)
 01302                {
 1303                    // A payload containing a single value: "0" is valid
 1304                    // If we are dealing with multi-value JSON,
 1305                    // ConsumeNumber will validate that we have a delimiter following the "0".
 01306                    return ConsumeNumberResult.Success;
 1307                }
 1308
 01309                if (!GetNextSpan())
 01310                {
 01311                    if (IsLastSpan)
 01312                    {
 01313                        return ConsumeNumberResult.Success;
 1314                    }
 01315                    return ConsumeNumberResult.NeedMoreData;
 1316                }
 1317
 01318                _totalConsumed += i;
 01319                HasValueSequence = true;
 01320                i = 0;
 01321                data = _buffer;
 01322                nextByte = data[i];
 01323                if (JsonConstants.Delimiters.Contains(nextByte))
 01324                {
 01325                    return ConsumeNumberResult.Success;
 1326                }
 01327            }
 01328            nextByte = data[i];
 01329            if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 01330            {
 01331                RollBackState(rollBackState, isError: true);
 01332                ThrowHelper.ThrowJsonReaderException(ref this,
 01333                    JsonHelpers.IsInRangeInclusive(nextByte, '0', '9') ? ExceptionResource.InvalidLeadingZeroInNumber : 
 01334                    nextByte);
 1335            }
 1336
 01337            return ConsumeNumberResult.OperationIncomplete;
 01338        }
 1339
 1340        private ConsumeNumberResult ConsumeIntegerDigitsMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i)
 01341        {
 01342            byte nextByte = default;
 01343            int counter = 0;
 01344            for (; i < data.Length; i++)
 01345            {
 01346                nextByte = data[i];
 01347                if (!JsonHelpers.IsDigit(nextByte))
 01348                {
 01349                    break;
 1350                }
 01351                counter++;
 01352            }
 01353            if (i >= data.Length)
 01354            {
 01355                if (IsLastSpan)
 01356                {
 1357                    // A payload containing a single value of integers (e.g. "12") is valid
 1358                    // If we are dealing with multi-value JSON,
 1359                    // ConsumeNumber will validate that we have a delimiter following the integer.
 01360                    _bytePositionInLine += counter;
 01361                    return ConsumeNumberResult.Success;
 1362                }
 1363
 01364                while (true)
 01365                {
 01366                    if (!GetNextSpan())
 01367                    {
 01368                        if (IsLastSpan)
 01369                        {
 01370                            _bytePositionInLine += counter;
 01371                            return ConsumeNumberResult.Success;
 1372                        }
 01373                        return ConsumeNumberResult.NeedMoreData;
 1374                    }
 1375
 01376                    _totalConsumed += i;
 01377                    _bytePositionInLine += counter;
 01378                    counter = 0;
 01379                    HasValueSequence = true;
 01380                    i = 0;
 01381                    data = _buffer;
 01382                    for (; i < data.Length; i++)
 01383                    {
 01384                        nextByte = data[i];
 01385                        if (!JsonHelpers.IsDigit(nextByte))
 01386                        {
 01387                            break;
 1388                        }
 01389                    }
 01390                    _bytePositionInLine += i;
 01391                    if (i >= data.Length)
 01392                    {
 01393                        if (IsLastSpan)
 01394                        {
 01395                            return ConsumeNumberResult.Success;
 1396                        }
 01397                    }
 1398                    else
 01399                    {
 01400                        break;
 1401                    }
 01402                }
 1403
 01404            }
 1405            else
 01406            {
 01407                _bytePositionInLine += counter;
 01408            }
 1409
 01410            if (JsonConstants.Delimiters.Contains(nextByte))
 01411            {
 01412                return ConsumeNumberResult.Success;
 1413            }
 1414
 01415            return ConsumeNumberResult.OperationIncomplete;
 01416        }
 1417
 1418        private ConsumeNumberResult ConsumeDecimalDigitsMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scop
 01419        {
 01420            if (i >= data.Length)
 01421            {
 01422                if (IsLastSpan)
 01423                {
 01424                    RollBackState(rollBackState, isError: true);
 01425                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1426                }
 01427                if (!GetNextSpan())
 01428                {
 01429                    if (IsLastSpan)
 01430                    {
 01431                        RollBackState(rollBackState, isError: true);
 01432                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1433                    }
 01434                    return ConsumeNumberResult.NeedMoreData;
 1435                }
 01436                _totalConsumed += i;
 01437                HasValueSequence = true;
 01438                i = 0;
 01439                data = _buffer;
 01440            }
 01441            byte nextByte = data[i];
 01442            if (!JsonHelpers.IsDigit(nextByte))
 01443            {
 01444                RollBackState(rollBackState, isError: true);
 01445                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterDecimal, next
 1446            }
 01447            i++;
 01448            _bytePositionInLine++;
 01449            return ConsumeIntegerDigitsMultiSegment(ref data, ref i);
 01450        }
 1451
 1452        private ConsumeNumberResult ConsumeSignMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scoped in Par
 01453        {
 01454            if (i >= data.Length)
 01455            {
 01456                if (IsLastSpan)
 01457                {
 01458                    RollBackState(rollBackState, isError: true);
 01459                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1460                }
 1461
 01462                if (!GetNextSpan())
 01463                {
 01464                    if (IsLastSpan)
 01465                    {
 01466                        RollBackState(rollBackState, isError: true);
 01467                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1468                    }
 01469                    return ConsumeNumberResult.NeedMoreData;
 1470                }
 01471                _totalConsumed += i;
 01472                HasValueSequence = true;
 01473                i = 0;
 01474                data = _buffer;
 01475            }
 1476
 01477            byte nextByte = data[i];
 01478            if (nextByte is (byte)'+' or (byte)'-')
 01479            {
 01480                i++;
 01481                _bytePositionInLine++;
 01482                if (i >= data.Length)
 01483                {
 01484                    if (IsLastSpan)
 01485                    {
 01486                        RollBackState(rollBackState, isError: true);
 01487                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1488                    }
 1489
 01490                    if (!GetNextSpan())
 01491                    {
 01492                        if (IsLastSpan)
 01493                        {
 01494                            RollBackState(rollBackState, isError: true);
 01495                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfD
 1496                        }
 01497                        return ConsumeNumberResult.NeedMoreData;
 1498                    }
 01499                    _totalConsumed += i;
 01500                    HasValueSequence = true;
 01501                    i = 0;
 01502                    data = _buffer;
 01503                }
 01504                nextByte = data[i];
 01505            }
 1506
 01507            if (!JsonHelpers.IsDigit(nextByte))
 01508            {
 01509                RollBackState(rollBackState, isError: true);
 01510                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nextByt
 1511            }
 1512
 01513            return ConsumeNumberResult.OperationIncomplete;
 01514        }
 1515
 1516        private bool ConsumeNextTokenOrRollbackMultiSegment(byte marker)
 01517        {
 01518            long prevTotalConsumed = _totalConsumed;
 01519            int prevConsumed = _consumed;
 01520            long prevPosition = _bytePositionInLine;
 01521            long prevLineNumber = _lineNumber;
 01522            JsonTokenType prevTokenType = _tokenType;
 01523            SequencePosition prevSequencePosition = _currentPosition;
 01524            bool prevTrailingCommaBeforeComment = _trailingCommaBeforeComment;
 01525            ConsumeTokenResult result = ConsumeNextTokenMultiSegment(marker);
 01526            if (result == ConsumeTokenResult.Success)
 01527            {
 01528                return true;
 1529            }
 01530            if (result == ConsumeTokenResult.NotEnoughDataRollBackState)
 01531            {
 01532                _consumed = prevConsumed;
 01533                _tokenType = prevTokenType;
 01534                _bytePositionInLine = prevPosition;
 01535                _lineNumber = prevLineNumber;
 01536                _totalConsumed = prevTotalConsumed;
 01537                _currentPosition = prevSequencePosition;
 01538                _trailingCommaBeforeComment = prevTrailingCommaBeforeComment;
 01539            }
 01540            return false;
 01541        }
 1542
 1543        /// <summary>
 1544        /// This method consumes the next token regardless of whether we are inside an object or an array.
 1545        /// For an object, it reads the next property name token. For an array, it just reads the next value.
 1546        /// </summary>
 1547        private ConsumeTokenResult ConsumeNextTokenMultiSegment(byte marker)
 01548        {
 01549            if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
 01550            {
 01551                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
 01552                {
 01553                    if (marker == JsonConstants.Slash)
 01554                    {
 01555                        return SkipOrConsumeCommentMultiSegmentWithRollback() ? ConsumeTokenResult.Success : ConsumeToke
 1556                    }
 01557                    if (_tokenType == JsonTokenType.Comment)
 01558                    {
 01559                        return ConsumeNextTokenFromLastNonCommentTokenMultiSegment();
 1560                    }
 01561                }
 1562                else
 01563                {
 01564                    Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 01565                    return ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiSegment(marker);
 1566                }
 01567            }
 1568
 01569            if (_bitStack.CurrentDepth == 0)
 01570            {
 01571                if (_readerOptions.AllowMultipleValues)
 01572                {
 01573                    return ReadFirstTokenMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoug
 1574                }
 1575
 01576                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 1577            }
 1578
 01579            if (marker == JsonConstants.ListSeparator)
 01580            {
 01581                _consumed++;
 01582                _bytePositionInLine++;
 1583
 01584                if (_consumed >= (uint)_buffer.Length)
 01585                {
 01586                    if (IsLastSpan)
 01587                    {
 01588                        _consumed--;
 01589                        _bytePositionInLine--;
 01590                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1591                    }
 01592                    if (!GetNextSpan())
 01593                    {
 01594                        if (IsLastSpan)
 01595                        {
 01596                            _consumed--;
 01597                            _bytePositionInLine--;
 01598                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrVa
 1599                        }
 01600                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1601                    }
 01602                }
 01603                byte first = _buffer[_consumed];
 1604
 1605                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01606                if (first <= JsonConstants.Space)
 01607                {
 01608                    SkipWhiteSpaceMultiSegment();
 1609                    // The next character must be a start of a property name or value.
 01610                    if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01611                    {
 01612                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1613                    }
 01614                    first = _buffer[_consumed];
 01615                }
 1616
 01617                TokenStartIndex = BytesConsumed;
 1618
 01619                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && first == JsonConstants.Slash)
 01620                {
 01621                    _trailingCommaBeforeComment = true;
 01622                    return SkipOrConsumeCommentMultiSegmentWithRollback() ? ConsumeTokenResult.Success : ConsumeTokenRes
 1623                }
 1624
 01625                if (_inObject)
 01626                {
 01627                    if (first != JsonConstants.Quote)
 01628                    {
 01629                        if (first == JsonConstants.CloseBrace)
 01630                        {
 01631                            if (_readerOptions.AllowTrailingCommas)
 01632                            {
 01633                                EndObject();
 01634                                return ConsumeTokenResult.Success;
 1635                            }
 01636                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1637                        }
 01638                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1639                    }
 01640                    return ConsumePropertyNameMultiSegment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnough
 1641                }
 1642                else
 01643                {
 01644                    if (first == JsonConstants.CloseBracket)
 01645                    {
 01646                        if (_readerOptions.AllowTrailingCommas)
 01647                        {
 01648                            EndArray();
 01649                            return ConsumeTokenResult.Success;
 1650                        }
 01651                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1652                    }
 01653                    return ConsumeValueMultiSegment(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDa
 1654                }
 1655            }
 01656            else if (marker == JsonConstants.CloseBrace)
 01657            {
 01658                EndObject();
 01659            }
 01660            else if (marker == JsonConstants.CloseBracket)
 01661            {
 01662                EndArray();
 01663            }
 1664            else
 01665            {
 01666                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 1667            }
 01668            return ConsumeTokenResult.Success;
 01669        }
 1670
 1671        private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentTokenMultiSegment()
 01672        {
 01673            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Allow);
 01674            Debug.Assert(_tokenType == JsonTokenType.Comment);
 1675
 01676            if (JsonReaderHelper.IsTokenTypePrimitive(_previousTokenType))
 01677            {
 01678                _tokenType = _inObject ? JsonTokenType.StartObject : JsonTokenType.StartArray;
 01679            }
 1680            else
 01681            {
 01682                _tokenType = _previousTokenType;
 01683            }
 1684
 01685            Debug.Assert(_tokenType != JsonTokenType.Comment);
 1686
 01687            if (!HasMoreDataMultiSegment())
 01688            {
 01689                goto RollBack;
 1690            }
 1691
 01692            byte first = _buffer[_consumed];
 1693
 1694            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01695            if (first <= JsonConstants.Space)
 01696            {
 01697                SkipWhiteSpaceMultiSegment();
 01698                if (!HasMoreDataMultiSegment())
 01699                {
 01700                    goto RollBack;
 1701                }
 01702                first = _buffer[_consumed];
 01703            }
 1704
 01705            if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None)
 01706            {
 01707                if (_readerOptions.AllowMultipleValues)
 01708                {
 01709                    return ReadFirstTokenMultiSegment(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnough
 1710                }
 1711
 01712                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
 1713            }
 1714
 01715            Debug.Assert(first != JsonConstants.Slash);
 1716
 01717            TokenStartIndex = BytesConsumed;
 1718
 01719            if (first == JsonConstants.ListSeparator)
 01720            {
 1721                // A comma without some JSON value preceding it is invalid
 01722                if (_previousTokenType <= JsonTokenType.StartObject || _previousTokenType == JsonTokenType.StartArray ||
 01723                {
 01724                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueAfter
 1725                }
 1726
 01727                _consumed++;
 01728                _bytePositionInLine++;
 1729
 01730                if (_consumed >= (uint)_buffer.Length)
 01731                {
 01732                    if (IsLastSpan)
 01733                    {
 01734                        _consumed--;
 01735                        _bytePositionInLine--;
 01736                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1737                    }
 01738                    if (!GetNextSpan())
 01739                    {
 01740                        if (IsLastSpan)
 01741                        {
 01742                            _consumed--;
 01743                            _bytePositionInLine--;
 01744                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrVa
 1745                        }
 01746                        goto RollBack;
 1747                    }
 01748                }
 01749                first = _buffer[_consumed];
 1750
 1751                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01752                if (first <= JsonConstants.Space)
 01753                {
 01754                    SkipWhiteSpaceMultiSegment();
 1755                    // The next character must be a start of a property name or value.
 01756                    if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01757                    {
 01758                        goto RollBack;
 1759                    }
 01760                    first = _buffer[_consumed];
 01761                }
 1762
 01763                TokenStartIndex = BytesConsumed;
 1764
 01765                if (first == JsonConstants.Slash)
 01766                {
 01767                    _trailingCommaBeforeComment = true;
 01768                    if (SkipOrConsumeCommentMultiSegmentWithRollback())
 01769                    {
 01770                        goto Done;
 1771                    }
 1772                    else
 01773                    {
 01774                        goto RollBack;
 1775                    }
 1776                }
 1777
 01778                if (_inObject)
 01779                {
 01780                    if (first != JsonConstants.Quote)
 01781                    {
 01782                        if (first == JsonConstants.CloseBrace)
 01783                        {
 01784                            if (_readerOptions.AllowTrailingCommas)
 01785                            {
 01786                                EndObject();
 01787                                goto Done;
 1788                            }
 01789                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1790                        }
 1791
 01792                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1793                    }
 01794                    if (ConsumePropertyNameMultiSegment())
 01795                    {
 01796                        goto Done;
 1797                    }
 1798                    else
 01799                    {
 01800                        goto RollBack;
 1801                    }
 1802                }
 1803                else
 01804                {
 01805                    if (first == JsonConstants.CloseBracket)
 01806                    {
 01807                        if (_readerOptions.AllowTrailingCommas)
 01808                        {
 01809                            EndArray();
 01810                            goto Done;
 1811                        }
 01812                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1813                    }
 1814
 01815                    if (ConsumeValueMultiSegment(first))
 01816                    {
 01817                        goto Done;
 1818                    }
 1819                    else
 01820                    {
 01821                        goto RollBack;
 1822                    }
 1823                }
 1824            }
 01825            else if (first == JsonConstants.CloseBrace)
 01826            {
 01827                EndObject();
 01828            }
 01829            else if (first == JsonConstants.CloseBracket)
 01830            {
 01831                EndArray();
 01832            }
 01833            else if (_tokenType == JsonTokenType.None)
 01834            {
 01835                if (ReadFirstTokenMultiSegment(first))
 01836                {
 01837                    goto Done;
 1838                }
 1839                else
 01840                {
 01841                    goto RollBack;
 1842                }
 1843            }
 01844            else if (_tokenType == JsonTokenType.StartObject)
 01845            {
 01846                Debug.Assert(first != JsonConstants.CloseBrace);
 01847                if (first != JsonConstants.Quote)
 01848                {
 01849                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound, fi
 1850                }
 1851
 01852                long prevTotalConsumed = _totalConsumed;
 01853                int prevConsumed = _consumed;
 01854                long prevPosition = _bytePositionInLine;
 01855                long prevLineNumber = _lineNumber;
 01856                if (!ConsumePropertyNameMultiSegment())
 01857                {
 1858                    // roll back potential changes
 01859                    _consumed = prevConsumed;
 01860                    _tokenType = JsonTokenType.StartObject;
 01861                    _bytePositionInLine = prevPosition;
 01862                    _lineNumber = prevLineNumber;
 01863                    _totalConsumed = prevTotalConsumed;
 01864                    goto RollBack;
 1865                }
 01866                goto Done;
 1867            }
 01868            else if (_tokenType == JsonTokenType.StartArray)
 01869            {
 01870                Debug.Assert(first != JsonConstants.CloseBracket);
 01871                if (!ConsumeValueMultiSegment(first))
 01872                {
 01873                    goto RollBack;
 1874                }
 01875                goto Done;
 1876            }
 01877            else if (_tokenType == JsonTokenType.PropertyName)
 01878            {
 01879                if (!ConsumeValueMultiSegment(first))
 01880                {
 01881                    goto RollBack;
 1882                }
 01883                goto Done;
 1884            }
 1885            else
 01886            {
 01887                Debug.Assert(_tokenType == JsonTokenType.EndArray || _tokenType == JsonTokenType.EndObject);
 01888                if (_inObject)
 01889                {
 01890                    Debug.Assert(first != JsonConstants.CloseBrace);
 01891                    if (first != JsonConstants.Quote)
 01892                    {
 01893                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1894                    }
 1895
 01896                    if (ConsumePropertyNameMultiSegment())
 01897                    {
 01898                        goto Done;
 1899                    }
 1900                    else
 01901                    {
 01902                        goto RollBack;
 1903                    }
 1904                }
 1905                else
 01906                {
 01907                    Debug.Assert(first != JsonConstants.CloseBracket);
 1908
 01909                    if (ConsumeValueMultiSegment(first))
 01910                    {
 01911                        goto Done;
 1912                    }
 1913                    else
 01914                    {
 01915                        goto RollBack;
 1916                    }
 1917                }
 1918            }
 1919
 01920        Done:
 01921            return ConsumeTokenResult.Success;
 1922
 01923        RollBack:
 01924            return ConsumeTokenResult.NotEnoughDataRollBackState;
 01925        }
 1926
 1927        private bool SkipAllCommentsMultiSegment(scoped ref byte marker)
 01928        {
 01929            while (marker == JsonConstants.Slash)
 01930            {
 01931                if (SkipOrConsumeCommentMultiSegmentWithRollback())
 01932                {
 01933                    if (!HasMoreDataMultiSegment())
 01934                    {
 01935                        goto IncompleteNoRollback;
 1936                    }
 1937
 01938                    marker = _buffer[_consumed];
 1939
 1940                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01941                    if (marker <= JsonConstants.Space)
 01942                    {
 01943                        SkipWhiteSpaceMultiSegment();
 01944                        if (!HasMoreDataMultiSegment())
 01945                        {
 01946                            goto IncompleteNoRollback;
 1947                        }
 01948                        marker = _buffer[_consumed];
 01949                    }
 01950                }
 1951                else
 01952                {
 01953                    goto IncompleteNoRollback;
 1954                }
 01955            }
 01956            return true;
 1957
 01958        IncompleteNoRollback:
 01959            return false;
 01960        }
 1961
 1962        private bool SkipAllCommentsMultiSegment(scoped ref byte marker, ExceptionResource resource)
 01963        {
 01964            while (marker == JsonConstants.Slash)
 01965            {
 01966                if (SkipOrConsumeCommentMultiSegmentWithRollback())
 01967                {
 1968                    // The next character must be a start of a property name or value.
 01969                    if (!HasMoreDataMultiSegment(resource))
 01970                    {
 01971                        goto IncompleteRollback;
 1972                    }
 1973
 01974                    marker = _buffer[_consumed];
 1975
 1976                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01977                    if (marker <= JsonConstants.Space)
 01978                    {
 01979                        SkipWhiteSpaceMultiSegment();
 1980                        // The next character must be a start of a property name or value.
 01981                        if (!HasMoreDataMultiSegment(resource))
 01982                        {
 01983                            goto IncompleteRollback;
 1984                        }
 01985                        marker = _buffer[_consumed];
 01986                    }
 01987                }
 1988                else
 01989                {
 01990                    goto IncompleteRollback;
 1991                }
 01992            }
 01993            return true;
 1994
 01995        IncompleteRollback:
 01996            return false;
 01997        }
 1998
 1999        private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiSegment(byte marker)
 02000        {
 02001            if (!SkipAllCommentsMultiSegment(ref marker))
 02002            {
 02003                goto IncompleteNoRollback;
 2004            }
 2005
 02006            TokenStartIndex = BytesConsumed;
 2007
 02008            if (_tokenType == JsonTokenType.StartObject)
 02009            {
 02010                if (marker == JsonConstants.CloseBrace)
 02011                {
 02012                    EndObject();
 02013                }
 2014                else
 02015                {
 02016                    if (marker != JsonConstants.Quote)
 02017                    {
 02018                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2019                    }
 2020
 02021                    long prevTotalConsumed = _totalConsumed;
 02022                    int prevConsumed = _consumed;
 02023                    long prevPosition = _bytePositionInLine;
 02024                    long prevLineNumber = _lineNumber;
 02025                    SequencePosition copy = _currentPosition;
 02026                    if (!ConsumePropertyNameMultiSegment())
 02027                    {
 2028                        // roll back potential changes
 02029                        _consumed = prevConsumed;
 02030                        _tokenType = JsonTokenType.StartObject;
 02031                        _bytePositionInLine = prevPosition;
 02032                        _lineNumber = prevLineNumber;
 02033                        _totalConsumed = prevTotalConsumed;
 02034                        _currentPosition = copy;
 02035                        goto IncompleteNoRollback;
 2036                    }
 02037                    goto Done;
 2038                }
 02039            }
 02040            else if (_tokenType == JsonTokenType.StartArray)
 02041            {
 02042                if (marker == JsonConstants.CloseBracket)
 02043                {
 02044                    EndArray();
 02045                }
 2046                else
 02047                {
 02048                    if (!ConsumeValueMultiSegment(marker))
 02049                    {
 02050                        goto IncompleteNoRollback;
 2051                    }
 02052                    goto Done;
 2053                }
 02054            }
 02055            else if (_tokenType == JsonTokenType.PropertyName)
 02056            {
 02057                if (!ConsumeValueMultiSegment(marker))
 02058                {
 02059                    goto IncompleteNoRollback;
 2060                }
 02061                goto Done;
 2062            }
 02063            else if (_bitStack.CurrentDepth == 0)
 02064            {
 02065                if (_readerOptions.AllowMultipleValues)
 02066                {
 02067                    return ReadFirstTokenMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoug
 2068                }
 2069
 02070                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 2071            }
 02072            else if (marker == JsonConstants.ListSeparator)
 02073            {
 02074                _consumed++;
 02075                _bytePositionInLine++;
 2076
 02077                if (_consumed >= (uint)_buffer.Length)
 02078                {
 02079                    if (IsLastSpan)
 02080                    {
 02081                        _consumed--;
 02082                        _bytePositionInLine--;
 02083                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 2084                    }
 02085                    if (!GetNextSpan())
 02086                    {
 02087                        if (IsLastSpan)
 02088                        {
 02089                            _consumed--;
 02090                            _bytePositionInLine--;
 02091                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrVa
 2092                        }
 02093                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2094                    }
 02095                }
 02096                marker = _buffer[_consumed];
 2097
 2098                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02099                if (marker <= JsonConstants.Space)
 02100                {
 02101                    SkipWhiteSpaceMultiSegment();
 2102                    // The next character must be a start of a property name or value.
 02103                    if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02104                    {
 02105                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2106                    }
 02107                    marker = _buffer[_consumed];
 02108                }
 2109
 02110                if (!SkipAllCommentsMultiSegment(ref marker, ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02111                {
 02112                    goto IncompleteRollback;
 2113                }
 2114
 02115                TokenStartIndex = BytesConsumed;
 2116
 02117                if (_inObject)
 02118                {
 02119                    if (marker != JsonConstants.Quote)
 02120                    {
 02121                        if (marker == JsonConstants.CloseBrace)
 02122                        {
 02123                            if (_readerOptions.AllowTrailingCommas)
 02124                            {
 02125                                EndObject();
 02126                                goto Done;
 2127                            }
 02128                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 2129                        }
 2130
 02131                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2132                    }
 02133                    return ConsumePropertyNameMultiSegment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnough
 2134                }
 2135                else
 02136                {
 02137                    if (marker == JsonConstants.CloseBracket)
 02138                    {
 02139                        if (_readerOptions.AllowTrailingCommas)
 02140                        {
 02141                            EndArray();
 02142                            goto Done;
 2143                        }
 02144                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2145                    }
 02146                    return ConsumeValueMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughD
 2147                }
 2148            }
 02149            else if (marker == JsonConstants.CloseBrace)
 02150            {
 02151                EndObject();
 02152            }
 02153            else if (marker == JsonConstants.CloseBracket)
 02154            {
 02155                EndArray();
 02156            }
 2157            else
 02158            {
 02159                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 2160            }
 2161
 02162        Done:
 02163            return ConsumeTokenResult.Success;
 02164        IncompleteNoRollback:
 02165            return ConsumeTokenResult.IncompleteNoRollBackNecessary;
 02166        IncompleteRollback:
 02167            return ConsumeTokenResult.NotEnoughDataRollBackState;
 02168        }
 2169
 2170        private bool SkipOrConsumeCommentMultiSegmentWithRollback()
 02171        {
 02172            long prevTotalConsumed = BytesConsumed;
 02173            SequencePosition start = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + 
 02174            bool skipSucceeded = SkipCommentMultiSegment(out int tailBytesToIgnore);
 2175
 02176            if (skipSucceeded)
 02177            {
 02178                Debug.Assert(
 02179                    _readerOptions.CommentHandling == JsonCommentHandling.Allow ||
 02180                    _readerOptions.CommentHandling == JsonCommentHandling.Skip);
 2181
 02182                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
 02183                {
 02184                    SequencePosition end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetIntege
 2185
 02186                    ReadOnlySequence<byte> commentSequence = _sequence.Slice(start, end);
 02187                    commentSequence = commentSequence.Slice(2, commentSequence.Length - 2 - tailBytesToIgnore);
 02188                    HasValueSequence = !commentSequence.IsSingleSegment;
 2189
 02190                    if (HasValueSequence)
 02191                    {
 02192                        ValueSequence = commentSequence;
 02193                    }
 2194                    else
 02195                    {
 02196                        ValueSpan = commentSequence.FirstSpan;
 02197                    }
 2198
 02199                    if (_tokenType != JsonTokenType.Comment)
 02200                    {
 02201                        _previousTokenType = _tokenType;
 02202                    }
 2203
 02204                    _tokenType = JsonTokenType.Comment;
 02205                }
 02206            }
 2207            else
 02208            {
 02209                _totalConsumed = prevTotalConsumed;
 2210                // Note: BytesConsumed = _totalConsumed + _consumed
 2211                // Changing _consumed and _totalConsumed to original values might not work correctly
 2212                // since _consumed is tracking position in the current sequence
 2213                // and current sequence might not be the same as we could've called GetNextSpan.
 2214                // Since we return false we do not expect these APIs to be called again
 2215                // so the values are ok to be slightly incorrect as long as the sum remains the same
 2216                // if we don't reset this value to zero the BytesConsumed might be reported incorrectly.
 02217                _consumed = 0;
 02218            }
 2219
 02220            return skipSucceeded;
 02221        }
 2222
 2223        private bool SkipCommentMultiSegment(out int tailBytesToIgnore)
 02224        {
 02225            _consumed++;
 02226            _bytePositionInLine++;
 2227            // Create local copy to avoid bounds checks.
 02228            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed);
 2229
 02230            if (localBuffer.Length == 0)
 02231            {
 02232                if (IsLastSpan)
 02233                {
 02234                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReadingComm
 2235                }
 2236
 02237                if (!GetNextSpan())
 02238                {
 02239                    if (IsLastSpan)
 02240                    {
 02241                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReading
 2242                    }
 2243
 02244                    tailBytesToIgnore = 0;
 02245                    return false;
 2246                }
 2247
 02248                localBuffer = _buffer;
 02249            }
 2250
 02251            byte marker = localBuffer[0];
 02252            if (marker is not (JsonConstants.Slash or JsonConstants.Asterisk))
 02253            {
 02254                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAtStartOfComment, marke
 2255            }
 2256
 02257            bool multiLine = marker == JsonConstants.Asterisk;
 2258
 02259            _consumed++;
 02260            _bytePositionInLine++;
 02261            localBuffer = localBuffer.Slice(1);
 2262
 02263            if (localBuffer.Length == 0)
 02264            {
 02265                if (IsLastSpan)
 02266                {
 02267                    tailBytesToIgnore = 0;
 2268
 02269                    if (multiLine)
 02270                    {
 02271                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReading
 2272                    }
 2273
 02274                    return true;
 2275                }
 2276
 02277                if (!GetNextSpan())
 02278                {
 02279                    tailBytesToIgnore = 0;
 2280
 02281                    if (IsLastSpan)
 02282                    {
 02283                        if (multiLine)
 02284                        {
 02285                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileRea
 2286                        }
 2287
 02288                        return true;
 2289                    }
 2290
 02291                    return false;
 2292                }
 2293
 02294                localBuffer = _buffer;
 02295            }
 2296
 02297            if (multiLine)
 02298            {
 02299                tailBytesToIgnore = 2;
 02300                return SkipMultiLineCommentMultiSegment(localBuffer);
 2301            }
 2302            else
 02303            {
 02304                return SkipSingleLineCommentMultiSegment(localBuffer, out tailBytesToIgnore);
 2305            }
 02306        }
 2307
 2308        private bool SkipSingleLineCommentMultiSegment(ReadOnlySpan<byte> localBuffer, out int tailBytesToSkip)
 02309        {
 02310            bool expectLF = false;
 02311            int dangerousLineSeparatorBytesConsumed = 0;
 02312            tailBytesToSkip = 0;
 2313
 02314            while (true)
 02315            {
 02316                if (expectLF)
 02317                {
 02318                    if (localBuffer[0] == JsonConstants.LineFeed)
 02319                    {
 02320                        tailBytesToSkip++;
 02321                        _consumed++;
 02322                    }
 2323
 02324                    break;
 2325                }
 2326
 02327                int idx = FindLineSeparatorMultiSegment(localBuffer, ref dangerousLineSeparatorBytesConsumed);
 02328                Debug.Assert(dangerousLineSeparatorBytesConsumed is >= 0 and <= 2);
 2329
 02330                if (idx != -1)
 02331                {
 02332                    tailBytesToSkip++;
 02333                    _consumed += idx + 1;
 02334                    _bytePositionInLine += idx + 1;
 2335
 02336                    if (localBuffer[idx] == JsonConstants.LineFeed)
 02337                    {
 02338                        break;
 2339                    }
 2340
 2341                    // If we are here, we have definintely found a \r. So now to check if \n follows.
 02342                    Debug.Assert(localBuffer[idx] == JsonConstants.CarriageReturn);
 2343
 02344                    if (idx < localBuffer.Length - 1)
 02345                    {
 02346                        if (localBuffer[idx + 1] == JsonConstants.LineFeed)
 02347                        {
 02348                            tailBytesToSkip++;
 02349                            _consumed++;
 02350                            _bytePositionInLine++;
 02351                        }
 2352
 02353                        break;
 2354                    }
 2355
 02356                    expectLF = true;
 02357                }
 2358                else
 02359                {
 02360                    _consumed += localBuffer.Length;
 02361                    _bytePositionInLine += localBuffer.Length;
 02362                }
 2363
 02364                if (IsLastSpan)
 02365                {
 02366                    if (expectLF)
 02367                    {
 02368                        break;
 2369                    }
 2370
 02371                    return true;
 2372                }
 2373
 02374                if (!GetNextSpan())
 02375                {
 02376                    if (IsLastSpan)
 02377                    {
 02378                        if (expectLF)
 02379                        {
 02380                            break;
 2381                        }
 2382
 02383                        return true;
 2384                    }
 2385                    else
 02386                    {
 02387                        return false;
 2388                    }
 2389                }
 2390
 02391                localBuffer = _buffer;
 02392            }
 2393
 02394            _bytePositionInLine = 0;
 02395            _lineNumber++;
 2396
 02397            return true;
 02398        }
 2399
 2400        private int FindLineSeparatorMultiSegment(ReadOnlySpan<byte> localBuffer, scoped ref int dangerousLineSeparatorB
 02401        {
 02402            Debug.Assert(dangerousLineSeparatorBytesConsumed >= 0 && dangerousLineSeparatorBytesConsumed <= 2);
 2403
 02404            if (dangerousLineSeparatorBytesConsumed != 0)
 02405            {
 02406                ThrowOnDangerousLineSeparatorMultiSegment(localBuffer, ref dangerousLineSeparatorBytesConsumed);
 2407
 02408                if (dangerousLineSeparatorBytesConsumed != 0)
 02409                {
 2410                    // this can only happen if localBuffer size is 1 and we have previously only consumed 1 byte
 2411                    // or localBuffer is 0
 02412                    Debug.Assert(dangerousLineSeparatorBytesConsumed >= 1 && dangerousLineSeparatorBytesConsumed <= 2 &&
 02413                    return -1;
 2414                }
 02415            }
 2416
 02417            int totalIdx = 0;
 02418            while (true)
 02419            {
 02420                int idx = localBuffer.IndexOfAny(JsonConstants.LineFeed, JsonConstants.CarriageReturn, JsonConstants.Sta
 02421                dangerousLineSeparatorBytesConsumed = 0;
 2422
 02423                if (idx == -1)
 02424                {
 02425                    return -1;
 2426                }
 2427
 02428                if (localBuffer[idx] != JsonConstants.StartingByteOfNonStandardSeparator)
 02429                {
 02430                    return totalIdx + idx;
 2431                }
 2432
 02433                int p = idx + 1;
 02434                localBuffer = localBuffer.Slice(p);
 02435                totalIdx += p;
 2436
 02437                dangerousLineSeparatorBytesConsumed++;
 02438                ThrowOnDangerousLineSeparatorMultiSegment(localBuffer, ref dangerousLineSeparatorBytesConsumed);
 2439
 02440                if (dangerousLineSeparatorBytesConsumed != 0)
 02441                {
 2442                    // this can only happen in the end of the local buffer
 02443                    Debug.Assert(localBuffer.Length < 2);
 02444                    return -1;
 2445                }
 02446            }
 02447        }
 2448
 2449        // assumes first byte (JsonConstants.UnexpectedEndOfLineSeparator) is already read
 2450        private void ThrowOnDangerousLineSeparatorMultiSegment(ReadOnlySpan<byte> localBuffer, scoped ref int dangerousL
 02451        {
 02452            Debug.Assert(dangerousLineSeparatorBytesConsumed == 1 || dangerousLineSeparatorBytesConsumed == 2);
 2453
 2454            // \u2028 and \u2029 are considered respectively line and paragraph separators
 2455            // UTF-8 representation for them is E2, 80, A8/A9
 2456            // we have already read E2 and maybe 80 we need to check for remaining 1 or 2 bytes
 2457
 02458            if (localBuffer.IsEmpty)
 02459            {
 02460                return;
 2461            }
 2462
 02463            if (dangerousLineSeparatorBytesConsumed == 1)
 02464            {
 02465                if (localBuffer[0] == 0x80)
 02466                {
 02467                    localBuffer = localBuffer.Slice(1);
 02468                    dangerousLineSeparatorBytesConsumed++;
 2469
 02470                    if (localBuffer.IsEmpty)
 02471                    {
 02472                        return;
 2473                    }
 02474                }
 2475                else
 02476                {
 2477                    // no match
 02478                    dangerousLineSeparatorBytesConsumed = 0;
 02479                    return;
 2480                }
 02481            }
 2482
 02483            if (dangerousLineSeparatorBytesConsumed == 2)
 02484            {
 02485                byte lastByte = localBuffer[0];
 02486                if (lastByte is 0xA8 or 0xA9)
 02487                {
 02488                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfLineSeparator);
 2489                }
 2490                else
 02491                {
 2492                    // no match
 02493                    dangerousLineSeparatorBytesConsumed = 0;
 02494                    return;
 2495                }
 2496            }
 02497        }
 2498
 2499        private bool SkipMultiLineCommentMultiSegment(ReadOnlySpan<byte> localBuffer)
 02500        {
 02501            bool expectSlash = false;
 02502            bool ignoreNextLfForLineTracking = false;
 2503
 02504            while (true)
 02505            {
 02506                Debug.Assert(localBuffer.Length > 0);
 2507
 02508                if (expectSlash)
 02509                {
 02510                    if (localBuffer[0] == JsonConstants.Slash)
 02511                    {
 02512                        _consumed++;
 02513                        _bytePositionInLine++;
 02514                        return true;
 2515                    }
 2516
 02517                    expectSlash = false;
 02518                }
 2519
 02520                if (ignoreNextLfForLineTracking)
 02521                {
 02522                    if (localBuffer[0] == JsonConstants.LineFeed)
 02523                    {
 02524                        _consumed++;
 02525                        localBuffer = localBuffer.Slice(1);
 02526                    }
 2527
 02528                    ignoreNextLfForLineTracking = false;
 02529                }
 2530
 02531                int idx = localBuffer.IndexOfAny(JsonConstants.Asterisk, JsonConstants.LineFeed, JsonConstants.CarriageR
 2532
 02533                if (idx != -1)
 02534                {
 02535                    int nextIdx = idx + 1;
 02536                    byte marker = localBuffer[idx];
 02537                    localBuffer = localBuffer.Slice(nextIdx);
 2538
 02539                    _consumed += nextIdx;
 2540
 02541                    switch (marker)
 2542                    {
 2543                        case JsonConstants.Asterisk:
 02544                            expectSlash = true;
 02545                            _bytePositionInLine += nextIdx;
 02546                            break;
 2547                        case JsonConstants.LineFeed:
 02548                            _bytePositionInLine = 0;
 02549                            _lineNumber++;
 02550                            break;
 2551                        default:
 02552                            Debug.Assert(marker == JsonConstants.CarriageReturn);
 02553                            _bytePositionInLine = 0;
 02554                            _lineNumber++;
 02555                            ignoreNextLfForLineTracking = true;
 02556                            break;
 2557                    }
 02558                }
 2559                else
 02560                {
 02561                    _consumed += localBuffer.Length;
 02562                    _bytePositionInLine += localBuffer.Length;
 02563                    localBuffer = ReadOnlySpan<byte>.Empty;
 02564                }
 2565
 02566                if (localBuffer.IsEmpty)
 02567                {
 02568                    if (IsLastSpan)
 02569                    {
 02570                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReading
 2571                    }
 2572
 02573                    if (!GetNextSpan())
 02574                    {
 02575                        if (IsLastSpan)
 02576                        {
 02577                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileRea
 2578                        }
 2579                        else
 02580                        {
 02581                            return false;
 2582                        }
 2583                    }
 2584
 02585                    localBuffer = _buffer;
 02586                    Debug.Assert(!localBuffer.IsEmpty);
 02587                }
 02588            }
 02589        }
 2590
 2591        private PartialStateForRollback CaptureState() =>
 02592            new PartialStateForRollback(_totalConsumed, _bytePositionInLine, _consumed, _currentPosition);
 2593
 2594        private readonly struct PartialStateForRollback
 2595        {
 2596            public readonly long _prevTotalConsumed;
 2597            public readonly long _prevBytePositionInLine;
 2598            public readonly int _prevConsumed;
 2599            public readonly SequencePosition _prevCurrentPosition;
 2600
 2601            public PartialStateForRollback(long totalConsumed, long bytePositionInLine, int consumed, SequencePosition c
 02602            {
 02603                _prevTotalConsumed = totalConsumed;
 02604                _prevBytePositionInLine = bytePositionInLine;
 02605                _prevConsumed = consumed;
 02606                _prevCurrentPosition = currentPosition;
 02607            }
 2608
 2609            public SequencePosition GetStartPosition(int offset = 0)
 02610            {
 02611                return new SequencePosition(_prevCurrentPosition.GetObject(), _prevCurrentPosition.GetInteger() + _prevC
 02612            }
 2613        }
 2614    }
 2615}

D:\runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Reader\Utf8JsonReader.TryGet.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.Runtime.CompilerServices;
 9
 10namespace System.Text.Json
 11{
 12    public ref partial struct Utf8JsonReader
 13    {
 14        /// <summary>
 15        /// Parses the current JSON token value from the source, unescaped, and transcoded as a <see cref="string"/>.
 16        /// </summary>
 17        /// <remarks>
 18        /// Returns <see langword="null" /> when <see cref="TokenType"/> is <see cref="JsonTokenType.Null"/>.
 19        /// </remarks>
 20        /// <exception cref="InvalidOperationException">
 21        /// Thrown if trying to get the value of the JSON token that is not a string
 22        /// (i.e. other than <see cref="JsonTokenType.String"/>, <see cref="JsonTokenType.PropertyName"/> or
 23        /// <see cref="JsonTokenType.Null"/>).
 24        /// <seealso cref="TokenType" />
 25        /// It will also throw when the JSON string contains invalid UTF-8 bytes, or invalid UTF-16 surrogates.
 26        /// </exception>
 27        public string? GetString()
 028        {
 029            if (TokenType == JsonTokenType.Null)
 030            {
 031                return null;
 32            }
 33
 034            if (TokenType != JsonTokenType.String && TokenType != JsonTokenType.PropertyName)
 035            {
 036                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
 37            }
 38
 039            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 40
 041            if (ValueIsEscaped)
 042            {
 043                return JsonReaderHelper.GetUnescapedString(span);
 44            }
 45
 046            Debug.Assert(!span.Contains(JsonConstants.BackSlash));
 047            return JsonReaderHelper.TranscodeHelper(span);
 048        }
 49
 50        /// <summary>
 51        /// Copies the current JSON token value from the source, unescaped as a UTF-8 string to the destination buffer.
 52        /// </summary>
 53        /// <param name="utf8Destination">A buffer to write the unescaped UTF-8 bytes into.</param>
 54        /// <returns>The number of bytes written to <paramref name="utf8Destination"/>.</returns>
 55        /// <remarks>
 56        /// Unlike <see cref="GetString"/>, this method does not support <see cref="JsonTokenType.Null"/>.
 57        ///
 58        /// This method will throw <see cref="ArgumentException"/> if the destination buffer is too small to hold the un
 59        /// An appropriately sized buffer can be determined by consulting the length of either <see cref="ValueSpan"/> o
 60        /// since the unescaped result is always less than or equal to the length of the encoded strings.
 61        /// </remarks>
 62        /// <exception cref="InvalidOperationException">
 63        /// Thrown if trying to get the value of the JSON token that is not a string
 64        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>.
 65        /// <seealso cref="TokenType" />
 66        /// It will also throw when the JSON string contains invalid UTF-8 bytes, or invalid UTF-16 surrogates.
 67        /// </exception>
 68        /// <exception cref="ArgumentException">The destination buffer is too small to hold the unescaped value.</except
 69        public readonly int CopyString(Span<byte> utf8Destination)
 070        {
 071            if (_tokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
 072            {
 073                ThrowHelper.ThrowInvalidOperationException_ExpectedString(_tokenType);
 74            }
 75
 076            return CopyValue(utf8Destination);
 077        }
 78
 79        internal readonly int CopyValue(Span<byte> utf8Destination)
 080        {
 081            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName or JsonTokenType.Number);
 082            Debug.Assert(_tokenType != JsonTokenType.Number || !ValueIsEscaped, "Numbers can't contain escape characters
 83
 84            int bytesWritten;
 85
 086            if (ValueIsEscaped)
 087            {
 088                if (!TryCopyEscapedString(utf8Destination, out bytesWritten))
 089                {
 090                    utf8Destination.Slice(0, bytesWritten).Clear();
 091                    ThrowHelper.ThrowArgumentException_DestinationTooShort();
 92                }
 093            }
 94            else
 095            {
 096                if (HasValueSequence)
 097                {
 098                    ReadOnlySequence<byte> valueSequence = ValueSequence;
 099                    valueSequence.CopyTo(utf8Destination);
 0100                    bytesWritten = (int)valueSequence.Length;
 0101                }
 102                else
 0103                {
 0104                    ReadOnlySpan<byte> valueSpan = ValueSpan;
 0105                    valueSpan.CopyTo(utf8Destination);
 0106                    bytesWritten = valueSpan.Length;
 0107                }
 0108            }
 109
 0110            JsonReaderHelper.ValidateUtf8(utf8Destination.Slice(0, bytesWritten));
 0111            return bytesWritten;
 0112        }
 113
 114        /// <summary>
 115        /// Copies the current JSON token value from the source, unescaped, and transcoded as a UTF-16 char buffer.
 116        /// </summary>
 117        /// <param name="destination">A buffer to write the transcoded UTF-16 characters into.</param>
 118        /// <returns>The number of characters written to <paramref name="destination"/>.</returns>
 119        /// <remarks>
 120        /// Unlike <see cref="GetString"/>, this method does not support <see cref="JsonTokenType.Null"/>.
 121        ///
 122        /// This method will throw <see cref="ArgumentException"/> if the destination buffer is too small to hold the un
 123        /// An appropriately sized buffer can be determined by consulting the length of either <see cref="ValueSpan"/> o
 124        /// since the unescaped result is always less than or equal to the length of the encoded strings.
 125        /// </remarks>
 126        /// <exception cref="InvalidOperationException">
 127        /// Thrown if trying to get the value of the JSON token that is not a string
 128        /// (i.e. other than <see cref="JsonTokenType.String"/> or <see cref="JsonTokenType.PropertyName"/>.
 129        /// <seealso cref="TokenType" />
 130        /// It will also throw when the JSON string contains invalid UTF-8 bytes, or invalid UTF-16 surrogates.
 131        /// </exception>
 132        /// <exception cref="ArgumentException">The destination buffer is too small to hold the unescaped value.</except
 133        public readonly int CopyString(Span<char> destination)
 0134        {
 0135            if (_tokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
 0136            {
 0137                ThrowHelper.ThrowInvalidOperationException_ExpectedString(_tokenType);
 138            }
 139
 0140            return CopyValue(destination);
 0141        }
 142
 143        internal readonly unsafe int CopyValue(Span<char> destination)
 0144        {
 0145            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName or JsonTokenType.Number);
 0146            Debug.Assert(_tokenType != JsonTokenType.Number || !ValueIsEscaped, "Numbers can't contain escape characters
 147
 148            scoped ReadOnlySpan<byte> unescapedSource;
 0149            byte[]? rentedBuffer = null;
 150            int valueLength;
 151
 0152            if (ValueIsEscaped)
 0153            {
 0154                valueLength = ValueLength;
 155
 0156                Span<byte> unescapedBuffer = valueLength <= JsonConstants.StackallocByteThreshold ?
 0157                    stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0158                    (rentedBuffer = ArrayPool<byte>.Shared.Rent(valueLength));
 159
 0160                bool success = TryCopyEscapedString(unescapedBuffer, out int bytesWritten);
 0161                Debug.Assert(success);
 0162                unescapedSource = unescapedBuffer.Slice(0, bytesWritten);
 0163            }
 164            else
 0165            {
 0166                if (HasValueSequence)
 0167                {
 0168                    ReadOnlySequence<byte> valueSequence = ValueSequence;
 0169                    valueLength = checked((int)valueSequence.Length);
 170
 0171                    Span<byte> intermediate = valueLength <= JsonConstants.StackallocByteThreshold ?
 0172                        stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0173                        (rentedBuffer = ArrayPool<byte>.Shared.Rent(valueLength));
 174
 0175                    valueSequence.CopyTo(intermediate);
 0176                    unescapedSource = intermediate.Slice(0, valueLength);
 0177                }
 178                else
 0179                {
 0180                    unescapedSource = ValueSpan;
 0181                }
 0182            }
 183
 0184            int charsWritten = JsonReaderHelper.TranscodeHelper(unescapedSource, destination);
 185
 0186            if (rentedBuffer is not null)
 0187            {
 0188                new Span<byte>(rentedBuffer, 0, unescapedSource.Length).Clear();
 0189                ArrayPool<byte>.Shared.Return(rentedBuffer);
 0190            }
 191
 0192            return charsWritten;
 0193        }
 194
 195        private readonly unsafe bool TryCopyEscapedString(Span<byte> destination, out int bytesWritten)
 0196        {
 0197            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName);
 0198            Debug.Assert(ValueIsEscaped);
 199
 0200            byte[]? rentedBuffer = null;
 201            scoped ReadOnlySpan<byte> source;
 202
 0203            if (HasValueSequence)
 0204            {
 0205                ReadOnlySequence<byte> valueSequence = ValueSequence;
 0206                int sequenceLength = checked((int)valueSequence.Length);
 207
 0208                Span<byte> intermediate = sequenceLength <= JsonConstants.StackallocByteThreshold ?
 0209                    stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0210                    (rentedBuffer = ArrayPool<byte>.Shared.Rent(sequenceLength));
 211
 0212                valueSequence.CopyTo(intermediate);
 0213                source = intermediate.Slice(0, sequenceLength);
 0214            }
 215            else
 0216            {
 0217                source = ValueSpan;
 0218            }
 219
 0220            bool success = JsonReaderHelper.TryUnescape(source, destination, out bytesWritten);
 221
 0222            if (rentedBuffer is not null)
 0223            {
 0224                new Span<byte>(rentedBuffer, 0, source.Length).Clear();
 0225                ArrayPool<byte>.Shared.Return(rentedBuffer);
 0226            }
 227
 0228            Debug.Assert(bytesWritten < source.Length, "source buffer must contain at least one escape sequence");
 0229            return success;
 0230        }
 231
 232        /// <summary>
 233        /// Parses the current JSON token value from the source as a comment, transcoded as a <see cref="string"/>.
 234        /// </summary>
 235        /// <exception cref="InvalidOperationException">
 236        /// Thrown if trying to get the value of the JSON token that is not a comment.
 237        /// <seealso cref="TokenType" />
 238        /// </exception>
 239        public string GetComment()
 0240        {
 0241            if (TokenType != JsonTokenType.Comment)
 0242            {
 0243                ThrowHelper.ThrowInvalidOperationException_ExpectedComment(TokenType);
 244            }
 0245            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 0246            return JsonReaderHelper.TranscodeHelper(span);
 0247        }
 248
 249        /// <summary>
 250        /// Parses the current JSON token value from the source as a <see cref="bool"/>.
 251        /// Returns <see langword="true"/> if the TokenType is JsonTokenType.True and <see langword="false"/> if the Tok
 252        /// </summary>
 253        /// <exception cref="InvalidOperationException">
 254        /// Thrown if trying to get the value of a JSON token that is not a boolean (i.e. <see cref="JsonTokenType.True"
 255        /// <seealso cref="TokenType" />
 256        /// </exception>
 257        public bool GetBoolean()
 0258        {
 0259            JsonTokenType type = TokenType;
 0260            if (type == JsonTokenType.True)
 0261            {
 0262                Debug.Assert((HasValueSequence ? ValueSequence.ToArray() : ValueSpan).Length == 4);
 0263                return true;
 264            }
 0265            else if (type != JsonTokenType.False)
 0266            {
 0267                ThrowHelper.ThrowInvalidOperationException_ExpectedBoolean(TokenType);
 268                Debug.Fail("Throw helper should have thrown an exception.");
 269            }
 270
 0271            Debug.Assert((HasValueSequence ? ValueSequence.ToArray() : ValueSpan).Length == 5);
 0272            return false;
 0273        }
 274
 275        /// <summary>
 276        /// Parses the current JSON token value from the source and decodes the Base64 encoded JSON string as bytes.
 277        /// </summary>
 278        /// <exception cref="InvalidOperationException">
 279        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 280        /// <seealso cref="TokenType" />
 281        /// </exception>
 282        /// <exception cref="FormatException">
 283        /// The JSON string contains data outside of the expected Base64 range, or if it contains invalid/more than two 
 284        /// or is incomplete (i.e. the JSON string length is not a multiple of 4).
 285        /// </exception>
 286        public byte[] GetBytesFromBase64()
 0287        {
 0288            if (!TryGetBytesFromBase64(out byte[]? value))
 0289            {
 0290                ThrowHelper.ThrowFormatException(DataType.Base64String);
 291            }
 0292            return value;
 0293        }
 294
 295        /// <summary>
 296        /// Parses the current JSON token value from the source as a <see cref="byte"/>.
 297        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="byte"/>
 298        /// value.
 299        /// Throws exceptions otherwise.
 300        /// </summary>
 301        /// <exception cref="InvalidOperationException">
 302        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 303        /// <seealso cref="TokenType" />
 304        /// </exception>
 305        /// <exception cref="FormatException">
 306        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 307        /// is written in scientific notation) or, it represents a number less than <see cref="byte.MinValue"/> or great
 308        /// than <see cref="byte.MaxValue"/>.
 309        /// </exception>
 310        public byte GetByte()
 0311        {
 0312            if (!TryGetByte(out byte value))
 0313            {
 0314                ThrowHelper.ThrowFormatException(NumericType.Byte);
 315            }
 0316            return value;
 0317        }
 318
 319        internal byte GetByteWithQuotes()
 0320        {
 0321            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0322            if (!TryGetByteCore(out byte value, span))
 0323            {
 0324                ThrowHelper.ThrowFormatException(NumericType.Byte);
 325            }
 0326            return value;
 0327        }
 328
 329        /// <summary>
 330        /// Parses the current JSON token value from the source as an <see cref="sbyte"/>.
 331        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to an <see cref="sbyte"
 332        /// value.
 333        /// Throws exceptions otherwise.
 334        /// </summary>
 335        /// <exception cref="InvalidOperationException">
 336        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 337        /// <seealso cref="TokenType" />
 338        /// </exception>
 339        /// <exception cref="FormatException">
 340        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 341        /// is written in scientific notation) or, it represents a number less than <see cref="sbyte.MinValue"/> or grea
 342        /// than <see cref="sbyte.MaxValue"/>.
 343        /// </exception>
 344        [System.CLSCompliantAttribute(false)]
 345        public sbyte GetSByte()
 0346        {
 0347            if (!TryGetSByte(out sbyte value))
 0348            {
 0349                ThrowHelper.ThrowFormatException(NumericType.SByte);
 350            }
 0351            return value;
 0352        }
 353
 354        internal sbyte GetSByteWithQuotes()
 0355        {
 0356            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0357            if (!TryGetSByteCore(out sbyte value, span))
 0358            {
 0359                ThrowHelper.ThrowFormatException(NumericType.SByte);
 360            }
 0361            return value;
 0362        }
 363
 364        /// <summary>
 365        /// Parses the current JSON token value from the source as a <see cref="short"/>.
 366        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="short"/
 367        /// value.
 368        /// Throws exceptions otherwise.
 369        /// </summary>
 370        /// <exception cref="InvalidOperationException">
 371        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 372        /// <seealso cref="TokenType" />
 373        /// </exception>
 374        /// <exception cref="FormatException">
 375        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 376        /// is written in scientific notation) or, it represents a number less than <see cref="short.MinValue"/> or grea
 377        /// than <see cref="short.MaxValue"/>.
 378        /// </exception>
 379        public short GetInt16()
 0380        {
 0381            if (!TryGetInt16(out short value))
 0382            {
 0383                ThrowHelper.ThrowFormatException(NumericType.Int16);
 384            }
 0385            return value;
 0386        }
 387
 388        internal short GetInt16WithQuotes()
 0389        {
 0390            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0391            if (!TryGetInt16Core(out short value, span))
 0392            {
 0393                ThrowHelper.ThrowFormatException(NumericType.Int16);
 394            }
 0395            return value;
 0396        }
 397
 398        /// <summary>
 399        /// Parses the current JSON token value from the source as an <see cref="int"/>.
 400        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to an <see cref="int"/>
 401        /// value.
 402        /// Throws exceptions otherwise.
 403        /// </summary>
 404        /// <exception cref="InvalidOperationException">
 405        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 406        /// <seealso cref="TokenType" />
 407        /// </exception>
 408        /// <exception cref="FormatException">
 409        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 410        /// is written in scientific notation) or, it represents a number less than <see cref="int.MinValue"/> or greate
 411        /// than <see cref="int.MaxValue"/>.
 412        /// </exception>
 413        public int GetInt32()
 0414        {
 0415            if (!TryGetInt32(out int value))
 0416            {
 0417                ThrowHelper.ThrowFormatException(NumericType.Int32);
 418            }
 0419            return value;
 0420        }
 421
 422        internal int GetInt32WithQuotes()
 0423        {
 0424            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0425            if (!TryGetInt32Core(out int value, span))
 0426            {
 0427                ThrowHelper.ThrowFormatException(NumericType.Int32);
 428            }
 0429            return value;
 0430        }
 431
 432        /// <summary>
 433        /// Parses the current JSON token value from the source as a <see cref="long"/>.
 434        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="long"/>
 435        /// value.
 436        /// Throws exceptions otherwise.
 437        /// </summary>
 438        /// <exception cref="InvalidOperationException">
 439        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 440        /// <seealso cref="TokenType" />
 441        /// </exception>
 442        /// <exception cref="FormatException">
 443        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 444        /// is written in scientific notation) or, it represents a number less than <see cref="long.MinValue"/> or great
 445        /// than <see cref="long.MaxValue"/>.
 446        /// </exception>
 447        public long GetInt64()
 0448        {
 0449            if (!TryGetInt64(out long value))
 0450            {
 0451                ThrowHelper.ThrowFormatException(NumericType.Int64);
 452            }
 0453            return value;
 0454        }
 455
 456        internal long GetInt64WithQuotes()
 0457        {
 0458            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0459            if (!TryGetInt64Core(out long value, span))
 0460            {
 0461                ThrowHelper.ThrowFormatException(NumericType.Int64);
 462            }
 0463            return value;
 0464        }
 465
 466        /// <summary>
 467        /// Parses the current JSON token value from the source as a <see cref="ushort"/>.
 468        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="ushort"
 469        /// value.
 470        /// Throws exceptions otherwise.
 471        /// </summary>
 472        /// <exception cref="InvalidOperationException">
 473        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 474        /// <seealso cref="TokenType" />
 475        /// </exception>
 476        /// <exception cref="FormatException">
 477        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 478        /// is written in scientific notation) or, it represents a number less than <see cref="ushort.MinValue"/> or gre
 479        /// than <see cref="ushort.MaxValue"/>.
 480        /// </exception>
 481        [System.CLSCompliantAttribute(false)]
 482        public ushort GetUInt16()
 0483        {
 0484            if (!TryGetUInt16(out ushort value))
 0485            {
 0486                ThrowHelper.ThrowFormatException(NumericType.UInt16);
 487            }
 0488            return value;
 0489        }
 490
 491        internal ushort GetUInt16WithQuotes()
 0492        {
 0493            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0494            if (!TryGetUInt16Core(out ushort value, span))
 0495            {
 0496                ThrowHelper.ThrowFormatException(NumericType.UInt16);
 497            }
 0498            return value;
 0499        }
 500
 501        /// <summary>
 502        /// Parses the current JSON token value from the source as a <see cref="uint"/>.
 503        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="uint"/>
 504        /// value.
 505        /// Throws exceptions otherwise.
 506        /// </summary>
 507        /// <exception cref="InvalidOperationException">
 508        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 509        /// <seealso cref="TokenType" />
 510        /// </exception>
 511        /// <exception cref="FormatException">
 512        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 513        /// is written in scientific notation) or, it represents a number less than <see cref="uint.MinValue"/> or great
 514        /// than <see cref="uint.MaxValue"/>.
 515        /// </exception>
 516        [System.CLSCompliantAttribute(false)]
 517        public uint GetUInt32()
 0518        {
 0519            if (!TryGetUInt32(out uint value))
 0520            {
 0521                ThrowHelper.ThrowFormatException(NumericType.UInt32);
 522            }
 0523            return value;
 0524        }
 525
 526        internal uint GetUInt32WithQuotes()
 0527        {
 0528            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0529            if (!TryGetUInt32Core(out uint value, span))
 0530            {
 0531                ThrowHelper.ThrowFormatException(NumericType.UInt32);
 532            }
 0533            return value;
 0534        }
 535
 536        /// <summary>
 537        /// Parses the current JSON token value from the source as a <see cref="ulong"/>.
 538        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="ulong"/
 539        /// value.
 540        /// Throws exceptions otherwise.
 541        /// </summary>
 542        /// <exception cref="InvalidOperationException">
 543        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 544        /// <seealso cref="TokenType" />
 545        /// </exception>
 546        /// <exception cref="FormatException">
 547        /// Thrown if the JSON token value is either of incorrect numeric format (for example if it contains a decimal o
 548        /// is written in scientific notation) or, it represents a number less than <see cref="ulong.MinValue"/> or grea
 549        /// than <see cref="ulong.MaxValue"/>.
 550        /// </exception>
 551        [System.CLSCompliantAttribute(false)]
 552        public ulong GetUInt64()
 0553        {
 0554            if (!TryGetUInt64(out ulong value))
 0555            {
 0556                ThrowHelper.ThrowFormatException(NumericType.UInt64);
 557            }
 0558            return value;
 0559        }
 560
 561        internal ulong GetUInt64WithQuotes()
 0562        {
 0563            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0564            if (!TryGetUInt64Core(out ulong value, span))
 0565            {
 0566                ThrowHelper.ThrowFormatException(NumericType.UInt64);
 567            }
 0568            return value;
 0569        }
 570
 571        /// <summary>
 572        /// Parses the current JSON token value from the source as a <see cref="float"/>.
 573        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="float"/
 574        /// value.
 575        /// Throws exceptions otherwise.
 576        /// </summary>
 577        /// <exception cref="InvalidOperationException">
 578        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 579        /// <seealso cref="TokenType" />
 580        /// </exception>
 581        /// <exception cref="FormatException">
 582        /// On any framework that is not .NET Core 3.0 or higher, thrown if the JSON token value represents a number les
 583        /// than <see cref="float.MaxValue"/>.
 584        /// </exception>
 585        public float GetSingle()
 0586        {
 0587            if (!TryGetSingle(out float value))
 0588            {
 0589                ThrowHelper.ThrowFormatException(NumericType.Single);
 590            }
 0591            return value;
 0592        }
 593
 594        internal float GetSingleWithQuotes()
 0595        {
 0596            ReadOnlySpan<byte> span = GetUnescapedSpan();
 597
 0598            if (JsonReaderHelper.TryGetFloatingPointConstant(span, out float value))
 0599            {
 0600                return value;
 601            }
 602
 603            // NETCOREAPP implementation of the TryParse method above permits case-insensitive variants of the
 604            // float constants "NaN", "Infinity", "-Infinity". This differs from the NETFRAMEWORK implementation.
 605            // The following logic reconciles the two implementations to enforce consistent behavior.
 0606            if (!(Utf8Parser.TryParse(span, out value, out int bytesConsumed)
 0607                  && span.Length == bytesConsumed
 0608                  && float.IsFinite(value)))
 0609            {
 0610                ThrowHelper.ThrowFormatException(NumericType.Single);
 611            }
 612
 0613            return value;
 0614        }
 615
 616        internal float GetSingleFloatingPointConstant()
 0617        {
 0618            ReadOnlySpan<byte> span = GetUnescapedSpan();
 619
 0620            if (!JsonReaderHelper.TryGetFloatingPointConstant(span, out float value))
 0621            {
 0622                ThrowHelper.ThrowFormatException(NumericType.Single);
 623            }
 624
 0625            return value;
 0626        }
 627
 628        /// <summary>
 629        /// Parses the current JSON token value from the source as a <see cref="double"/>.
 630        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="double"
 631        /// value.
 632        /// Throws exceptions otherwise.
 633        /// </summary>
 634        /// <exception cref="InvalidOperationException">
 635        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 636        /// <seealso cref="TokenType" />
 637        /// </exception>
 638        /// <exception cref="FormatException">
 639        /// On any framework that is not .NET Core 3.0 or higher, thrown if the JSON token value represents a number les
 640        /// than <see cref="double.MaxValue"/>.
 641        /// </exception>
 642        public double GetDouble()
 0643        {
 0644            if (!TryGetDouble(out double value))
 0645            {
 0646                ThrowHelper.ThrowFormatException(NumericType.Double);
 647            }
 0648            return value;
 0649        }
 650
 651        internal double GetDoubleWithQuotes()
 0652        {
 0653            ReadOnlySpan<byte> span = GetUnescapedSpan();
 654
 0655            if (JsonReaderHelper.TryGetFloatingPointConstant(span, out double value))
 0656            {
 0657                return value;
 658            }
 659
 660            // NETCOREAPP implementation of the TryParse method above permits case-insensitive variants of the
 661            // float constants "NaN", "Infinity", "-Infinity". This differs from the NETFRAMEWORK implementation.
 662            // The following logic reconciles the two implementations to enforce consistent behavior.
 0663            if (!(Utf8Parser.TryParse(span, out value, out int bytesConsumed)
 0664                  && span.Length == bytesConsumed
 0665                  && double.IsFinite(value)))
 0666            {
 0667                ThrowHelper.ThrowFormatException(NumericType.Double);
 668            }
 669
 0670            return value;
 0671        }
 672
 673        internal double GetDoubleFloatingPointConstant()
 0674        {
 0675            ReadOnlySpan<byte> span = GetUnescapedSpan();
 676
 0677            if (!JsonReaderHelper.TryGetFloatingPointConstant(span, out double value))
 0678            {
 0679                ThrowHelper.ThrowFormatException(NumericType.Double);
 680            }
 681
 0682            return value;
 0683        }
 684
 685        /// <summary>
 686        /// Parses the current JSON token value from the source as a <see cref="decimal"/>.
 687        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="decimal
 688        /// value.
 689        /// Throws exceptions otherwise.
 690        /// </summary>
 691        /// <exception cref="InvalidOperationException">
 692        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 693        /// <seealso cref="TokenType" />
 694        /// </exception>
 695        /// <exception cref="FormatException">
 696        /// Thrown if the JSON token value represents a number less than <see cref="decimal.MinValue"/> or greater
 697        /// than <see cref="decimal.MaxValue"/>.
 698        /// </exception>
 699        public decimal GetDecimal()
 0700        {
 0701            if (!TryGetDecimal(out decimal value))
 0702            {
 0703                ThrowHelper.ThrowFormatException(NumericType.Decimal);
 704            }
 0705            return value;
 0706        }
 707
 708        internal decimal GetDecimalWithQuotes()
 0709        {
 0710            ReadOnlySpan<byte> span = GetUnescapedSpan();
 0711            if (!TryGetDecimalCore(out decimal value, span))
 0712            {
 0713                ThrowHelper.ThrowFormatException(NumericType.Decimal);
 714            }
 0715            return value;
 0716        }
 717
 718        /// <summary>
 719        /// Parses the current JSON token value from the source as a <see cref="DateTime"/>.
 720        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="DateTim
 721        /// value.
 722        /// Throws exceptions otherwise.
 723        /// </summary>
 724        /// <exception cref="InvalidOperationException">
 725        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 726        /// <seealso cref="TokenType" />
 727        /// </exception>
 728        /// <exception cref="FormatException">
 729        /// Thrown if the JSON token value is of an unsupported format. Only a subset of ISO 8601 formats are supported.
 730        /// </exception>
 731        public DateTime GetDateTime()
 0732        {
 0733            if (!TryGetDateTime(out DateTime value))
 0734            {
 0735                ThrowHelper.ThrowFormatException(DataType.DateTime);
 736            }
 737
 0738            return value;
 0739        }
 740
 741        internal DateTime GetDateTimeNoValidation()
 0742        {
 0743            if (!TryGetDateTimeCore(out DateTime value))
 0744            {
 0745                ThrowHelper.ThrowFormatException(DataType.DateTime);
 746            }
 747
 0748            return value;
 0749        }
 750
 751        /// <summary>
 752        /// Parses the current JSON token value from the source as a <see cref="DateTimeOffset"/>.
 753        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="DateTim
 754        /// value.
 755        /// Throws exceptions otherwise.
 756        /// </summary>
 757        /// <exception cref="InvalidOperationException">
 758        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 759        /// <seealso cref="TokenType" />
 760        /// </exception>
 761        /// <exception cref="FormatException">
 762        /// Thrown if the JSON token value is of an unsupported format. Only a subset of ISO 8601 formats are supported.
 763        /// </exception>
 764        public DateTimeOffset GetDateTimeOffset()
 0765        {
 0766            if (!TryGetDateTimeOffset(out DateTimeOffset value))
 0767            {
 0768                ThrowHelper.ThrowFormatException(DataType.DateTimeOffset);
 769            }
 770
 0771            return value;
 0772        }
 773
 774        internal DateTimeOffset GetDateTimeOffsetNoValidation()
 0775        {
 0776            if (!TryGetDateTimeOffsetCore(out DateTimeOffset value))
 0777            {
 0778                ThrowHelper.ThrowFormatException(DataType.DateTimeOffset);
 779            }
 780
 0781            return value;
 0782        }
 783
 784        /// <summary>
 785        /// Parses the current JSON token value from the source as a <see cref="Guid"/>.
 786        /// Returns the value if the entire UTF-8 encoded token value can be successfully parsed to a <see cref="Guid"/>
 787        /// value.
 788        /// Throws exceptions otherwise.
 789        /// </summary>
 790        /// <exception cref="InvalidOperationException">
 791        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 792        /// <seealso cref="TokenType" />
 793        /// </exception>
 794        /// <exception cref="FormatException">
 795        /// Thrown if the JSON token value is of an unsupported format for a Guid.
 796        /// </exception>
 797        public Guid GetGuid()
 0798        {
 0799            if (!TryGetGuid(out Guid value))
 0800            {
 0801                ThrowHelper.ThrowFormatException(DataType.Guid);
 802            }
 803
 0804            return value;
 0805        }
 806
 807        internal Guid GetGuidNoValidation()
 0808        {
 0809            if (!TryGetGuidCore(out Guid value))
 0810            {
 0811                ThrowHelper.ThrowFormatException(DataType.Guid);
 812            }
 813
 0814            return value;
 0815        }
 816
 817        /// <summary>
 818        /// Parses the current JSON token value from the source and decodes the Base64 encoded JSON string as bytes.
 819        /// Returns <see langword="true"/> if the entire token value is encoded as valid Base64 text and can be successf
 820        /// decoded to bytes.
 821        /// Returns <see langword="false"/> otherwise.
 822        /// </summary>
 823        /// <exception cref="InvalidOperationException">
 824        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 825        /// <seealso cref="TokenType" />
 826        /// </exception>
 827        public bool TryGetBytesFromBase64([NotNullWhen(true)] out byte[]? value)
 0828        {
 0829            if (TokenType != JsonTokenType.String)
 0830            {
 0831                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
 832            }
 833
 0834            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 835
 0836            if (ValueIsEscaped)
 0837            {
 0838                return JsonReaderHelper.TryGetUnescapedBase64Bytes(span, out value);
 839            }
 840
 0841            Debug.Assert(!span.Contains(JsonConstants.BackSlash));
 0842            return JsonReaderHelper.TryDecodeBase64(span, out value);
 0843        }
 844
 845        /// <summary>
 846        /// Parses the current JSON token value from the source as a <see cref="byte"/>.
 847        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 848        /// parsed to a <see cref="byte"/> value.
 849        /// Returns <see langword="false"/> otherwise.
 850        /// </summary>
 851        /// <exception cref="InvalidOperationException">
 852        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 853        /// <seealso cref="TokenType" />
 854        /// </exception>
 855        public bool TryGetByte(out byte value)
 0856        {
 0857            if (TokenType != JsonTokenType.Number)
 0858            {
 0859                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 860            }
 861
 0862            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 0863            return TryGetByteCore(out value, span);
 0864        }
 865
 866        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 867        internal static bool TryGetByteCore(out byte value, ReadOnlySpan<byte> span)
 0868        {
 0869            if (Utf8Parser.TryParse(span, out byte tmp, out int bytesConsumed)
 0870                && span.Length == bytesConsumed)
 0871            {
 0872                value = tmp;
 0873                return true;
 874            }
 875
 0876            value = 0;
 0877            return false;
 0878        }
 879
 880        /// <summary>
 881        /// Parses the current JSON token value from the source as an <see cref="sbyte"/>.
 882        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 883        /// parsed to an <see cref="sbyte"/> value.
 884        /// Returns <see langword="false"/> otherwise.
 885        /// </summary>
 886        /// <exception cref="InvalidOperationException">
 887        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 888        /// <seealso cref="TokenType" />
 889        /// </exception>
 890        [System.CLSCompliantAttribute(false)]
 891        public bool TryGetSByte(out sbyte value)
 0892        {
 0893            if (TokenType != JsonTokenType.Number)
 0894            {
 0895                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 896            }
 897
 0898            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 0899            return TryGetSByteCore(out value, span);
 0900        }
 901
 902        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 903        internal static bool TryGetSByteCore(out sbyte value, ReadOnlySpan<byte> span)
 0904        {
 0905            if (Utf8Parser.TryParse(span, out sbyte tmp, out int bytesConsumed)
 0906                && span.Length == bytesConsumed)
 0907            {
 0908                value = tmp;
 0909                return true;
 910            }
 911
 0912            value = 0;
 0913            return false;
 0914        }
 915
 916        /// <summary>
 917        /// Parses the current JSON token value from the source as a <see cref="short"/>.
 918        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 919        /// parsed to a <see cref="short"/> value.
 920        /// Returns <see langword="false"/> otherwise.
 921        /// </summary>
 922        /// <exception cref="InvalidOperationException">
 923        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 924        /// <seealso cref="TokenType" />
 925        /// </exception>
 926        public bool TryGetInt16(out short value)
 0927        {
 0928            if (TokenType != JsonTokenType.Number)
 0929            {
 0930                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 931            }
 932
 0933            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 0934            return TryGetInt16Core(out value, span);
 0935        }
 936
 937        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 938        internal static bool TryGetInt16Core(out short value, ReadOnlySpan<byte> span)
 0939        {
 0940            if (Utf8Parser.TryParse(span, out short tmp, out int bytesConsumed)
 0941                && span.Length == bytesConsumed)
 0942            {
 0943                value = tmp;
 0944                return true;
 945            }
 946
 0947            value = 0;
 0948            return false;
 0949        }
 950
 951        /// <summary>
 952        /// Parses the current JSON token value from the source as an <see cref="int"/>.
 953        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 954        /// parsed to an <see cref="int"/> value.
 955        /// Returns <see langword="false"/> otherwise.
 956        /// </summary>
 957        /// <exception cref="InvalidOperationException">
 958        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 959        /// <seealso cref="TokenType" />
 960        /// </exception>
 961        public bool TryGetInt32(out int value)
 0962        {
 0963            if (TokenType != JsonTokenType.Number)
 0964            {
 0965                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 966            }
 967
 0968            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 0969            return TryGetInt32Core(out value, span);
 0970        }
 971
 972        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 973        internal static bool TryGetInt32Core(out int value, ReadOnlySpan<byte> span)
 0974        {
 0975            if (Utf8Parser.TryParse(span, out int tmp, out int bytesConsumed)
 0976                && span.Length == bytesConsumed)
 0977            {
 0978                value = tmp;
 0979                return true;
 980            }
 981
 0982            value = 0;
 0983            return false;
 0984        }
 985
 986        /// <summary>
 987        /// Parses the current JSON token value from the source as a <see cref="long"/>.
 988        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 989        /// parsed to a <see cref="long"/> value.
 990        /// Returns <see langword="false"/> otherwise.
 991        /// </summary>
 992        /// <exception cref="InvalidOperationException">
 993        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 994        /// <seealso cref="TokenType" />
 995        /// </exception>
 996        public bool TryGetInt64(out long value)
 0997        {
 0998            if (TokenType != JsonTokenType.Number)
 0999            {
 01000                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1001            }
 1002
 01003            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 01004            return TryGetInt64Core(out value, span);
 01005        }
 1006
 1007        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1008        internal static bool TryGetInt64Core(out long value, ReadOnlySpan<byte> span)
 01009        {
 01010            if (Utf8Parser.TryParse(span, out long tmp, out int bytesConsumed)
 01011                && span.Length == bytesConsumed)
 01012            {
 01013                value = tmp;
 01014                return true;
 1015            }
 1016
 01017            value = 0;
 01018            return false;
 01019        }
 1020
 1021        /// <summary>
 1022        /// Parses the current JSON token value from the source as a <see cref="ushort"/>.
 1023        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1024        /// parsed to a <see cref="ushort"/> value.
 1025        /// Returns <see langword="false"/> otherwise.
 1026        /// </summary>
 1027        /// <exception cref="InvalidOperationException">
 1028        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 1029        /// <seealso cref="TokenType" />
 1030        /// </exception>
 1031        [System.CLSCompliantAttribute(false)]
 1032        public bool TryGetUInt16(out ushort value)
 01033        {
 01034            if (TokenType != JsonTokenType.Number)
 01035            {
 01036                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1037            }
 1038
 01039            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 01040            return TryGetUInt16Core(out value, span);
 01041        }
 1042
 1043        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1044        internal static bool TryGetUInt16Core(out ushort value, ReadOnlySpan<byte> span)
 01045        {
 01046            if (Utf8Parser.TryParse(span, out ushort tmp, out int bytesConsumed)
 01047                && span.Length == bytesConsumed)
 01048            {
 01049                value = tmp;
 01050                return true;
 1051            }
 1052
 01053            value = 0;
 01054            return false;
 01055        }
 1056
 1057        /// <summary>
 1058        /// Parses the current JSON token value from the source as a <see cref="uint"/>.
 1059        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1060        /// parsed to a <see cref="uint"/> value.
 1061        /// Returns <see langword="false"/> otherwise.
 1062        /// </summary>
 1063        /// <exception cref="InvalidOperationException">
 1064        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 1065        /// <seealso cref="TokenType" />
 1066        /// </exception>
 1067        [System.CLSCompliantAttribute(false)]
 1068        public bool TryGetUInt32(out uint value)
 01069        {
 01070            if (TokenType != JsonTokenType.Number)
 01071            {
 01072                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1073            }
 1074
 01075            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 01076            return TryGetUInt32Core(out value, span);
 01077        }
 1078
 1079        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1080        internal static bool TryGetUInt32Core(out uint value, ReadOnlySpan<byte> span)
 01081        {
 01082            if (Utf8Parser.TryParse(span, out uint tmp, out int bytesConsumed)
 01083                && span.Length == bytesConsumed)
 01084            {
 01085                value = tmp;
 01086                return true;
 1087            }
 1088
 01089            value = 0;
 01090            return false;
 01091        }
 1092
 1093        /// <summary>
 1094        /// Parses the current JSON token value from the source as a <see cref="ulong"/>.
 1095        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1096        /// parsed to a <see cref="ulong"/> value.
 1097        /// Returns <see langword="false"/> otherwise.
 1098        /// </summary>
 1099        /// <exception cref="InvalidOperationException">
 1100        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 1101        /// <seealso cref="TokenType" />
 1102        /// </exception>
 1103        [System.CLSCompliantAttribute(false)]
 1104        public bool TryGetUInt64(out ulong value)
 01105        {
 01106            if (TokenType != JsonTokenType.Number)
 01107            {
 01108                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1109            }
 1110
 01111            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 01112            return TryGetUInt64Core(out value, span);
 01113        }
 1114
 1115        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1116        internal static bool TryGetUInt64Core(out ulong value, ReadOnlySpan<byte> span)
 01117        {
 01118            if (Utf8Parser.TryParse(span, out ulong tmp, out int bytesConsumed)
 01119                && span.Length == bytesConsumed)
 01120            {
 01121                value = tmp;
 01122                return true;
 1123            }
 1124
 01125            value = 0;
 01126            return false;
 01127        }
 1128
 1129        /// <summary>
 1130        /// Parses the current JSON token value from the source as a <see cref="float"/>.
 1131        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1132        /// parsed to a <see cref="float"/> value.
 1133        /// Returns <see langword="false"/> otherwise.
 1134        /// </summary>
 1135        /// <exception cref="InvalidOperationException">
 1136        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 1137        /// <seealso cref="TokenType" />
 1138        /// </exception>
 1139        public bool TryGetSingle(out float value)
 01140        {
 01141            if (TokenType != JsonTokenType.Number)
 01142            {
 01143                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1144            }
 1145
 01146            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 1147
 01148            if (Utf8Parser.TryParse(span, out float tmp, out int bytesConsumed)
 01149                && span.Length == bytesConsumed)
 01150            {
 01151                value = tmp;
 01152                return true;
 1153            }
 1154
 01155            value = 0;
 01156            return false;
 01157        }
 1158
 1159        /// <summary>
 1160        /// Parses the current JSON token value from the source as a <see cref="double"/>.
 1161        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1162        /// parsed to a <see cref="double"/> value.
 1163        /// Returns <see langword="false"/> otherwise.
 1164        /// </summary>
 1165        /// <exception cref="InvalidOperationException">
 1166        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 1167        /// <seealso cref="TokenType" />
 1168        /// </exception>
 1169        public bool TryGetDouble(out double value)
 01170        {
 01171            if (TokenType != JsonTokenType.Number)
 01172            {
 01173                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1174            }
 1175
 01176            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 1177
 01178            if (Utf8Parser.TryParse(span, out double tmp, out int bytesConsumed)
 01179                && span.Length == bytesConsumed)
 01180            {
 01181                value = tmp;
 01182                return true;
 1183            }
 1184
 01185            value = 0;
 01186            return false;
 01187        }
 1188
 1189        /// <summary>
 1190        /// Parses the current JSON token value from the source as a <see cref="decimal"/>.
 1191        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1192        /// parsed to a <see cref="decimal"/> value.
 1193        /// Returns <see langword="false"/> otherwise.
 1194        /// </summary>
 1195        /// <exception cref="InvalidOperationException">
 1196        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.Number"/>.
 1197        /// <seealso cref="TokenType" />
 1198        /// </exception>
 1199        public bool TryGetDecimal(out decimal value)
 01200        {
 01201            if (TokenType != JsonTokenType.Number)
 01202            {
 01203                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1204            }
 1205
 01206            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 01207            return TryGetDecimalCore(out value, span);
 01208        }
 1209
 1210        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1211        internal static bool TryGetDecimalCore(out decimal value, ReadOnlySpan<byte> span)
 01212        {
 01213            if (Utf8Parser.TryParse(span, out decimal tmp, out int bytesConsumed)
 01214                && span.Length == bytesConsumed)
 01215            {
 01216                value = tmp;
 01217                return true;
 1218            }
 1219
 01220            value = 0;
 01221            return false;
 01222        }
 1223
 1224        /// <summary>
 1225        /// Parses the current JSON token value from the source as a <see cref="DateTime"/>.
 1226        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1227        /// parsed to a <see cref="DateTime"/> value.
 1228        /// Returns <see langword="false"/> otherwise.
 1229        /// </summary>
 1230        /// <exception cref="InvalidOperationException">
 1231        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 1232        /// <seealso cref="TokenType" />
 1233        /// </exception>
 1234        public bool TryGetDateTime(out DateTime value)
 01235        {
 01236            if (TokenType != JsonTokenType.String)
 01237            {
 01238                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
 1239            }
 1240
 01241            return TryGetDateTimeCore(out value);
 01242        }
 1243
 1244        internal unsafe bool TryGetDateTimeCore(out DateTime value)
 01245        {
 1246            scoped ReadOnlySpan<byte> span;
 1247
 01248            if (HasValueSequence)
 01249            {
 01250                long sequenceLength = ValueSequence.Length;
 01251                if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, JsonConsta
 01252                {
 01253                    value = default;
 01254                    return false;
 1255                }
 1256
 01257                Span<byte> stackSpan = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
 01258                ValueSequence.CopyTo(stackSpan);
 01259                span = stackSpan.Slice(0, (int)sequenceLength);
 01260            }
 1261            else
 01262            {
 01263                span = ValueSpan;
 01264            }
 1265
 01266            return JsonReaderHelper.TryGetValue(span, ValueIsEscaped, out value);
 01267        }
 1268
 1269        /// <summary>
 1270        /// Parses the current JSON token value from the source as a <see cref="DateTimeOffset"/>.
 1271        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1272        /// parsed to a <see cref="DateTimeOffset"/> value.
 1273        /// Returns <see langword="false"/> otherwise.
 1274        /// </summary>
 1275        /// <exception cref="InvalidOperationException">
 1276        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 1277        /// <seealso cref="TokenType" />
 1278        /// </exception>
 1279        public bool TryGetDateTimeOffset(out DateTimeOffset value)
 01280        {
 01281            if (TokenType != JsonTokenType.String)
 01282            {
 01283                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
 1284            }
 1285
 01286            return TryGetDateTimeOffsetCore(out value);
 01287        }
 1288
 1289        internal unsafe bool TryGetDateTimeOffsetCore(out DateTimeOffset value)
 01290        {
 1291            scoped ReadOnlySpan<byte> span;
 1292
 01293            if (HasValueSequence)
 01294            {
 01295                long sequenceLength = ValueSequence.Length;
 01296                if (!JsonHelpers.IsInRangeInclusive(sequenceLength, JsonConstants.MinimumDateTimeParseLength, JsonConsta
 01297                {
 01298                    value = default;
 01299                    return false;
 1300                }
 1301
 01302                Span<byte> stackSpan = stackalloc byte[JsonConstants.MaximumEscapedDateTimeOffsetParseLength];
 01303                ValueSequence.CopyTo(stackSpan);
 01304                span = stackSpan.Slice(0, (int)sequenceLength);
 01305            }
 1306            else
 01307            {
 01308                span = ValueSpan;
 01309            }
 1310
 01311            return JsonReaderHelper.TryGetValue(span, ValueIsEscaped, out value);
 01312        }
 1313
 1314        /// <summary>
 1315        /// Parses the current JSON token value from the source as a <see cref="Guid"/>.
 1316        /// Returns <see langword="true"/> if the entire UTF-8 encoded token value can be successfully
 1317        /// parsed to a <see cref="Guid"/> value. Only supports <see cref="Guid"/> values with hyphens
 1318        /// and without any surrounding decorations.
 1319        /// Returns <see langword="false"/> otherwise.
 1320        /// </summary>
 1321        /// <exception cref="InvalidOperationException">
 1322        /// Thrown if trying to get the value of a JSON token that is not a <see cref="JsonTokenType.String"/>.
 1323        /// <seealso cref="TokenType" />
 1324        /// </exception>
 1325        public bool TryGetGuid(out Guid value)
 01326        {
 01327            if (TokenType != JsonTokenType.String)
 01328            {
 01329                ThrowHelper.ThrowInvalidOperationException_ExpectedString(TokenType);
 1330            }
 1331
 01332            return TryGetGuidCore(out value);
 01333        }
 1334
 1335        internal unsafe bool TryGetGuidCore(out Guid value)
 01336        {
 1337            scoped ReadOnlySpan<byte> span;
 1338
 01339            if (HasValueSequence)
 01340            {
 01341                long sequenceLength = ValueSequence.Length;
 01342                if (sequenceLength > JsonConstants.MaximumEscapedGuidLength)
 01343                {
 01344                    value = default;
 01345                    return false;
 1346                }
 1347
 01348                Span<byte> stackSpan = stackalloc byte[JsonConstants.MaximumEscapedGuidLength];
 01349                ValueSequence.CopyTo(stackSpan);
 01350                span = stackSpan.Slice(0, (int)sequenceLength);
 01351            }
 1352            else
 01353            {
 01354                span = ValueSpan;
 01355            }
 1356
 01357            return JsonReaderHelper.TryGetValue(span, ValueIsEscaped, out value);
 01358        }
 1359    }
 1360}

Methods/Properties

IsLastSpan()
OriginalSequence()
OriginalSpan()
ValueLength()
AllowMultipleValues()
ValueSpan()
BytesConsumed()
TokenStartIndex()
CurrentDepth()
IsInArray()
TokenType()
HasValueSequence()
ValueIsEscaped()
IsFinalBlock()
ValueSequence()
Position()
CurrentState()
.ctor(System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.Text.Json.JsonReaderState)
.ctor(System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonReaderOptions)
Read()
Skip()
SkipHelper()
TrySkip()
TrySkipPartial(System.Int32)
ValueTextEquals(System.ReadOnlySpan`1<System.Byte>)
ValueTextEquals(System.String)
TextEqualsHelper(System.ReadOnlySpan`1<System.Byte>)
ValueTextEquals(System.ReadOnlySpan`1<System.Char>)
CompareToSequence(System.ReadOnlySpan`1<System.Byte>)
UnescapeAndCompare(System.ReadOnlySpan`1<System.Byte>)
UnescapeSequenceAndCompare(System.ReadOnlySpan`1<System.Byte>)
IsTokenTypeString(System.Text.Json.JsonTokenType)
MatchNotPossible(System.Int32)
MatchNotPossibleSequence(System.Int32)
StartObject()
EndObject()
StartArray()
EndArray()
UpdateBitStackOnEndToken()
ReadSingleSegment()
HasMoreData()
HasMoreData(System.Text.Json.ExceptionResource)
ReadFirstToken(System.Byte)
SkipWhiteSpace()
ConsumeValue(System.Byte)
ConsumeLiteral(System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonTokenType)
CheckLiteral(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>)
ThrowInvalidLiteral(System.ReadOnlySpan`1<System.Byte>)
ConsumeNumber()
ConsumePropertyName()
ConsumeString()
ConsumeStringAndValidate(System.ReadOnlySpan`1<System.Byte>,System.Int32)
ValidateHexDigits(System.ReadOnlySpan`1<System.Byte>,System.Int32)
TryGetNumber(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ConsumeNegativeSign(System.ReadOnlySpan`1<System.Byte>&,System.Int32&)
ConsumeZero(System.ReadOnlySpan`1<System.Byte>&,System.Int32&)
ConsumeIntegerDigits(System.ReadOnlySpan`1<System.Byte>&,System.Int32&)
ConsumeDecimalDigits(System.ReadOnlySpan`1<System.Byte>&,System.Int32&)
ConsumeSign(System.ReadOnlySpan`1<System.Byte>&,System.Int32&)
ConsumeNextTokenOrRollback(System.Byte)
ConsumeNextToken(System.Byte)
ConsumeNextTokenFromLastNonCommentToken()
SkipAllComments(System.Byte&)
SkipAllComments(System.Byte&,System.Text.Json.ExceptionResource)
ConsumeNextTokenUntilAfterAllCommentsAreSkipped(System.Byte)
SkipComment()
SkipSingleLineComment(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
FindLineSeparator(System.ReadOnlySpan`1<System.Byte>)
ThrowOnDangerousLineSeparator(System.ReadOnlySpan`1<System.Byte>)
SkipMultiLineComment(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ConsumeComment()
ConsumeSingleLineComment(System.ReadOnlySpan`1<System.Byte>,System.Int32)
ConsumeMultiLineComment(System.ReadOnlySpan`1<System.Byte>,System.Int32)
DebuggerDisplay()
DebugTokenType()
GetUnescapedSpan()
.ctor(System.Buffers.ReadOnlySequence`1<System.Byte>,System.Boolean,System.Text.Json.JsonReaderState)
.ctor(System.Buffers.ReadOnlySequence`1<System.Byte>,System.Text.Json.JsonReaderOptions)
ReadMultiSegment()
ValidateStateAtEndOfData()
HasMoreDataMultiSegment()
HasMoreDataMultiSegment(System.Text.Json.ExceptionResource)
GetNextSpan()
ReadFirstTokenMultiSegment(System.Byte)
SkipWhiteSpaceMultiSegment()
ConsumeValueMultiSegment(System.Byte)
ConsumeLiteralMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonTokenType)
CheckLiteralMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>,System.Int32&)
AmountToWrite(System.ReadOnlySpan`1<System.Byte>,System.Int64,System.ReadOnlySpan`1<System.Byte>,System.Int32)
FindMismatch(System.ReadOnlySpan`1<System.Byte>,System.ReadOnlySpan`1<System.Byte>)
GetInvalidLiteralMultiSegment(System.ReadOnlySpan`1<System.Byte>)
ConsumeNumberMultiSegment()
ConsumePropertyNameMultiSegment()
ConsumeStringMultiSegment()
ConsumeStringNextSegment()
ConsumeStringAndValidateMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.Int32)
RollBackState(System.Text.Json.Utf8JsonReader/PartialStateForRollback&,System.Boolean)
TryGetNumberMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ConsumeNegativeSignMultiSegment(System.ReadOnlySpan`1<System.Byte>&,System.Int32&,System.Text.Json.Utf8JsonReader/PartialStateForRollback&)
ConsumeZeroMultiSegment(System.ReadOnlySpan`1<System.Byte>&,System.Int32&,System.Text.Json.Utf8JsonReader/PartialStateForRollback&)
ConsumeIntegerDigitsMultiSegment(System.ReadOnlySpan`1<System.Byte>&,System.Int32&)
ConsumeDecimalDigitsMultiSegment(System.ReadOnlySpan`1<System.Byte>&,System.Int32&,System.Text.Json.Utf8JsonReader/PartialStateForRollback&)
ConsumeSignMultiSegment(System.ReadOnlySpan`1<System.Byte>&,System.Int32&,System.Text.Json.Utf8JsonReader/PartialStateForRollback&)
ConsumeNextTokenOrRollbackMultiSegment(System.Byte)
ConsumeNextTokenMultiSegment(System.Byte)
ConsumeNextTokenFromLastNonCommentTokenMultiSegment()
SkipAllCommentsMultiSegment(System.Byte&)
SkipAllCommentsMultiSegment(System.Byte&,System.Text.Json.ExceptionResource)
ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiSegment(System.Byte)
SkipOrConsumeCommentMultiSegmentWithRollback()
SkipCommentMultiSegment(System.Int32&)
SkipSingleLineCommentMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
FindLineSeparatorMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ThrowOnDangerousLineSeparatorMultiSegment(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
SkipMultiLineCommentMultiSegment(System.ReadOnlySpan`1<System.Byte>)
CaptureState()
.ctor(System.Int64,System.Int64,System.Int32,System.SequencePosition)
GetStartPosition(System.Int32)
GetString()
CopyString(System.Span`1<System.Byte>)
CopyValue(System.Span`1<System.Byte>)
CopyString(System.Span`1<System.Char>)
CopyValue(System.Span`1<System.Char>)
TryCopyEscapedString(System.Span`1<System.Byte>,System.Int32&)
GetComment()
GetBoolean()
GetBytesFromBase64()
GetByte()
GetByteWithQuotes()
GetSByte()
GetSByteWithQuotes()
GetInt16()
GetInt16WithQuotes()
GetInt32()
GetInt32WithQuotes()
GetInt64()
GetInt64WithQuotes()
GetUInt16()
GetUInt16WithQuotes()
GetUInt32()
GetUInt32WithQuotes()
GetUInt64()
GetUInt64WithQuotes()
GetSingle()
GetSingleWithQuotes()
GetSingleFloatingPointConstant()
GetDouble()
GetDoubleWithQuotes()
GetDoubleFloatingPointConstant()
GetDecimal()
GetDecimalWithQuotes()
GetDateTime()
GetDateTimeNoValidation()
GetDateTimeOffset()
GetDateTimeOffsetNoValidation()
GetGuid()
GetGuidNoValidation()
TryGetBytesFromBase64(System.Byte[]&)
TryGetByte(System.Byte&)
TryGetByteCore(System.Byte&,System.ReadOnlySpan`1<System.Byte>)
TryGetSByte(System.SByte&)
TryGetSByteCore(System.SByte&,System.ReadOnlySpan`1<System.Byte>)
TryGetInt16(System.Int16&)
TryGetInt16Core(System.Int16&,System.ReadOnlySpan`1<System.Byte>)
TryGetInt32(System.Int32&)
TryGetInt32Core(System.Int32&,System.ReadOnlySpan`1<System.Byte>)
TryGetInt64(System.Int64&)
TryGetInt64Core(System.Int64&,System.ReadOnlySpan`1<System.Byte>)
TryGetUInt16(System.UInt16&)
TryGetUInt16Core(System.UInt16&,System.ReadOnlySpan`1<System.Byte>)
TryGetUInt32(System.UInt32&)
TryGetUInt32Core(System.UInt32&,System.ReadOnlySpan`1<System.Byte>)
TryGetUInt64(System.UInt64&)
TryGetUInt64Core(System.UInt64&,System.ReadOnlySpan`1<System.Byte>)
TryGetSingle(System.Single&)
TryGetDouble(System.Double&)
TryGetDecimal(System.Decimal&)
TryGetDecimalCore(System.Decimal&,System.ReadOnlySpan`1<System.Byte>)
TryGetDateTime(System.DateTime&)
TryGetDateTimeCore(System.DateTime&)
TryGetDateTimeOffset(System.DateTimeOffset&)
TryGetDateTimeOffsetCore(System.DateTimeOffset&)
TryGetGuid(System.Guid&)
TryGetGuidCore(System.Guid&)