< Summary

Line coverage
6%
Covered lines: 241
Uncovered lines: 3756
Coverable lines: 3997
Total lines: 6599
Line coverage: 6%
Branch coverage
4%
Covered branches: 72
Total branches: 1759
Branch coverage: 4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .ctor(...)100%22100%
File 1: .ctor(...)100%110%
File 1: Read()20%101020%
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()0%220%
File 1: EndObject()0%880%
File 1: StartArray()0%220%
File 1: EndArray()0%880%
File 1: UpdateBitStackOnEndToken()100%110%
File 1: ReadSingleSegment()20.83%242425.35%
File 1: HasMoreData()6.25%161622.22%
File 1: HasMoreData(...)0%440%
File 1: ReadFirstToken(...)28.57%141418.42%
File 1: SkipWhiteSpace()27.77%181846.15%
File 1: ConsumeValue(...)31.25%484831.25%
File 1: ConsumeLiteral(...)0%660%
File 1: CheckLiteral(...)0%12120%
File 1: ThrowInvalidLiteral(...)0%440%
File 1: ConsumeNumber()0%12120%
File 1: ConsumePropertyName()0%10100%
File 1: ConsumeString()0%660%
File 1: ConsumeStringAndValidate(...)0%26260%
File 1: ValidateHexDigits(...)0%660%
File 1: TryGetNumber(...)0%42420%
File 1: ConsumeNegativeSign(...)0%880%
File 1: ConsumeZero(...)0%14140%
File 1: ConsumeIntegerDigits(...)0%10100%
File 1: ConsumeDecimalDigits(...)0%660%
File 1: ConsumeSign(...)0%14140%
File 1: ConsumeNextTokenOrRollback(...)0%440%
File 1: ConsumeNextToken(...)0%52520%
File 1: ConsumeNextTokenFromLastNonCommentToken()0%86860%
File 1: SkipAllComments(...)0%10100%
File 1: SkipAllComments(...)0%10100%
File 1: ConsumeNextTokenUntilAfterAllCommentsAreSkipped(...)0%58580%
File 1: SkipComment()0%880%
File 1: SkipSingleLineComment(...)0%12120%
File 1: FindLineSeparator(...)0%440%
File 1: ThrowOnDangerousLineSeparator(...)0%880%
File 1: SkipMultiLineComment(...)0%10100%
File 1: ConsumeComment()0%880%
File 1: ConsumeSingleLineComment(...)0%440%
File 1: ConsumeMultiLineComment(...)0%440%
File 1: GetUnescapedSpan()0%440%
File 2: .ctor(...)40%101066.66%
File 2: .ctor(...)100%110%
File 2: ReadMultiSegment()20.83%242425.97%
File 2: ValidateStateAtEndOfData()0%12120%
File 2: HasMoreDataMultiSegment()7.14%141421.05%
File 2: HasMoreDataMultiSegment(...)0%880%
File 2: GetNextSpan()40%101074.07%
File 2: ReadFirstTokenMultiSegment(...)28.57%141416.66%
File 2: SkipWhiteSpaceMultiSegment()75%4483.33%
File 2: ConsumeValueMultiSegment(...)25%606025.31%
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
 049        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>
 1780871        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
 1314480            {
 81#if DEBUG
 1314482                if (!_isInputSequence)
 434683                {
 434684                    Debug.Assert(_totalConsumed == 0);
 434685                }
 86#endif
 1314487                return _totalConsumed + _consumed;
 1314488            }
 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>
 1780899        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
 0119        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>
 0124        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>
 13356133        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>
 35616139        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>
 7926146        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>
 13356160        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>
 17808187        public readonly JsonReaderState CurrentState => new JsonReaderState
 17808188        (
 17808189            lineNumber: _lineNumber,
 17808190            bytePositionInLine: _bytePositionInLine,
 17808191            inObject: _inObject,
 17808192            isNotPrimitive: _isNotPrimitive,
 17808193            valueIsEscaped: ValueIsEscaped,
 17808194            trailingCommaBeforeComment: _trailingCommaBeforeComment,
 17808195            tokenType: _tokenType,
 17808196            previousTokenType: _previousTokenType,
 17808197            readerOptions: _readerOptions,
 17808198            bitStack: _bitStack
 17808199        );
 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)
 4452214        {
 4452215            _buffer = jsonData;
 216
 4452217            _isFinalBlock = isFinalBlock;
 4452218            _isInputSequence = false;
 219
 4452220            _lineNumber = state._lineNumber;
 4452221            _bytePositionInLine = state._bytePositionInLine;
 4452222            _inObject = state._inObject;
 4452223            _isNotPrimitive = state._isNotPrimitive;
 4452224            ValueIsEscaped = state._valueIsEscaped;
 4452225            _trailingCommaBeforeComment = state._trailingCommaBeforeComment;
 4452226            _tokenType = state._tokenType;
 4452227            _previousTokenType = state._previousTokenType;
 4452228            _readerOptions = state._readerOptions;
 4452229            if (_readerOptions.MaxDepth == 0)
 106230            {
 106231                _readerOptions.MaxDepth = JsonReaderOptions.DefaultMaxDepth;  // If max depth is not set, revert to the 
 106232            }
 4452233            _bitStack = state._bitStack;
 234
 4452235            _consumed = 0;
 4452236            TokenStartIndex = 0;
 4452237            _totalConsumed = 0;
 4452238            _isLastSegment = _isFinalBlock;
 4452239            _isMultiSegment = false;
 240
 4452241            ValueSpan = ReadOnlySpan<byte>.Empty;
 242
 4452243            _currentPosition = default;
 4452244            _nextPosition = default;
 4452245            _sequence = default;
 4452246            HasValueSequence = false;
 4452247            ValueSequence = ReadOnlySequence<byte>.Empty;
 4452248        }
 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()
 8904279        {
 8904280            bool retVal = _isMultiSegment ? ReadMultiSegment() : ReadSingleSegment();
 281
 0282            if (!retVal)
 0283            {
 0284                if (_isFinalBlock && TokenType is JsonTokenType.None && !_readerOptions.AllowMultipleValues)
 0285                {
 0286                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedJsonTokens);
 287                }
 0288            }
 0289            return retVal;
 0290        }
 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()
 0740        {
 0741            if (_bitStack.CurrentDepth >= _readerOptions.MaxDepth)
 0742                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ObjectDepthTooLarge);
 743
 0744            _bitStack.PushTrue();
 745
 0746            ValueSpan = _buffer.Slice(_consumed, 1);
 0747            _consumed++;
 0748            _bytePositionInLine++;
 0749            _tokenType = JsonTokenType.StartObject;
 0750            _inObject = true;
 0751        }
 752
 753        private void EndObject()
 0754        {
 0755            if (!_inObject || _bitStack.CurrentDepth <= 0)
 0756                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.MismatchedObjectArray, JsonConstants.Cl
 757
 0758            if (_trailingCommaBeforeComment)
 0759            {
 0760                if (!_readerOptions.AllowTrailingCommas)
 0761                {
 0762                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeObject
 763                }
 0764                _trailingCommaBeforeComment = false;
 0765            }
 766
 0767            _tokenType = JsonTokenType.EndObject;
 0768            ValueSpan = _buffer.Slice(_consumed, 1);
 769
 0770            UpdateBitStackOnEndToken();
 0771        }
 772
 773        private void StartArray()
 0774        {
 0775            if (_bitStack.CurrentDepth >= _readerOptions.MaxDepth)
 0776                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ArrayDepthTooLarge);
 777
 0778            _bitStack.PushFalse();
 779
 0780            ValueSpan = _buffer.Slice(_consumed, 1);
 0781            _consumed++;
 0782            _bytePositionInLine++;
 0783            _tokenType = JsonTokenType.StartArray;
 0784            _inObject = false;
 0785        }
 786
 787        private void EndArray()
 0788        {
 0789            if (_inObject || _bitStack.CurrentDepth <= 0)
 0790                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.MismatchedObjectArray, JsonConstants.Cl
 791
 0792            if (_trailingCommaBeforeComment)
 0793            {
 0794                if (!_readerOptions.AllowTrailingCommas)
 0795                {
 0796                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeArrayE
 797                }
 0798                _trailingCommaBeforeComment = false;
 0799            }
 800
 0801            _tokenType = JsonTokenType.EndArray;
 0802            ValueSpan = _buffer.Slice(_consumed, 1);
 803
 0804            UpdateBitStackOnEndToken();
 0805        }
 806
 807        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 808        private void UpdateBitStackOnEndToken()
 0809        {
 0810            _consumed++;
 0811            _bytePositionInLine++;
 0812            _inObject = _bitStack.Pop();
 0813        }
 814
 815        private bool ReadSingleSegment()
 4452816        {
 4452817            bool retVal = false;
 4452818            ValueSpan = default;
 4452819            ValueIsEscaped = false;
 820
 4452821            if (!HasMoreData())
 0822            {
 0823                goto Done;
 824            }
 825
 4452826            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.
 4452832            if (first <= JsonConstants.Space)
 252833            {
 252834                SkipWhiteSpace();
 252835                if (!HasMoreData())
 0836                {
 0837                    goto Done;
 838                }
 252839                first = _buffer[_consumed];
 252840            }
 841
 4452842            TokenStartIndex = _consumed;
 843
 4452844            if (_tokenType == JsonTokenType.None)
 4452845            {
 4452846                goto ReadFirstToken;
 847            }
 848
 0849            if (first == JsonConstants.Slash)
 0850            {
 0851                retVal = ConsumeNextTokenOrRollback(first);
 0852                goto Done;
 853            }
 854
 0855            if (_tokenType == JsonTokenType.StartObject)
 0856            {
 0857                if (first == JsonConstants.CloseBrace)
 0858                {
 0859                    EndObject();
 0860                }
 861                else
 0862                {
 0863                    if (first != JsonConstants.Quote)
 0864                    {
 0865                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 866                    }
 867
 0868                    int prevConsumed = _consumed;
 0869                    long prevPosition = _bytePositionInLine;
 0870                    long prevLineNumber = _lineNumber;
 0871                    retVal = ConsumePropertyName();
 0872                    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                }
 0882            }
 0883            else if (_tokenType == JsonTokenType.StartArray)
 0884            {
 0885                if (first == JsonConstants.CloseBracket)
 0886                {
 0887                    EndArray();
 0888                }
 889                else
 0890                {
 0891                    retVal = ConsumeValue(first);
 0892                    goto Done;
 893                }
 0894            }
 0895            else if (_tokenType == JsonTokenType.PropertyName)
 0896            {
 0897                retVal = ConsumeValue(first);
 0898                goto Done;
 899            }
 900            else
 0901            {
 0902                retVal = ConsumeNextTokenOrRollback(first);
 0903                goto Done;
 904            }
 905
 0906            retVal = true;
 907
 0908        Done:
 0909            return retVal;
 910
 4452911        ReadFirstToken:
 4452912            retVal = ReadFirstToken(first);
 0913            goto Done;
 0914        }
 915
 916        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 917        private bool HasMoreData()
 4704918        {
 4704919            if (_consumed >= (uint)_buffer.Length)
 0920            {
 0921                if (_isNotPrimitive && IsLastSpan)
 0922                {
 0923                    if (_bitStack.CurrentDepth != 0)
 0924                    {
 0925                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ZeroDepthAtEnd);
 926                    }
 927
 0928                    if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && _tokenType == JsonTokenType.Comme
 0929                    {
 0930                        return false;
 931                    }
 932
 0933                    if (_tokenType is not JsonTokenType.EndArray and not JsonTokenType.EndObject)
 0934                    {
 0935                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndOfJsonNonPrimitive);
 936                    }
 0937                }
 0938                return false;
 939            }
 4704940            return true;
 4704941        }
 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)
 0948        {
 0949            if (_consumed >= (uint)_buffer.Length)
 0950            {
 0951                if (IsLastSpan)
 0952                {
 0953                    ThrowHelper.ThrowJsonReaderException(ref this, resource);
 954                }
 0955                return false;
 956            }
 0957            return true;
 0958        }
 959
 960        private bool ReadFirstToken(byte first)
 4452961        {
 4452962            if (first == JsonConstants.OpenBrace)
 0963            {
 0964                _bitStack.SetFirstBit();
 0965                _tokenType = JsonTokenType.StartObject;
 0966                ValueSpan = _buffer.Slice(_consumed, 1);
 0967                _consumed++;
 0968                _bytePositionInLine++;
 0969                _inObject = true;
 0970                _isNotPrimitive = true;
 0971            }
 4452972            else if (first == JsonConstants.OpenBracket)
 0973            {
 0974                _bitStack.ResetFirstBit();
 0975                _tokenType = JsonTokenType.StartArray;
 0976                ValueSpan = _buffer.Slice(_consumed, 1);
 0977                _consumed++;
 0978                _bytePositionInLine++;
 0979                _isNotPrimitive = true;
 0980            }
 981            else
 4452982            {
 983                // Create local copy to avoid bounds checks.
 4452984                ReadOnlySpan<byte> localBuffer = _buffer;
 985
 4452986                if (JsonHelpers.IsDigit(first) || first == '-')
 0987                {
 0988                    if (!TryGetNumber(localBuffer.Slice(_consumed), out int numberOfBytes))
 0989                    {
 0990                        return false;
 991                    }
 0992                    _tokenType = JsonTokenType.Number;
 0993                    _consumed += numberOfBytes;
 0994                    _bytePositionInLine += numberOfBytes;
 0995                }
 4452996                else if (!ConsumeValue(first))
 0997                {
 0998                    return false;
 999                }
 1000
 01001                _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray;
 1002                // Intentionally fall out of the if-block to return true
 01003            }
 01004            return true;
 01005        }
 1006
 1007        private void SkipWhiteSpace()
 5461008        {
 1009            // Create local copy to avoid bounds checks.
 5461010            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.
 5461020            if (!OperatingSystem.IsBrowser())
 5461021            {
 5461022                ReadOnlySpan<byte> remaining = localBuffer.Slice(_consumed);
 5461023                int idx = remaining.IndexOfFirstNonWhiteSpace();
 5461024                if (idx > 0)
 1681025                {
 1026                    // Reproduce the scalar loop's line/byte-position bookkeeping for the skipped run.
 1681027                    (int newLines, int lastLineFeedIndex) = JsonReaderHelper.CountNewLines(remaining.Slice(0, idx));
 1681028                    _lineNumber += newLines;
 1681029                    if (lastLineFeedIndex >= 0)
 1681030                    {
 1031                        // Byte positions on the current line start after the last line feed character.
 1681032                        _bytePositionInLine = idx - lastLineFeedIndex - 1;
 1681033                    }
 1034                    else
 01035                    {
 01036                        _bytePositionInLine += idx;
 01037                    }
 1038
 1681039                    _consumed += idx;
 1681040                }
 1041
 1681042                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            }
 5461068        }
 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)
 44521075        {
 44521076            while (true)
 44521077            {
 44521078                Debug.Assert((_trailingCommaBeforeComment && _readerOptions.CommentHandling == JsonCommentHandling.Allow
 44521079                Debug.Assert((_trailingCommaBeforeComment && marker != JsonConstants.Slash) || !_trailingCommaBeforeComm
 44521080                _trailingCommaBeforeComment = false;
 1081
 44521082                if (marker == JsonConstants.Quote)
 01083                {
 01084                    return ConsumeString();
 1085                }
 44521086                else if (marker == JsonConstants.OpenBrace)
 01087                {
 01088                    StartObject();
 01089                }
 44521090                else if (marker == JsonConstants.OpenBracket)
 01091                {
 01092                    StartArray();
 01093                }
 44521094                else if (JsonHelpers.IsDigit(marker) || marker == '-')
 01095                {
 01096                    return ConsumeNumber();
 1097                }
 44521098                else if (marker == 'f')
 01099                {
 01100                    return ConsumeLiteral(JsonConstants.FalseValue, JsonTokenType.False);
 1101                }
 44521102                else if (marker == 't')
 01103                {
 01104                    return ConsumeLiteral(JsonConstants.TrueValue, JsonTokenType.True);
 1105                }
 44521106                else if (marker == 'n')
 01107                {
 01108                    return ConsumeLiteral(JsonConstants.NullValue, JsonTokenType.Null);
 1109                }
 1110                else
 44521111                {
 44521112                    switch (_readerOptions.CommentHandling)
 1113                    {
 1114                        case JsonCommentHandling.Disallow:
 21561115                            break;
 1116                        case JsonCommentHandling.Allow:
 01117                            if (marker == JsonConstants.Slash)
 01118                            {
 01119                                return ConsumeComment();
 1120                            }
 01121                            break;
 1122                        default:
 22961123                            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 22961124                            if (marker == JsonConstants.Slash)
 01125                            {
 01126                                if (SkipComment())
 01127                                {
 01128                                    if (_consumed >= (uint)_buffer.Length)
 01129                                    {
 01130                                        if (_isNotPrimitive && IsLastSpan && _tokenType != JsonTokenType.EndArray && _to
 01131                                        {
 01132                                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndO
 1133                                        }
 01134                                        return false;
 1135                                    }
 1136
 01137                                    marker = _buffer[_consumed];
 1138
 1139                                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not ne
 01140                                    if (marker <= JsonConstants.Space)
 01141                                    {
 01142                                        SkipWhiteSpace();
 01143                                        if (!HasMoreData())
 01144                                        {
 01145                                            return false;
 1146                                        }
 01147                                        marker = _buffer[_consumed];
 01148                                    }
 1149
 01150                                    TokenStartIndex = _consumed;
 1151
 1152                                    // Skip comments and consume the actual JSON value.
 01153                                    continue;
 1154                                }
 01155                                return false;
 1156                            }
 22961157                            break;
 1158                    }
 44521159                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, marke
 1160                }
 01161                break;
 1162            }
 01163            return true;
 01164        }
 1165
 1166        // Consumes 'null', or 'true', or 'false'
 1167        private bool ConsumeLiteral(ReadOnlySpan<byte> literal, JsonTokenType tokenType)
 01168        {
 01169            ReadOnlySpan<byte> span = _buffer.Slice(_consumed);
 01170            Debug.Assert(span.Length > 0);
 01171            Debug.Assert(span[0] == 'n' || span[0] == 't' || span[0] == 'f');
 1172
 01173            if (!span.StartsWith(literal))
 01174            {
 01175                return CheckLiteral(span, literal);
 1176            }
 1177
 01178            ValueSpan = span.Slice(0, literal.Length);
 01179            _tokenType = tokenType;
 01180            _consumed += literal.Length;
 01181            _bytePositionInLine += literal.Length;
 01182            return true;
 01183        }
 1184
 1185        private bool CheckLiteral(ReadOnlySpan<byte> span, ReadOnlySpan<byte> literal)
 01186        {
 01187            Debug.Assert(span.Length > 0 && span[0] == literal[0]);
 1188
 01189            int indexOfFirstMismatch = 0;
 1190
 01191            for (int i = 1; i < literal.Length; i++)
 01192            {
 01193                if (span.Length > i)
 01194                {
 01195                    if (span[i] != literal[i])
 01196                    {
 01197                        _bytePositionInLine += i;
 01198                        ThrowInvalidLiteral(span);
 01199                    }
 01200                }
 1201                else
 01202                {
 01203                    indexOfFirstMismatch = i;
 01204                    break;
 1205                }
 01206            }
 1207
 01208            Debug.Assert(indexOfFirstMismatch > 0 && indexOfFirstMismatch < literal.Length);
 1209
 01210            if (IsLastSpan)
 01211            {
 01212                _bytePositionInLine += indexOfFirstMismatch;
 01213                ThrowInvalidLiteral(span);
 01214            }
 01215            return false;
 01216        }
 1217
 1218        private void ThrowInvalidLiteral(ReadOnlySpan<byte> span)
 01219        {
 01220            byte firstByte = span[0];
 1221
 1222            ExceptionResource resource;
 01223            switch (firstByte)
 1224            {
 1225                case (byte)'t':
 01226                    resource = ExceptionResource.ExpectedTrue;
 01227                    break;
 1228                case (byte)'f':
 01229                    resource = ExceptionResource.ExpectedFalse;
 01230                    break;
 1231                default:
 01232                    Debug.Assert(firstByte == 'n');
 01233                    resource = ExceptionResource.ExpectedNull;
 01234                    break;
 1235            }
 01236            ThrowHelper.ThrowJsonReaderException(ref this, resource, bytes: span);
 1237        }
 1238
 1239        private bool ConsumeNumber()
 01240        {
 01241            if (!TryGetNumber(_buffer.Slice(_consumed), out int consumed))
 01242            {
 01243                return false;
 1244            }
 1245
 01246            _tokenType = JsonTokenType.Number;
 01247            _consumed += consumed;
 01248            _bytePositionInLine += consumed;
 1249
 01250            if (_consumed >= (uint)_buffer.Length)
 01251            {
 01252                Debug.Assert(IsLastSpan);
 1253
 1254                // If there is no more data, and the JSON is not a single value, throw.
 01255                if (_isNotPrimitive)
 01256                {
 01257                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, _buffer
 1258                }
 01259            }
 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
 01263            Debug.Assert(
 01264                ((_consumed < _buffer.Length) &&
 01265                !_isNotPrimitive &&
 01266                JsonConstants.Delimiters.Contains(_buffer[_consumed]))
 01267                || (_isNotPrimitive ^ (_consumed >= (uint)_buffer.Length)));
 1268
 01269            return true;
 01270        }
 1271
 1272        private bool ConsumePropertyName()
 01273        {
 01274            _trailingCommaBeforeComment = false;
 1275
 01276            if (!ConsumeString())
 01277            {
 01278                return false;
 1279            }
 1280
 01281            if (!HasMoreData(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 01282            {
 01283                return false;
 1284            }
 1285
 01286            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 ':'.
 01291            if (first <= JsonConstants.Space)
 01292            {
 01293                SkipWhiteSpace();
 01294                if (!HasMoreData(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 01295                {
 01296                    return false;
 1297                }
 01298                first = _buffer[_consumed];
 01299            }
 1300
 1301            // The next character must be a key / value separator. Validate and skip.
 01302            if (first != JsonConstants.KeyValueSeparator)
 01303            {
 01304                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedSeparatorAfterPropertyNameNotFo
 1305            }
 1306
 01307            _consumed++;
 01308            _bytePositionInLine++;
 01309            _tokenType = JsonTokenType.PropertyName;
 01310            return true;
 01311        }
 1312
 1313        private bool ConsumeString()
 01314        {
 01315            Debug.Assert(_buffer.Length >= _consumed + 1);
 01316            Debug.Assert(_buffer[_consumed] == JsonConstants.Quote);
 1317
 1318            // Create local copy to avoid bounds checks.
 01319            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.
 01325            int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 1326
 01327            if (idx >= 0)
 01328            {
 01329                byte foundByte = localBuffer[idx];
 01330                if (foundByte == JsonConstants.Quote)
 01331                {
 01332                    _bytePositionInLine += idx + 2; // Add 2 for the start and end quotes.
 01333                    ValueSpan = localBuffer.Slice(0, idx);
 01334                    ValueIsEscaped = false;
 01335                    _tokenType = JsonTokenType.String;
 01336                    _consumed += idx + 2;
 01337                    return true;
 1338                }
 1339                else
 01340                {
 01341                    return ConsumeStringAndValidate(localBuffer, idx);
 1342                }
 1343            }
 1344            else
 01345            {
 01346                if (IsLastSpan)
 01347                {
 01348                    _bytePositionInLine += localBuffer.Length + 1;  // Account for the start quote
 01349                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1350                }
 01351                return false;
 1352            }
 01353        }
 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)
 01359        {
 01360            Debug.Assert(idx >= 0 && idx < data.Length);
 01361            Debug.Assert(data[idx] != JsonConstants.Quote);
 01362            Debug.Assert(data[idx] == JsonConstants.BackSlash || data[idx] < JsonConstants.Space);
 1363
 01364            long prevLineBytePosition = _bytePositionInLine;
 01365            long prevLineNumber = _lineNumber;
 1366
 01367            _bytePositionInLine += idx + 1; // Add 1 for the first quote
 1368
 01369            bool nextCharEscaped = false;
 01370            for (; idx < data.Length; idx++)
 01371            {
 01372                byte currentByte = data[idx];
 01373                if (currentByte == JsonConstants.Quote)
 01374                {
 01375                    if (!nextCharEscaped)
 01376                    {
 01377                        goto Done;
 1378                    }
 01379                    nextCharEscaped = false;
 01380                }
 01381                else if (currentByte == JsonConstants.BackSlash)
 01382                {
 01383                    nextCharEscaped = !nextCharEscaped;
 01384                }
 01385                else if (nextCharEscaped)
 01386                {
 01387                    if (!JsonConstants.EscapableChars.Contains(currentByte))
 01388                    {
 01389                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAfterEscapeWith
 1390                    }
 1391
 01392                    if (currentByte == 'u')
 01393                    {
 1394                        // Expecting 4 hex digits to follow the escaped 'u'
 01395                        _bytePositionInLine++;  // move past the 'u'
 01396                        if (ValidateHexDigits(data, idx + 1))
 01397                        {
 01398                            idx += 4;   // Skip the 4 hex digits, the for loop accounts for idx incrementing past the 'u
 01399                        }
 1400                        else
 01401                        {
 1402                            // We found less than 4 hex digits. Check if there is more data to follow, otherwise throw.
 01403                            idx = data.Length;
 01404                            break;
 1405                        }
 1406
 01407                    }
 01408                    nextCharEscaped = false;
 01409                }
 01410                else if (currentByte < JsonConstants.Space)
 01411                {
 01412                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWithinString, curre
 1413                }
 1414
 01415                _bytePositionInLine++;
 01416            }
 1417
 01418            if (idx >= data.Length)
 01419            {
 01420                if (IsLastSpan)
 01421                {
 01422                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1423                }
 01424                _lineNumber = prevLineNumber;
 01425                _bytePositionInLine = prevLineBytePosition;
 01426                return false;
 1427            }
 1428
 01429        Done:
 01430            _bytePositionInLine++;  // Add 1 for the end quote
 01431            ValueSpan = data.Slice(0, idx);
 01432            ValueIsEscaped = true;
 01433            _tokenType = JsonTokenType.String;
 01434            _consumed += idx + 2;
 01435            return true;
 01436        }
 1437
 1438        private bool ValidateHexDigits(ReadOnlySpan<byte> data, int idx)
 01439        {
 01440            for (int j = idx; j < data.Length; j++)
 01441            {
 01442                byte nextByte = data[j];
 01443                if (!JsonReaderHelper.IsHexDigit(nextByte))
 01444                {
 01445                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidHexCharacterWithinString, ne
 1446                }
 01447                if (j - idx >= 3)
 01448                {
 01449                    return true;
 1450                }
 01451                _bytePositionInLine++;
 01452            }
 1453
 01454            return false;
 01455        }
 1456
 1457        // https://tools.ietf.org/html/rfc7159#section-6
 1458        private bool TryGetNumber(ReadOnlySpan<byte> data, out int consumed)
 01459        {
 1460            // TODO: https://github.com/dotnet/runtime/issues/27837
 01461            Debug.Assert(data.Length > 0);
 1462
 01463            consumed = 0;
 01464            int i = 0;
 1465
 01466            ConsumeNumberResult signResult = ConsumeNegativeSign(ref data, ref i);
 01467            if (signResult == ConsumeNumberResult.NeedMoreData)
 01468            {
 01469                return false;
 1470            }
 1471
 01472            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1473
 01474            byte nextByte = data[i];
 01475            Debug.Assert(nextByte is >= (byte)'0' and <= (byte)'9');
 1476
 01477            if (nextByte == '0')
 01478            {
 01479                ConsumeNumberResult result = ConsumeZero(ref data, ref i);
 01480                if (result == ConsumeNumberResult.NeedMoreData)
 01481                {
 01482                    return false;
 1483                }
 01484                if (result == ConsumeNumberResult.Success)
 01485                {
 01486                    goto Done;
 1487                }
 1488
 01489                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01490                nextByte = data[i];
 01491            }
 1492            else
 01493            {
 01494                i++;
 01495                ConsumeNumberResult result = ConsumeIntegerDigits(ref data, ref i);
 01496                if (result == ConsumeNumberResult.NeedMoreData)
 01497                {
 01498                    return false;
 1499                }
 01500                if (result == ConsumeNumberResult.Success)
 01501                {
 01502                    goto Done;
 1503                }
 1504
 01505                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01506                nextByte = data[i];
 01507                if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 01508                {
 01509                    _bytePositionInLine += i;
 01510                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, nextByt
 1511                }
 01512            }
 1513
 01514            Debug.Assert(nextByte is (byte)'.' or (byte)'E' or (byte)'e');
 1515
 01516            if (nextByte == '.')
 01517            {
 01518                i++;
 01519                ConsumeNumberResult result = ConsumeDecimalDigits(ref data, ref i);
 01520                if (result == ConsumeNumberResult.NeedMoreData)
 01521                {
 01522                    return false;
 1523                }
 01524                if (result == ConsumeNumberResult.Success)
 01525                {
 01526                    goto Done;
 1527                }
 1528
 01529                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01530                nextByte = data[i];
 01531                if (nextByte is not ((byte)'E' or (byte)'e'))
 01532                {
 01533                    _bytePositionInLine += i;
 01534                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedNextDigitEValueNotFound, ne
 1535                }
 01536            }
 1537
 01538            Debug.Assert(nextByte is (byte)'E' or (byte)'e');
 01539            i++;
 1540
 01541            signResult = ConsumeSign(ref data, ref i);
 01542            if (signResult == ConsumeNumberResult.NeedMoreData)
 01543            {
 01544                return false;
 1545            }
 1546
 01547            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1548
 01549            i++;
 01550            ConsumeNumberResult resultExponent = ConsumeIntegerDigits(ref data, ref i);
 01551            if (resultExponent == ConsumeNumberResult.NeedMoreData)
 01552            {
 01553                return false;
 1554            }
 01555            if (resultExponent == ConsumeNumberResult.Success)
 01556            {
 01557                goto Done;
 1558            }
 1559
 01560            Debug.Assert(resultExponent == ConsumeNumberResult.OperationIncomplete);
 1561
 01562            _bytePositionInLine += i;
 01563            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, data[i]);
 1564
 01565        Done:
 01566            ValueSpan = data.Slice(0, i);
 01567            consumed = i;
 01568            return true;
 01569        }
 1570
 1571        private ConsumeNumberResult ConsumeNegativeSign(ref ReadOnlySpan<byte> data, scoped ref int i)
 01572        {
 01573            byte nextByte = data[i];
 1574
 01575            if (nextByte == '-')
 01576            {
 01577                i++;
 01578                if (i >= data.Length)
 01579                {
 01580                    if (IsLastSpan)
 01581                    {
 01582                        _bytePositionInLine += i;
 01583                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1584                    }
 01585                    return ConsumeNumberResult.NeedMoreData;
 1586                }
 1587
 01588                nextByte = data[i];
 01589                if (!JsonHelpers.IsDigit(nextByte))
 01590                {
 01591                    _bytePositionInLine += i;
 01592                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nex
 1593                }
 01594            }
 01595            return ConsumeNumberResult.OperationIncomplete;
 01596        }
 1597
 1598        private ConsumeNumberResult ConsumeZero(ref ReadOnlySpan<byte> data, scoped ref int i)
 01599        {
 01600            Debug.Assert(data[i] == (byte)'0');
 01601            i++;
 1602            byte nextByte;
 01603            if (i < data.Length)
 01604            {
 01605                nextByte = data[i];
 01606                if (JsonConstants.Delimiters.Contains(nextByte))
 01607                {
 01608                    return ConsumeNumberResult.Success;
 1609                }
 01610            }
 1611            else
 01612            {
 01613                if (IsLastSpan)
 01614                {
 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".
 01618                    return ConsumeNumberResult.Success;
 1619                }
 1620                else
 01621                {
 01622                    return ConsumeNumberResult.NeedMoreData;
 1623                }
 1624            }
 01625            nextByte = data[i];
 01626            if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 01627            {
 01628                _bytePositionInLine += i;
 01629                ThrowHelper.ThrowJsonReaderException(ref this,
 01630                    JsonHelpers.IsInRangeInclusive(nextByte, '0', '9') ? ExceptionResource.InvalidLeadingZeroInNumber : 
 01631                    nextByte);
 1632            }
 1633
 01634            return ConsumeNumberResult.OperationIncomplete;
 01635        }
 1636
 1637        private ConsumeNumberResult ConsumeIntegerDigits(ref ReadOnlySpan<byte> data, scoped ref int i)
 01638        {
 01639            byte nextByte = default;
 01640            for (; i < data.Length; i++)
 01641            {
 01642                nextByte = data[i];
 01643                if (!JsonHelpers.IsDigit(nextByte))
 01644                {
 01645                    break;
 1646                }
 01647            }
 01648            if (i >= data.Length)
 01649            {
 01650                if (IsLastSpan)
 01651                {
 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.
 01655                    return ConsumeNumberResult.Success;
 1656                }
 1657                else
 01658                {
 01659                    return ConsumeNumberResult.NeedMoreData;
 1660                }
 1661            }
 01662            if (JsonConstants.Delimiters.Contains(nextByte))
 01663            {
 01664                return ConsumeNumberResult.Success;
 1665            }
 1666
 01667            return ConsumeNumberResult.OperationIncomplete;
 01668        }
 1669
 1670        private ConsumeNumberResult ConsumeDecimalDigits(ref ReadOnlySpan<byte> data, scoped ref int i)
 01671        {
 01672            if (i >= data.Length)
 01673            {
 01674                if (IsLastSpan)
 01675                {
 01676                    _bytePositionInLine += i;
 01677                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1678                }
 01679                return ConsumeNumberResult.NeedMoreData;
 1680            }
 01681            byte nextByte = data[i];
 01682            if (!JsonHelpers.IsDigit(nextByte))
 01683            {
 01684                _bytePositionInLine += i;
 01685                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterDecimal, next
 1686            }
 01687            i++;
 1688
 01689            return ConsumeIntegerDigits(ref data, ref i);
 01690        }
 1691
 1692        private ConsumeNumberResult ConsumeSign(ref ReadOnlySpan<byte> data, scoped ref int i)
 01693        {
 01694            if (i >= data.Length)
 01695            {
 01696                if (IsLastSpan)
 01697                {
 01698                    _bytePositionInLine += i;
 01699                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1700                }
 01701                return ConsumeNumberResult.NeedMoreData;
 1702            }
 1703
 01704            byte nextByte = data[i];
 01705            if (nextByte is (byte)'+' or (byte)'-')
 01706            {
 01707                i++;
 01708                if (i >= data.Length)
 01709                {
 01710                    if (IsLastSpan)
 01711                    {
 01712                        _bytePositionInLine += i;
 01713                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1714                    }
 01715                    return ConsumeNumberResult.NeedMoreData;
 1716                }
 01717                nextByte = data[i];
 01718            }
 1719
 01720            if (!JsonHelpers.IsDigit(nextByte))
 01721            {
 01722                _bytePositionInLine += i;
 01723                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nextByt
 1724            }
 1725
 01726            return ConsumeNumberResult.OperationIncomplete;
 01727        }
 1728
 1729        private bool ConsumeNextTokenOrRollback(byte marker)
 01730        {
 01731            int prevConsumed = _consumed;
 01732            long prevPosition = _bytePositionInLine;
 01733            long prevLineNumber = _lineNumber;
 01734            JsonTokenType prevTokenType = _tokenType;
 01735            bool prevTrailingCommaBeforeComment = _trailingCommaBeforeComment;
 01736            ConsumeTokenResult result = ConsumeNextToken(marker);
 01737            if (result == ConsumeTokenResult.Success)
 01738            {
 01739                return true;
 1740            }
 01741            if (result == ConsumeTokenResult.NotEnoughDataRollBackState)
 01742            {
 01743                _consumed = prevConsumed;
 01744                _tokenType = prevTokenType;
 01745                _bytePositionInLine = prevPosition;
 01746                _lineNumber = prevLineNumber;
 01747                _trailingCommaBeforeComment = prevTrailingCommaBeforeComment;
 01748            }
 01749            return false;
 01750        }
 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)
 01757        {
 01758            if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
 01759            {
 01760                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
 01772                {
 01773                    Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 01774                    return ConsumeNextTokenUntilAfterAllCommentsAreSkipped(marker);
 1775                }
 01776            }
 1777
 01778            if (_bitStack.CurrentDepth == 0)
 01779            {
 01780                if (_readerOptions.AllowMultipleValues)
 01781                {
 01782                    return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBac
 1783                }
 1784
 01785                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 1786            }
 1787
 01788            if (marker == JsonConstants.ListSeparator)
 01789            {
 01790                _consumed++;
 01791                _bytePositionInLine++;
 1792
 01793                if (_consumed >= (uint)_buffer.Length)
 01794                {
 01795                    if (IsLastSpan)
 01796                    {
 01797                        _consumed--;
 01798                        _bytePositionInLine--;
 01799                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1800                    }
 01801                    return ConsumeTokenResult.NotEnoughDataRollBackState;
 1802                }
 01803                byte first = _buffer[_consumed];
 1804
 1805                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01806                if (first <= JsonConstants.Space)
 01807                {
 01808                    SkipWhiteSpace();
 1809                    // The next character must be a start of a property name or value.
 01810                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01811                    {
 01812                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1813                    }
 01814                    first = _buffer[_consumed];
 01815                }
 1816
 01817                TokenStartIndex = _consumed;
 1818
 01819                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && first == JsonConstants.Slash)
 01820                {
 01821                    _trailingCommaBeforeComment = true;
 01822                    return ConsumeComment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState
 1823                }
 1824
 01825                if (_inObject)
 01826                {
 01827                    if (first != JsonConstants.Quote)
 01828                    {
 01829                        if (first == JsonConstants.CloseBrace)
 01830                        {
 01831                            if (_readerOptions.AllowTrailingCommas)
 01832                            {
 01833                                EndObject();
 01834                                return ConsumeTokenResult.Success;
 1835                            }
 01836                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1837                        }
 01838                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1839                    }
 01840                    return ConsumePropertyName() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 1841                }
 1842                else
 01843                {
 01844                    if (first == JsonConstants.CloseBracket)
 01845                    {
 01846                        if (_readerOptions.AllowTrailingCommas)
 01847                        {
 01848                            EndArray();
 01849                            return ConsumeTokenResult.Success;
 1850                        }
 01851                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1852                    }
 01853                    return ConsumeValue(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackSt
 1854                }
 1855            }
 01856            else if (marker == JsonConstants.CloseBrace)
 01857            {
 01858                EndObject();
 01859            }
 01860            else if (marker == JsonConstants.CloseBracket)
 01861            {
 01862                EndArray();
 01863            }
 1864            else
 01865            {
 01866                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 1867            }
 01868            return ConsumeTokenResult.Success;
 01869        }
 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)
 02117        {
 02118            while (marker == JsonConstants.Slash)
 02119            {
 02120                if (SkipComment())
 02121                {
 02122                    if (!HasMoreData())
 02123                    {
 02124                        goto IncompleteNoRollback;
 2125                    }
 2126
 02127                    marker = _buffer[_consumed];
 2128
 2129                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02130                    if (marker <= JsonConstants.Space)
 02131                    {
 02132                        SkipWhiteSpace();
 02133                        if (!HasMoreData())
 02134                        {
 02135                            goto IncompleteNoRollback;
 2136                        }
 02137                        marker = _buffer[_consumed];
 02138                    }
 02139                }
 2140                else
 02141                {
 02142                    goto IncompleteNoRollback;
 2143                }
 02144            }
 02145            return true;
 2146
 02147        IncompleteNoRollback:
 02148            return false;
 02149        }
 2150
 2151        private bool SkipAllComments(scoped ref byte marker, ExceptionResource resource)
 02152        {
 02153            while (marker == JsonConstants.Slash)
 02154            {
 02155                if (SkipComment())
 02156                {
 2157                    // The next character must be a start of a property name or value.
 02158                    if (!HasMoreData(resource))
 02159                    {
 02160                        goto IncompleteRollback;
 2161                    }
 2162
 02163                    marker = _buffer[_consumed];
 2164
 2165                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02166                    if (marker <= JsonConstants.Space)
 02167                    {
 02168                        SkipWhiteSpace();
 2169                        // The next character must be a start of a property name or value.
 02170                        if (!HasMoreData(resource))
 02171                        {
 02172                            goto IncompleteRollback;
 2173                        }
 02174                        marker = _buffer[_consumed];
 02175                    }
 02176                }
 2177                else
 02178                {
 02179                    goto IncompleteRollback;
 2180                }
 02181            }
 02182            return true;
 2183
 02184        IncompleteRollback:
 02185            return false;
 02186        }
 2187
 2188        private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkipped(byte marker)
 02189        {
 02190            if (!SkipAllComments(ref marker))
 02191            {
 02192                goto IncompleteNoRollback;
 2193            }
 2194
 02195            TokenStartIndex = _consumed;
 2196
 02197            if (_tokenType == JsonTokenType.StartObject)
 02198            {
 02199                if (marker == JsonConstants.CloseBrace)
 02200                {
 02201                    EndObject();
 02202                }
 2203                else
 02204                {
 02205                    if (marker != JsonConstants.Quote)
 02206                    {
 02207                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2208                    }
 2209
 02210                    int prevConsumed = _consumed;
 02211                    long prevPosition = _bytePositionInLine;
 02212                    long prevLineNumber = _lineNumber;
 02213                    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                }
 02224            }
 02225            else if (_tokenType == JsonTokenType.StartArray)
 02226            {
 02227                if (marker == JsonConstants.CloseBracket)
 02228                {
 02229                    EndArray();
 02230                }
 2231                else
 02232                {
 02233                    if (!ConsumeValue(marker))
 02234                    {
 02235                        goto IncompleteNoRollback;
 2236                    }
 02237                    goto Done;
 2238                }
 02239            }
 02240            else if (_tokenType == JsonTokenType.PropertyName)
 02241            {
 02242                if (!ConsumeValue(marker))
 02243                {
 02244                    goto IncompleteNoRollback;
 2245                }
 02246                goto Done;
 2247            }
 02248            else if (_bitStack.CurrentDepth == 0)
 02249            {
 02250                if (_readerOptions.AllowMultipleValues)
 02251                {
 02252                    return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBac
 2253                }
 2254
 02255                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 2256            }
 02257            else if (marker == JsonConstants.ListSeparator)
 02258            {
 02259                _consumed++;
 02260                _bytePositionInLine++;
 2261
 02262                if (_consumed >= (uint)_buffer.Length)
 02263                {
 02264                    if (IsLastSpan)
 02265                    {
 02266                        _consumed--;
 02267                        _bytePositionInLine--;
 02268                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 2269                    }
 02270                    return ConsumeTokenResult.NotEnoughDataRollBackState;
 2271                }
 02272                marker = _buffer[_consumed];
 2273
 2274                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02275                if (marker <= JsonConstants.Space)
 02276                {
 02277                    SkipWhiteSpace();
 2278                    // The next character must be a start of a property name or value.
 02279                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02280                    {
 02281                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2282                    }
 02283                    marker = _buffer[_consumed];
 02284                }
 2285
 02286                if (!SkipAllComments(ref marker, ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02287                {
 02288                    goto IncompleteRollback;
 2289                }
 2290
 02291                TokenStartIndex = _consumed;
 2292
 02293                if (_inObject)
 02294                {
 02295                    if (marker != JsonConstants.Quote)
 02296                    {
 02297                        if (marker == JsonConstants.CloseBrace)
 02298                        {
 02299                            if (_readerOptions.AllowTrailingCommas)
 02300                            {
 02301                                EndObject();
 02302                                goto Done;
 2303                            }
 02304                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 2305                        }
 2306
 02307                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2308                    }
 02309                    return ConsumePropertyName() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 2310                }
 2311                else
 02312                {
 02313                    if (marker == JsonConstants.CloseBracket)
 02314                    {
 02315                        if (_readerOptions.AllowTrailingCommas)
 02316                        {
 02317                            EndArray();
 02318                            goto Done;
 2319                        }
 02320                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2321                    }
 2322
 02323                    return ConsumeValue(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackS
 2324                }
 2325            }
 02326            else if (marker == JsonConstants.CloseBrace)
 02327            {
 02328                EndObject();
 02329            }
 02330            else if (marker == JsonConstants.CloseBracket)
 02331            {
 02332                EndArray();
 02333            }
 2334            else
 02335            {
 02336                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 2337            }
 2338
 02339        Done:
 02340            return ConsumeTokenResult.Success;
 02341        IncompleteNoRollback:
 02342            return ConsumeTokenResult.IncompleteNoRollBackNecessary;
 02343        IncompleteRollback:
 02344            return ConsumeTokenResult.NotEnoughDataRollBackState;
 02345        }
 2346
 2347        private bool SkipComment()
 02348        {
 2349            // Create local copy to avoid bounds checks.
 02350            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 2351
 02352            if (localBuffer.Length > 0)
 02353            {
 02354                byte marker = localBuffer[0];
 02355                if (marker == JsonConstants.Slash)
 02356                {
 02357                    return SkipSingleLineComment(localBuffer.Slice(1), out _);
 2358                }
 02359                else if (marker == JsonConstants.Asterisk)
 02360                {
 02361                    return SkipMultiLineComment(localBuffer.Slice(1), out _);
 2362                }
 2363                else
 02364                {
 02365                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, JsonC
 2366                }
 2367            }
 2368
 02369            if (IsLastSpan)
 02370            {
 02371                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, JsonConst
 2372            }
 02373            return false;
 02374        }
 2375
 2376        private bool SkipSingleLineComment(ReadOnlySpan<byte> localBuffer, out int idx)
 02377        {
 02378            idx = FindLineSeparator(localBuffer);
 2379            int toConsume;
 02380            if (idx != -1)
 02381            {
 02382                toConsume = idx;
 02383                if (localBuffer[idx] == JsonConstants.LineFeed)
 02384                {
 02385                    goto EndOfComment;
 2386                }
 2387
 2388                // If we are here, we have definintely found a \r. So now to check if \n follows.
 02389                Debug.Assert(localBuffer[idx] == JsonConstants.CarriageReturn);
 2390
 02391                if (idx < localBuffer.Length - 1)
 02392                {
 02393                    if (localBuffer[idx + 1] == JsonConstants.LineFeed)
 02394                    {
 02395                        toConsume++;
 02396                    }
 2397
 02398                    goto EndOfComment;
 2399                }
 2400
 02401                if (IsLastSpan)
 02402                {
 02403                    goto EndOfComment;
 2404                }
 2405                else
 02406                {
 2407                    // there might be LF in the next segment
 02408                    return false;
 2409                }
 2410            }
 2411
 02412            if (IsLastSpan)
 02413            {
 02414                idx = localBuffer.Length;
 02415                toConsume = idx;
 2416                // Assume everything on this line is a comment and there is no more data.
 02417                _bytePositionInLine += 2 + localBuffer.Length;
 02418                goto Done;
 2419            }
 2420            else
 02421            {
 02422                return false;
 2423            }
 2424
 02425        EndOfComment:
 02426            toConsume++;
 02427            _bytePositionInLine = 0;
 02428            _lineNumber++;
 2429
 02430        Done:
 02431            _consumed += 2 + toConsume;
 02432            return true;
 02433        }
 2434
 2435        private int FindLineSeparator(ReadOnlySpan<byte> localBuffer)
 02436        {
 02437            int totalIdx = 0;
 02438            while (true)
 02439            {
 02440                int idx = localBuffer.IndexOfAny(JsonConstants.LineFeed, JsonConstants.CarriageReturn, JsonConstants.Sta
 2441
 02442                if (idx == -1)
 02443                {
 02444                    return -1;
 2445                }
 2446
 02447                totalIdx += idx;
 2448
 02449                if (localBuffer[idx] != JsonConstants.StartingByteOfNonStandardSeparator)
 02450                {
 02451                    return totalIdx;
 2452                }
 2453
 02454                totalIdx++;
 02455                localBuffer = localBuffer.Slice(idx + 1);
 2456
 02457                ThrowOnDangerousLineSeparator(localBuffer);
 02458            }
 02459        }
 2460
 2461        // assumes first byte (JsonConstants.StartingByteOfNonStandardSeparator) is already read
 2462        private void ThrowOnDangerousLineSeparator(ReadOnlySpan<byte> localBuffer)
 02463        {
 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
 02468            if (localBuffer.Length < 2)
 02469            {
 02470                return;
 2471            }
 2472
 02473            byte next = localBuffer[1];
 02474            if (localBuffer[0] == 0x80 && (next is 0xA8 or 0xA9))
 02475            {
 02476                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfLineSeparator);
 2477            }
 02478        }
 2479
 2480        private bool SkipMultiLineComment(ReadOnlySpan<byte> localBuffer, out int idx)
 02481        {
 02482            idx = 0;
 02483            while (true)
 02484            {
 02485                int foundIdx = localBuffer.Slice(idx).IndexOf(JsonConstants.Slash);
 02486                if (foundIdx == -1)
 02487                {
 02488                    if (IsLastSpan)
 02489                    {
 02490                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfCommentNotFound);
 2491                    }
 02492                    return false;
 2493                }
 02494                if (foundIdx != 0 && localBuffer[foundIdx + idx - 1] == JsonConstants.Asterisk)
 02495                {
 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. */
 02498                    idx += foundIdx - 1;
 02499                    break;
 2500                }
 02501                idx += foundIdx + 1;
 02502            }
 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.
 02507            _consumed += 4 + idx;
 2508
 02509            (int newLines, int newLineIndex) = JsonReaderHelper.CountNewLines(localBuffer.Slice(0, idx));
 02510            _lineNumber += newLines;
 02511            if (newLineIndex != -1)
 02512            {
 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.
 02515                _bytePositionInLine = idx - newLineIndex + 1;
 02516            }
 2517            else
 02518            {
 02519                _bytePositionInLine += 4 + idx;
 02520            }
 02521            return true;
 02522        }
 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)
 445225        {
 445226            _buffer = jsonData.FirstSpan;
 27
 445228            _isFinalBlock = isFinalBlock;
 445229            _isInputSequence = true;
 30
 445231            _lineNumber = state._lineNumber;
 445232            _bytePositionInLine = state._bytePositionInLine;
 445233            _inObject = state._inObject;
 445234            _isNotPrimitive = state._isNotPrimitive;
 445235            ValueIsEscaped = state._valueIsEscaped;
 445236            _trailingCommaBeforeComment = state._trailingCommaBeforeComment;
 445237            _tokenType = state._tokenType;
 445238            _previousTokenType = state._previousTokenType;
 445239            _readerOptions = state._readerOptions;
 445240            if (_readerOptions.MaxDepth == 0)
 10641            {
 10642                _readerOptions.MaxDepth = JsonReaderOptions.DefaultMaxDepth;  // If max depth is not set, revert to the 
 10643            }
 445244            _bitStack = state._bitStack;
 45
 445246            _consumed = 0;
 445247            TokenStartIndex = 0;
 445248            _totalConsumed = 0;
 49
 445250            ValueSpan = ReadOnlySpan<byte>.Empty;
 51
 445252            _sequence = jsonData;
 445253            HasValueSequence = false;
 445254            ValueSequence = ReadOnlySequence<byte>.Empty;
 55
 445256            if (jsonData.IsSingleSegment)
 057            {
 058                _nextPosition = default;
 059                _currentPosition = jsonData.Start;
 060                _isLastSegment = isFinalBlock;
 061                _isMultiSegment = false;
 062            }
 63            else
 445264            {
 445265                _currentPosition = jsonData.Start;
 445266                _nextPosition = _currentPosition;
 67
 445268                bool firstSegmentIsEmpty = _buffer.Length == 0;
 445269                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
 445292                _isLastSegment = !jsonData.TryGet(ref _nextPosition, out _, advance: !firstSegmentIsEmpty) && isFinalBlo
 93
 445294                Debug.Assert(!_nextPosition.Equals(_currentPosition));
 95
 445296                _isMultiSegment = true;
 445297            }
 445298        }
 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()
 4452121        {
 4452122            bool retVal = false;
 4452123            HasValueSequence = false;
 4452124            ValueIsEscaped = false;
 4452125            ValueSpan = default;
 4452126            ValueSequence = default;
 127
 4452128            if (!HasMoreDataMultiSegment())
 0129            {
 0130                goto Done;
 131            }
 132
 4452133            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.
 4452139            if (first <= JsonConstants.Space)
 252140            {
 252141                SkipWhiteSpaceMultiSegment();
 252142                if (!HasMoreDataMultiSegment())
 0143                {
 0144                    goto Done;
 145                }
 252146                first = _buffer[_consumed];
 252147            }
 148
 4452149            TokenStartIndex = BytesConsumed;
 150
 4452151            if (_tokenType == JsonTokenType.None)
 4452152            {
 4452153                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
 4452222        ReadFirstToken:
 4452223            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()
 4704251        {
 4704252            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            }
 4704271            return true;
 4704272        }
 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()
 42299        {
 300            ReadOnlyMemory<byte> memory;
 42301            while (true)
 42302            {
 42303                Debug.Assert(!_isMultiSegment || _currentPosition.GetObject() is not null);
 42304                SequencePosition copy = _currentPosition;
 42305                _currentPosition = _nextPosition;
 42306                bool noMoreData = !_sequence.TryGet(ref _nextPosition, out memory, advance: true);
 42307                if (noMoreData)
 0308                {
 0309                    _currentPosition = copy;
 0310                    _isLastSegment = true;
 0311                    return false;
 312                }
 42313                if (memory.Length != 0)
 42314                {
 42315                    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
 42323            if (_isFinalBlock)
 42324            {
 42325                _isLastSegment = !_sequence.TryGet(ref _nextPosition, out _, advance: false);
 42326            }
 327
 42328            _buffer = memory.Span;
 42329            _totalConsumed += _consumed;
 42330            _consumed = 0;
 331
 42332            return true;
 42333        }
 334
 335        private bool ReadFirstTokenMultiSegment(byte first)
 4452336        {
 4452337            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            }
 4452347            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
 4452357            {
 4452358                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                }
 4452367                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()
 252379        {
 294380            while (true)
 294381            {
 294382                SkipWhiteSpace();
 383
 294384                if (_consumed < _buffer.Length)
 252385                {
 252386                    break;
 387                }
 388
 42389                if (!GetNextSpan())
 0390                {
 0391                    break;
 392                }
 42393            }
 252394        }
 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)
 4452401        {
 4452402            while (true)
 4452403            {
 4452404                Debug.Assert((_trailingCommaBeforeComment && _readerOptions.CommentHandling == JsonCommentHandling.Allow
 4452405                Debug.Assert((_trailingCommaBeforeComment && marker != JsonConstants.Slash) || !_trailingCommaBeforeComm
 4452406                _trailingCommaBeforeComment = false;
 407
 4452408                if (marker == JsonConstants.Quote)
 0409                {
 0410                    return ConsumeStringMultiSegment();
 411                }
 4452412                else if (marker == JsonConstants.OpenBrace)
 0413                {
 0414                    StartObject();
 0415                }
 4452416                else if (marker == JsonConstants.OpenBracket)
 0417                {
 0418                    StartArray();
 0419                }
 4452420                else if (JsonHelpers.IsDigit(marker) || marker == '-')
 0421                {
 0422                    return ConsumeNumberMultiSegment();
 423                }
 4452424                else if (marker == 'f')
 0425                {
 0426                    return ConsumeLiteralMultiSegment(JsonConstants.FalseValue, JsonTokenType.False);
 427                }
 4452428                else if (marker == 't')
 0429                {
 0430                    return ConsumeLiteralMultiSegment(JsonConstants.TrueValue, JsonTokenType.True);
 431                }
 4452432                else if (marker == 'n')
 0433                {
 0434                    return ConsumeLiteralMultiSegment(JsonConstants.NullValue, JsonTokenType.Null);
 435                }
 436                else
 4452437                {
 4452438                    switch (_readerOptions.CommentHandling)
 439                    {
 440                        case JsonCommentHandling.Disallow:
 2156441                            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:
 2296455                            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 2296456                            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                            }
 2296500                            break;
 501                    }
 4452502                    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&)