< Summary

Line coverage
27%
Covered lines: 1118
Uncovered lines: 2882
Coverable lines: 4000
Total lines: 6618
Line coverage: 27.9%
Branch coverage
22%
Covered branches: 395
Total branches: 1759
Branch coverage: 22.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()80%101080%
File 1: Skip()0%220%
File 1: SkipHelper()37.5%8847.05%
File 1: TrySkip()25%4430.76%
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()54.16%242447.88%
File 1: HasMoreData()25%161638.88%
File 1: HasMoreData(...)0%440%
File 1: ReadFirstToken(...)71.42%141465.78%
File 1: SkipWhiteSpace()33.33%181853.84%
File 1: ConsumeValue(...)37.5%484840.62%
File 1: ConsumeLiteral(...)50%6653.84%
File 1: CheckLiteral(...)33.33%121244%
File 1: ThrowInvalidLiteral(...)25%4454.54%
File 1: ConsumeNumber()0%12120%
File 1: ConsumePropertyName()0%10100%
File 1: ConsumeString()66.66%6664%
File 1: ConsumeStringAndValidate(...)26.92%262627.41%
File 1: ValidateHexDigits(...)0%660%
File 1: TryGetNumber(...)40.47%424250%
File 1: ConsumeNegativeSign(...)50%8855%
File 1: ConsumeZero(...)71.42%141468%
File 1: ConsumeIntegerDigits(...)90%101090.47%
File 1: ConsumeDecimalDigits(...)0%660%
File 1: ConsumeSign(...)28.57%141429.62%
File 1: ConsumeNextTokenOrRollback(...)0%4435%
File 1: ConsumeNextToken(...)5.76%52527.31%
File 1: ConsumeNextTokenFromLastNonCommentToken()0%86860%
File 1: SkipAllComments(...)0%10100%
File 1: SkipAllComments(...)0%10100%
File 1: ConsumeNextTokenUntilAfterAllCommentsAreSkipped(...)0%58580%
File 1: SkipComment()37.5%8850%
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()50%242444.15%
File 2: ValidateStateAtEndOfData()0%12120%
File 2: HasMoreDataMultiSegment()7.14%141421.05%
File 2: HasMoreDataMultiSegment(...)0%880%
File 2: GetNextSpan()50%101088.88%
File 2: ReadFirstTokenMultiSegment(...)71.42%141463.88%
File 2: SkipWhiteSpaceMultiSegment()75%4483.33%
File 2: ConsumeValueMultiSegment(...)30%606034.17%
File 2: ConsumeLiteralMultiSegment(...)37.5%8842.85%
File 2: CheckLiteralMultiSegment(...)61.11%181871.21%
File 2: AmountToWrite(System.ReadOnlySpan`1<System.Byte>,System.Int64,System.ReadOnlySpan`1<System.Byte>,System.Int32)100%11100%
File 2: FindMismatch(...)50%22100%
File 2: GetInvalidLiteralMultiSegment(...)25%4458.33%
File 2: ConsumeNumberMultiSegment()0%12120%
File 2: ConsumePropertyNameMultiSegment()0%10100%
File 2: ConsumeStringMultiSegment()66.66%6653.84%
File 2: ConsumeStringNextSegment()28.94%383831.45%
File 2: ConsumeStringAndValidateMultiSegment(...)17.5%404019.29%
File 2: RollBackState(...)50%2266.66%
File 2: TryGetNumberMultiSegment(...)40.9%444446.93%
File 2: ConsumeNegativeSignMultiSegment(...)33.33%121238.23%
File 2: ConsumeZeroMultiSegment(...)68.18%222272.5%
File 2: ConsumeIntegerDigitsMultiSegment(...)77.27%222281.03%
File 2: ConsumeDecimalDigitsMultiSegment(...)0%10100%
File 2: ConsumeSignMultiSegment(...)31.81%222232%
File 2: ConsumeNextTokenOrRollbackMultiSegment(...)0%4437.5%
File 2: ConsumeNextTokenMultiSegment(...)5.35%56566.66%
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(...)11.53%262616.66%
File 2: SkipSingleLineCommentMultiSegment(...)0%24240%
File 2: FindLineSeparatorMultiSegment(...)0%16160%
File 2: ThrowOnDangerousLineSeparatorMultiSegment(...)0%16160%
File 2: SkipMultiLineCommentMultiSegment(...)0%22220%
File 2: CaptureState()100%11100%
File 2: .ctor(...)100%11100%
File 2: GetStartPosition(...)100%11100%
File 3: GetString()30%101035.71%
File 3: CopyString(...)0%440%
File 3: CopyValue(...)50%121276.92%
File 3: CopyString(...)0%440%
File 3: CopyValue(...)0%16160%
File 3: TryCopyEscapedString(...)0%880%
File 3: GetComment()0%440%
File 3: GetBoolean()25%8850%
File 3: GetBytesFromBase64()0%2233.33%
File 3: GetByte()50%2266.66%
File 3: GetByteWithQuotes()0%220%
File 3: GetSByte()50%2266.66%
File 3: GetSByteWithQuotes()0%220%
File 3: GetInt16()50%2266.66%
File 3: GetInt16WithQuotes()0%220%
File 3: GetInt32()50%2266.66%
File 3: GetInt32WithQuotes()0%220%
File 3: GetInt64()50%2266.66%
File 3: GetInt64WithQuotes()0%220%
File 3: GetUInt16()50%2266.66%
File 3: GetUInt16WithQuotes()0%220%
File 3: GetUInt32()50%2266.66%
File 3: GetUInt32WithQuotes()0%220%
File 3: GetUInt64()50%2266.66%
File 3: GetUInt64WithQuotes()0%220%
File 3: GetSingle()50%2266.66%
File 3: GetSingleWithQuotes()0%880%
File 3: GetSingleFloatingPointConstant()0%220%
File 3: GetDouble()50%2266.66%
File 3: GetDoubleWithQuotes()0%880%
File 3: GetDoubleFloatingPointConstant()0%220%
File 3: GetDecimal()50%2266.66%
File 3: GetDecimalWithQuotes()0%220%
File 3: GetDateTime()0%2233.33%
File 3: GetDateTimeNoValidation()0%220%
File 3: GetDateTimeOffset()0%2233.33%
File 3: GetDateTimeOffsetNoValidation()0%220%
File 3: GetGuid()0%2233.33%
File 3: GetGuidNoValidation()0%220%
File 3: TryGetBytesFromBase64(...)16.66%6636.36%
File 3: TryGetByte(...)100%44100%
File 3: TryGetByteCore(...)50%4466.66%
File 3: TryGetSByte(...)100%44100%
File 3: TryGetSByteCore(...)50%4466.66%
File 3: TryGetInt16(...)100%44100%
File 3: TryGetInt16Core(...)50%4466.66%
File 3: TryGetInt32(...)100%44100%
File 3: TryGetInt32Core(...)50%4477.77%
File 3: TryGetInt64(...)100%44100%
File 3: TryGetInt64Core(...)50%4477.77%
File 3: TryGetUInt16(...)100%44100%
File 3: TryGetUInt16Core(...)50%4466.66%
File 3: TryGetUInt32(...)100%44100%
File 3: TryGetUInt32Core(...)50%4477.77%
File 3: TryGetUInt64(...)100%44100%
File 3: TryGetUInt64Core(...)50%4477.77%
File 3: TryGetSingle(...)75%8884.61%
File 3: TryGetDouble(...)75%8884.61%
File 3: TryGetDecimal(...)100%44100%
File 3: TryGetDecimalCore(...)50%4477.77%
File 3: TryGetDateTime(...)50%2266.66%
File 3: TryGetDateTimeCore(...)0%440%
File 3: TryGetDateTimeOffset(...)50%2266.66%
File 3: TryGetDateTimeOffsetCore(...)0%440%
File 3: TryGetGuid(...)50%2266.66%
File 3: TryGetGuidCore(...)0%440%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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
 102049        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
 1255        internal readonly int ValueLength => HasValueSequence ? checked((int)ValueSequence.Length) : ValueSpan.Length;
 56
 6857        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>
 4824271        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
 3571380            {
 81#if DEBUG
 3571382                if (!_isInputSequence)
 1188483                {
 1188484                    Debug.Assert(_totalConsumed == 0);
 1188485                }
 86#endif
 3571387                return _totalConsumed + _consumed;
 3571388            }
 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>
 4781499        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
 528108            {
 528109                int readerDepth = _bitStack.CurrentDepth;
 528110                if (TokenType is JsonTokenType.StartArray or JsonTokenType.StartObject)
 368111                {
 368112                    Debug.Assert(readerDepth >= 1);
 368113                    readerDepth--;
 368114                }
 528115                return readerDepth;
 528116            }
 117        }
 118
 12119        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>
 2838124        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>
 36719133        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>
 95392139        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>
 21302146        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>
 35893160        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>
 47604187        public readonly JsonReaderState CurrentState => new JsonReaderState
 47604188        (
 47604189            lineNumber: _lineNumber,
 47604190            bytePositionInLine: _bytePositionInLine,
 47604191            inObject: _inObject,
 47604192            isNotPrimitive: _isNotPrimitive,
 47604193            valueIsEscaped: ValueIsEscaped,
 47604194            trailingCommaBeforeComment: _trailingCommaBeforeComment,
 47604195            tokenType: _tokenType,
 47604196            previousTokenType: _previousTokenType,
 47604197            readerOptions: _readerOptions,
 47604198            bitStack: _bitStack
 47604199        );
 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)
 11898214        {
 11898215            _buffer = jsonData;
 216
 11898217            _isFinalBlock = isFinalBlock;
 11898218            _isInputSequence = false;
 219
 11898220            _lineNumber = state._lineNumber;
 11898221            _bytePositionInLine = state._bytePositionInLine;
 11898222            _inObject = state._inObject;
 11898223            _isNotPrimitive = state._isNotPrimitive;
 11898224            ValueIsEscaped = state._valueIsEscaped;
 11898225            _trailingCommaBeforeComment = state._trailingCommaBeforeComment;
 11898226            _tokenType = state._tokenType;
 11898227            _previousTokenType = state._previousTokenType;
 11898228            _readerOptions = state._readerOptions;
 11898229            if (_readerOptions.MaxDepth == 0)
 283230            {
 283231                _readerOptions.MaxDepth = JsonReaderOptions.DefaultMaxDepth;  // If max depth is not set, revert to the 
 283232            }
 11898233            _bitStack = state._bitStack;
 234
 11898235            _consumed = 0;
 11898236            TokenStartIndex = 0;
 11898237            _totalConsumed = 0;
 11898238            _isLastSegment = _isFinalBlock;
 11898239            _isMultiSegment = false;
 240
 11898241            ValueSpan = ReadOnlySpan<byte>.Empty;
 242
 11898243            _currentPosition = default;
 11898244            _nextPosition = default;
 11898245            _sequence = default;
 11898246            HasValueSequence = false;
 11898247            ValueSequence = ReadOnlySequence<byte>.Empty;
 11898248        }
 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()
 23938279        {
 23938280            bool retVal = _isMultiSegment ? ReadMultiSegment() : ReadSingleSegment();
 281
 528282            if (!retVal)
 12283            {
 12284                if (_isFinalBlock && TokenType is JsonTokenType.None && !_readerOptions.AllowMultipleValues)
 0285                {
 0286                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedJsonTokens);
 287                }
 12288            }
 528289            return retVal;
 528290        }
 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()
 48323        {
 48324            Debug.Assert(_isFinalBlock);
 325
 48326            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
 48334            if (TokenType is JsonTokenType.StartObject or JsonTokenType.StartArray)
 48335            {
 48336                int depth = CurrentDepth;
 337                do
 48338                {
 48339                    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()
 48372        {
 48373            if (_isFinalBlock)
 48374            {
 48375                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.First.Span, 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()
 11981816        {
 11981817            bool retVal = false;
 11981818            ValueSpan = default;
 11981819            ValueIsEscaped = false;
 820
 11981821            if (!HasMoreData())
 12822            {
 12823                goto Done;
 824            }
 825
 11969826            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.
 11969832            if (first <= JsonConstants.Space)
 1806833            {
 1806834                SkipWhiteSpace();
 1806835                if (!HasMoreData())
 0836                {
 0837                    goto Done;
 838                }
 1806839                first = _buffer[_consumed];
 1806840            }
 841
 11969842            TokenStartIndex = _consumed;
 843
 11969844            if (_tokenType == JsonTokenType.None)
 11898845            {
 11898846                goto ReadFirstToken;
 847            }
 848
 71849            if (first == JsonConstants.Slash)
 0850            {
 0851                retVal = ConsumeNextTokenOrRollback(first);
 0852                goto Done;
 853            }
 854
 71855            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            }
 71883            else if (_tokenType == JsonTokenType.StartArray)
 35884            {
 35885                if (first == JsonConstants.CloseBracket)
 0886                {
 0887                    EndArray();
 0888                }
 889                else
 35890                {
 35891                    retVal = ConsumeValue(first);
 0892                    goto Done;
 893                }
 0894            }
 36895            else if (_tokenType == JsonTokenType.PropertyName)
 0896            {
 0897                retVal = ConsumeValue(first);
 0898                goto Done;
 899            }
 900            else
 36901            {
 36902                retVal = ConsumeNextTokenOrRollback(first);
 0903                goto Done;
 904            }
 905
 0906            retVal = true;
 907
 276908        Done:
 276909            return retVal;
 910
 11898911        ReadFirstToken:
 11898912            retVal = ReadFirstToken(first);
 264913            goto Done;
 276914        }
 915
 916        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 917        private bool HasMoreData()
 13787918        {
 13787919            if (_consumed >= (uint)_buffer.Length)
 12920            {
 12921                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                }
 12938                return false;
 939            }
 13775940            return true;
 13787941        }
 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)
 11898961        {
 11898962            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            }
 11898972            else if (first == JsonConstants.OpenBracket)
 168973            {
 168974                _bitStack.ResetFirstBit();
 168975                _tokenType = JsonTokenType.StartArray;
 168976                ValueSpan = _buffer.Slice(_consumed, 1);
 168977                _consumed++;
 168978                _bytePositionInLine++;
 168979                _isNotPrimitive = true;
 168980            }
 981            else
 11730982            {
 983                // Create local copy to avoid bounds checks.
 11730984                ReadOnlySpan<byte> localBuffer = _buffer;
 985
 11730986                if (JsonHelpers.IsDigit(first) || first == '-')
 810987                {
 810988                    if (!TryGetNumber(localBuffer.Slice(_consumed), out int numberOfBytes))
 0989                    {
 0990                        return false;
 991                    }
 96992                    _tokenType = JsonTokenType.Number;
 96993                    _consumed += numberOfBytes;
 96994                    _bytePositionInLine += numberOfBytes;
 96995                }
 10920996                else if (!ConsumeValue(first))
 0997                {
 0998                    return false;
 999                }
 1000
 961001                _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray;
 1002                // Intentionally fall out of the if-block to return true
 961003            }
 2641004            return true;
 2641005        }
 1006
 1007        private void SkipWhiteSpace()
 36961008        {
 1009            // Create local copy to avoid bounds checks.
 36961010            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.
 36961020            if (!OperatingSystem.IsBrowser())
 36961021            {
 36961022                ReadOnlySpan<byte> remaining = localBuffer.Slice(_consumed);
 36961023                int idx = remaining.IndexOfFirstNonWhiteSpace();
 36961024                if (idx > 0)
 3361025                {
 1026                    // Reproduce the scalar loop's line/byte-position bookkeeping for the skipped run.
 3361027                    (int newLines, int lastLineFeedIndex) = JsonReaderHelper.CountNewLines(remaining.Slice(0, idx));
 3361028                    _lineNumber += newLines;
 3361029                    if (lastLineFeedIndex >= 0)
 841030                    {
 1031                        // Byte positions on the current line start after the last line feed character.
 841032                        _bytePositionInLine = idx - lastLineFeedIndex - 1;
 841033                    }
 1034                    else
 2521035                    {
 2521036                        _bytePositionInLine += idx;
 2521037                    }
 1038
 3361039                    _consumed += idx;
 3361040                }
 1041
 3361042                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            }
 36961068        }
 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)
 109551075        {
 109551076            while (true)
 109551077            {
 109551078                Debug.Assert((_trailingCommaBeforeComment && _readerOptions.CommentHandling == JsonCommentHandling.Allow
 109551079                Debug.Assert((_trailingCommaBeforeComment && marker != JsonConstants.Slash) || !_trailingCommaBeforeComm
 109551080                _trailingCommaBeforeComment = false;
 1081
 109551082                if (marker == JsonConstants.Quote)
 2521083                {
 2521084                    return ConsumeString();
 1085                }
 107031086                else if (marker == JsonConstants.OpenBrace)
 01087                {
 01088                    StartObject();
 01089                }
 107031090                else if (marker == JsonConstants.OpenBracket)
 01091                {
 01092                    StartArray();
 01093                }
 107031094                else if (JsonHelpers.IsDigit(marker) || marker == '-')
 01095                {
 01096                    return ConsumeNumber();
 1097                }
 107031098                else if (marker == 'f')
 1261099                {
 1261100                    return ConsumeLiteral(JsonConstants.FalseValue, JsonTokenType.False);
 1101                }
 105771102                else if (marker == 't')
 01103                {
 01104                    return ConsumeLiteral(JsonConstants.TrueValue, JsonTokenType.True);
 1105                }
 105771106                else if (marker == 'n')
 01107                {
 01108                    return ConsumeLiteral(JsonConstants.NullValue, JsonTokenType.Null);
 1109                }
 1110                else
 105771111                {
 105771112                    switch (_readerOptions.CommentHandling)
 1113                    {
 1114                        case JsonCommentHandling.Disallow:
 79121115                            break;
 1116                        case JsonCommentHandling.Allow:
 01117                            if (marker == JsonConstants.Slash)
 01118                            {
 01119                                return ConsumeComment();
 1120                            }
 01121                            break;
 1122                        default:
 26651123                            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 26651124                            if (marker == JsonConstants.Slash)
 821125                            {
 821126                                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                            }
 25831157                            break;
 1158                    }
 104951159                    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)
 1261168        {
 1261169            ReadOnlySpan<byte> span = _buffer.Slice(_consumed);
 1261170            Debug.Assert(span.Length > 0);
 1261171            Debug.Assert(span[0] == 'n' || span[0] == 't' || span[0] == 'f');
 1172
 1261173            if (!span.StartsWith(literal))
 1261174            {
 1261175                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)
 1261186        {
 1261187            Debug.Assert(span.Length > 0 && span[0] == literal[0]);
 1188
 1261189            int indexOfFirstMismatch = 0;
 1190
 2521191            for (int i = 1; i < literal.Length; i++)
 1261192            {
 1261193                if (span.Length > i)
 1261194                {
 1261195                    if (span[i] != literal[i])
 1261196                    {
 1261197                        _bytePositionInLine += i;
 1261198                        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)
 1261219        {
 1261220            byte firstByte = span[0];
 1221
 1222            ExceptionResource resource;
 1261223            switch (firstByte)
 1224            {
 1225                case (byte)'t':
 01226                    resource = ExceptionResource.ExpectedTrue;
 01227                    break;
 1228                case (byte)'f':
 1261229                    resource = ExceptionResource.ExpectedFalse;
 1261230                    break;
 1231                default:
 01232                    Debug.Assert(firstByte == 'n');
 01233                    resource = ExceptionResource.ExpectedNull;
 01234                    break;
 1235            }
 1261236            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()
 2521314        {
 2521315            Debug.Assert(_buffer.Length >= _consumed + 1);
 2521316            Debug.Assert(_buffer[_consumed] == JsonConstants.Quote);
 1317
 1318            // Create local copy to avoid bounds checks.
 2521319            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.
 2521325            int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 1326
 2521327            if (idx >= 0)
 1261328            {
 1261329                byte foundByte = localBuffer[idx];
 1261330                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
 1261340                {
 1261341                    return ConsumeStringAndValidate(localBuffer, idx);
 1342                }
 1343            }
 1344            else
 1261345            {
 1261346                if (IsLastSpan)
 1261347                {
 1261348                    _bytePositionInLine += localBuffer.Length + 1;  // Account for the start quote
 1261349                    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)
 1261359        {
 1261360            Debug.Assert(idx >= 0 && idx < data.Length);
 1261361            Debug.Assert(data[idx] != JsonConstants.Quote);
 1261362            Debug.Assert(data[idx] == JsonConstants.BackSlash || data[idx] < JsonConstants.Space);
 1363
 1261364            long prevLineBytePosition = _bytePositionInLine;
 1261365            long prevLineNumber = _lineNumber;
 1366
 1261367            _bytePositionInLine += idx + 1; // Add 1 for the first quote
 1368
 1261369            bool nextCharEscaped = false;
 1261370            for (; idx < data.Length; idx++)
 1261371            {
 1261372                byte currentByte = data[idx];
 1261373                if (currentByte == JsonConstants.Quote)
 01374                {
 01375                    if (!nextCharEscaped)
 01376                    {
 01377                        goto Done;
 1378                    }
 01379                    nextCharEscaped = false;
 01380                }
 1261381                else if (currentByte == JsonConstants.BackSlash)
 01382                {
 01383                    nextCharEscaped = !nextCharEscaped;
 01384                }
 1261385                else if (nextCharEscaped)
 01386                {
 01387                    int index = JsonConstants.EscapableChars.IndexOf(currentByte);
 01388                    if (index == -1)
 01389                    {
 01390                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAfterEscapeWith
 1391                    }
 1392
 01393                    if (currentByte == 'u')
 01394                    {
 1395                        // Expecting 4 hex digits to follow the escaped 'u'
 01396                        _bytePositionInLine++;  // move past the 'u'
 01397                        if (ValidateHexDigits(data, idx + 1))
 01398                        {
 01399                            idx += 4;   // Skip the 4 hex digits, the for loop accounts for idx incrementing past the 'u
 01400                        }
 1401                        else
 01402                        {
 1403                            // We found less than 4 hex digits. Check if there is more data to follow, otherwise throw.
 01404                            idx = data.Length;
 01405                            break;
 1406                        }
 1407
 01408                    }
 01409                    nextCharEscaped = false;
 01410                }
 1261411                else if (currentByte < JsonConstants.Space)
 1261412                {
 1261413                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWithinString, curre
 1414                }
 1415
 01416                _bytePositionInLine++;
 01417            }
 1418
 01419            if (idx >= data.Length)
 01420            {
 01421                if (IsLastSpan)
 01422                {
 01423                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1424                }
 01425                _lineNumber = prevLineNumber;
 01426                _bytePositionInLine = prevLineBytePosition;
 01427                return false;
 1428            }
 1429
 01430        Done:
 01431            _bytePositionInLine++;  // Add 1 for the end quote
 01432            ValueSpan = data.Slice(0, idx);
 01433            ValueIsEscaped = true;
 01434            _tokenType = JsonTokenType.String;
 01435            _consumed += idx + 2;
 01436            return true;
 01437        }
 1438
 1439        private bool ValidateHexDigits(ReadOnlySpan<byte> data, int idx)
 01440        {
 01441            for (int j = idx; j < data.Length; j++)
 01442            {
 01443                byte nextByte = data[j];
 01444                if (!JsonReaderHelper.IsHexDigit(nextByte))
 01445                {
 01446                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidHexCharacterWithinString, ne
 1447                }
 01448                if (j - idx >= 3)
 01449                {
 01450                    return true;
 1451                }
 01452                _bytePositionInLine++;
 01453            }
 1454
 01455            return false;
 01456        }
 1457
 1458        // https://tools.ietf.org/html/rfc7159#section-6
 1459        private bool TryGetNumber(ReadOnlySpan<byte> data, out int consumed)
 8101460        {
 1461            // TODO: https://github.com/dotnet/runtime/issues/27837
 8101462            Debug.Assert(data.Length > 0);
 1463
 8101464            consumed = 0;
 8101465            int i = 0;
 1466
 8101467            ConsumeNumberResult signResult = ConsumeNegativeSign(ref data, ref i);
 8101468            if (signResult == ConsumeNumberResult.NeedMoreData)
 01469            {
 01470                return false;
 1471            }
 1472
 8101473            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1474
 8101475            byte nextByte = data[i];
 8101476            Debug.Assert(nextByte is >= (byte)'0' and <= (byte)'9');
 1477
 8101478            if (nextByte == '0')
 2101479            {
 2101480                ConsumeNumberResult result = ConsumeZero(ref data, ref i);
 841481                if (result == ConsumeNumberResult.NeedMoreData)
 01482                {
 01483                    return false;
 1484                }
 841485                if (result == ConsumeNumberResult.Success)
 01486                {
 01487                    goto Done;
 1488                }
 1489
 841490                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 841491                nextByte = data[i];
 841492            }
 1493            else
 6001494            {
 6001495                i++;
 6001496                ConsumeNumberResult result = ConsumeIntegerDigits(ref data, ref i);
 6001497                if (result == ConsumeNumberResult.NeedMoreData)
 01498                {
 01499                    return false;
 1500                }
 6001501                if (result == ConsumeNumberResult.Success)
 961502                {
 961503                    goto Done;
 1504                }
 1505
 5041506                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 5041507                nextByte = data[i];
 5041508                if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 5041509                {
 5041510                    _bytePositionInLine += i;
 5041511                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, nextByt
 1512                }
 01513            }
 1514
 841515            Debug.Assert(nextByte is (byte)'.' or (byte)'E' or (byte)'e');
 1516
 841517            if (nextByte == '.')
 01518            {
 01519                i++;
 01520                ConsumeNumberResult result = ConsumeDecimalDigits(ref data, ref i);
 01521                if (result == ConsumeNumberResult.NeedMoreData)
 01522                {
 01523                    return false;
 1524                }
 01525                if (result == ConsumeNumberResult.Success)
 01526                {
 01527                    goto Done;
 1528                }
 1529
 01530                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01531                nextByte = data[i];
 01532                if (nextByte is not ((byte)'E' or (byte)'e'))
 01533                {
 01534                    _bytePositionInLine += i;
 01535                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedNextDigitEValueNotFound, ne
 1536                }
 01537            }
 1538
 841539            Debug.Assert(nextByte is (byte)'E' or (byte)'e');
 841540            i++;
 1541
 841542            signResult = ConsumeSign(ref data, ref i);
 01543            if (signResult == ConsumeNumberResult.NeedMoreData)
 01544            {
 01545                return false;
 1546            }
 1547
 01548            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1549
 01550            i++;
 01551            ConsumeNumberResult resultExponent = ConsumeIntegerDigits(ref data, ref i);
 01552            if (resultExponent == ConsumeNumberResult.NeedMoreData)
 01553            {
 01554                return false;
 1555            }
 01556            if (resultExponent == ConsumeNumberResult.Success)
 01557            {
 01558                goto Done;
 1559            }
 1560
 01561            Debug.Assert(resultExponent == ConsumeNumberResult.OperationIncomplete);
 1562
 01563            _bytePositionInLine += i;
 01564            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, data[i]);
 1565
 961566        Done:
 961567            ValueSpan = data.Slice(0, i);
 961568            consumed = i;
 961569            return true;
 961570        }
 1571
 1572        private ConsumeNumberResult ConsumeNegativeSign(ref ReadOnlySpan<byte> data, scoped ref int i)
 8101573        {
 8101574            byte nextByte = data[i];
 1575
 8101576            if (nextByte == '-')
 421577            {
 421578                i++;
 421579                if (i >= data.Length)
 01580                {
 01581                    if (IsLastSpan)
 01582                    {
 01583                        _bytePositionInLine += i;
 01584                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1585                    }
 01586                    return ConsumeNumberResult.NeedMoreData;
 1587                }
 1588
 421589                nextByte = data[i];
 421590                if (!JsonHelpers.IsDigit(nextByte))
 01591                {
 01592                    _bytePositionInLine += i;
 01593                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nex
 1594                }
 421595            }
 8101596            return ConsumeNumberResult.OperationIncomplete;
 8101597        }
 1598
 1599        private ConsumeNumberResult ConsumeZero(ref ReadOnlySpan<byte> data, scoped ref int i)
 2101600        {
 2101601            Debug.Assert(data[i] == (byte)'0');
 2101602            i++;
 1603            byte nextByte;
 2101604            if (i < data.Length)
 2101605            {
 2101606                nextByte = data[i];
 2101607                if (JsonConstants.Delimiters.Contains(nextByte))
 01608                {
 01609                    return ConsumeNumberResult.Success;
 1610                }
 2101611            }
 1612            else
 01613            {
 01614                if (IsLastSpan)
 01615                {
 1616                    // A payload containing a single value: "0" is valid
 1617                    // If we are dealing with multi-value JSON,
 1618                    // ConsumeNumber will validate that we have a delimiter following the "0".
 01619                    return ConsumeNumberResult.Success;
 1620                }
 1621                else
 01622                {
 01623                    return ConsumeNumberResult.NeedMoreData;
 1624                }
 1625            }
 2101626            nextByte = data[i];
 2101627            if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 1261628            {
 1261629                _bytePositionInLine += i;
 1261630                ThrowHelper.ThrowJsonReaderException(ref this,
 1261631                    JsonHelpers.IsInRangeInclusive(nextByte, '0', '9') ? ExceptionResource.InvalidLeadingZeroInNumber : 
 1261632                    nextByte);
 1633            }
 1634
 841635            return ConsumeNumberResult.OperationIncomplete;
 841636        }
 1637
 1638        private ConsumeNumberResult ConsumeIntegerDigits(ref ReadOnlySpan<byte> data, scoped ref int i)
 6001639        {
 6001640            byte nextByte = default;
 41641641            for (; i < data.Length; i++)
 23701642            {
 23701643                nextByte = data[i];
 23701644                if (!JsonHelpers.IsDigit(nextByte))
 5881645                {
 5881646                    break;
 1647                }
 17821648            }
 6001649            if (i >= data.Length)
 121650            {
 121651                if (IsLastSpan)
 121652                {
 1653                    // A payload containing a single value of integers (e.g. "12") is valid
 1654                    // If we are dealing with multi-value JSON,
 1655                    // ConsumeNumber will validate that we have a delimiter following the integer.
 121656                    return ConsumeNumberResult.Success;
 1657                }
 1658                else
 01659                {
 01660                    return ConsumeNumberResult.NeedMoreData;
 1661                }
 1662            }
 5881663            if (JsonConstants.Delimiters.Contains(nextByte))
 841664            {
 841665                return ConsumeNumberResult.Success;
 1666            }
 1667
 5041668            return ConsumeNumberResult.OperationIncomplete;
 6001669        }
 1670
 1671        private ConsumeNumberResult ConsumeDecimalDigits(ref ReadOnlySpan<byte> data, scoped ref int i)
 01672        {
 01673            if (i >= data.Length)
 01674            {
 01675                if (IsLastSpan)
 01676                {
 01677                    _bytePositionInLine += i;
 01678                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1679                }
 01680                return ConsumeNumberResult.NeedMoreData;
 1681            }
 01682            byte nextByte = data[i];
 01683            if (!JsonHelpers.IsDigit(nextByte))
 01684            {
 01685                _bytePositionInLine += i;
 01686                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterDecimal, next
 1687            }
 01688            i++;
 1689
 01690            return ConsumeIntegerDigits(ref data, ref i);
 01691        }
 1692
 1693        private ConsumeNumberResult ConsumeSign(ref ReadOnlySpan<byte> data, scoped ref int i)
 841694        {
 841695            if (i >= data.Length)
 01696            {
 01697                if (IsLastSpan)
 01698                {
 01699                    _bytePositionInLine += i;
 01700                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1701                }
 01702                return ConsumeNumberResult.NeedMoreData;
 1703            }
 1704
 841705            byte nextByte = data[i];
 841706            if (nextByte is (byte)'+' or (byte)'-')
 01707            {
 01708                i++;
 01709                if (i >= data.Length)
 01710                {
 01711                    if (IsLastSpan)
 01712                    {
 01713                        _bytePositionInLine += i;
 01714                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1715                    }
 01716                    return ConsumeNumberResult.NeedMoreData;
 1717                }
 01718                nextByte = data[i];
 01719            }
 1720
 841721            if (!JsonHelpers.IsDigit(nextByte))
 841722            {
 841723                _bytePositionInLine += i;
 841724                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nextByt
 1725            }
 1726
 01727            return ConsumeNumberResult.OperationIncomplete;
 01728        }
 1729
 1730        private bool ConsumeNextTokenOrRollback(byte marker)
 361731        {
 361732            int prevConsumed = _consumed;
 361733            long prevPosition = _bytePositionInLine;
 361734            long prevLineNumber = _lineNumber;
 361735            JsonTokenType prevTokenType = _tokenType;
 361736            bool prevTrailingCommaBeforeComment = _trailingCommaBeforeComment;
 361737            ConsumeTokenResult result = ConsumeNextToken(marker);
 01738            if (result == ConsumeTokenResult.Success)
 01739            {
 01740                return true;
 1741            }
 01742            if (result == ConsumeTokenResult.NotEnoughDataRollBackState)
 01743            {
 01744                _consumed = prevConsumed;
 01745                _tokenType = prevTokenType;
 01746                _bytePositionInLine = prevPosition;
 01747                _lineNumber = prevLineNumber;
 01748                _trailingCommaBeforeComment = prevTrailingCommaBeforeComment;
 01749            }
 01750            return false;
 01751        }
 1752
 1753        /// <summary>
 1754        /// This method consumes the next token regardless of whether we are inside an object or an array.
 1755        /// For an object, it reads the next property name token. For an array, it just reads the next value.
 1756        /// </summary>
 1757        private ConsumeTokenResult ConsumeNextToken(byte marker)
 361758        {
 361759            if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
 01760            {
 01761                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
 01762                {
 01763                    if (marker == JsonConstants.Slash)
 01764                    {
 01765                        return ConsumeComment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackS
 1766                    }
 01767                    if (_tokenType == JsonTokenType.Comment)
 01768                    {
 01769                        return ConsumeNextTokenFromLastNonCommentToken();
 1770                    }
 01771                }
 1772                else
 01773                {
 01774                    Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 01775                    return ConsumeNextTokenUntilAfterAllCommentsAreSkipped(marker);
 1776                }
 01777            }
 1778
 361779            if (_bitStack.CurrentDepth == 0)
 361780            {
 361781                if (_readerOptions.AllowMultipleValues)
 01782                {
 01783                    return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBac
 1784                }
 1785
 361786                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 1787            }
 1788
 01789            if (marker == JsonConstants.ListSeparator)
 01790            {
 01791                _consumed++;
 01792                _bytePositionInLine++;
 1793
 01794                if (_consumed >= (uint)_buffer.Length)
 01795                {
 01796                    if (IsLastSpan)
 01797                    {
 01798                        _consumed--;
 01799                        _bytePositionInLine--;
 01800                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1801                    }
 01802                    return ConsumeTokenResult.NotEnoughDataRollBackState;
 1803                }
 01804                byte first = _buffer[_consumed];
 1805
 1806                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01807                if (first <= JsonConstants.Space)
 01808                {
 01809                    SkipWhiteSpace();
 1810                    // The next character must be a start of a property name or value.
 01811                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01812                    {
 01813                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1814                    }
 01815                    first = _buffer[_consumed];
 01816                }
 1817
 01818                TokenStartIndex = _consumed;
 1819
 01820                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && first == JsonConstants.Slash)
 01821                {
 01822                    _trailingCommaBeforeComment = true;
 01823                    return ConsumeComment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState
 1824                }
 1825
 01826                if (_inObject)
 01827                {
 01828                    if (first != JsonConstants.Quote)
 01829                    {
 01830                        if (first == JsonConstants.CloseBrace)
 01831                        {
 01832                            if (_readerOptions.AllowTrailingCommas)
 01833                            {
 01834                                EndObject();
 01835                                return ConsumeTokenResult.Success;
 1836                            }
 01837                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1838                        }
 01839                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1840                    }
 01841                    return ConsumePropertyName() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 1842                }
 1843                else
 01844                {
 01845                    if (first == JsonConstants.CloseBracket)
 01846                    {
 01847                        if (_readerOptions.AllowTrailingCommas)
 01848                        {
 01849                            EndArray();
 01850                            return ConsumeTokenResult.Success;
 1851                        }
 01852                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1853                    }
 01854                    return ConsumeValue(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackSt
 1855                }
 1856            }
 01857            else if (marker == JsonConstants.CloseBrace)
 01858            {
 01859                EndObject();
 01860            }
 01861            else if (marker == JsonConstants.CloseBracket)
 01862            {
 01863                EndArray();
 01864            }
 1865            else
 01866            {
 01867                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 1868            }
 01869            return ConsumeTokenResult.Success;
 01870        }
 1871
 1872        private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentToken()
 01873        {
 01874            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Allow);
 01875            Debug.Assert(_tokenType == JsonTokenType.Comment);
 1876
 01877            if (JsonReaderHelper.IsTokenTypePrimitive(_previousTokenType))
 01878            {
 01879                _tokenType = _inObject ? JsonTokenType.StartObject : JsonTokenType.StartArray;
 01880            }
 1881            else
 01882            {
 01883                _tokenType = _previousTokenType;
 01884            }
 1885
 01886            Debug.Assert(_tokenType != JsonTokenType.Comment);
 1887
 01888            if (!HasMoreData())
 01889            {
 01890                goto RollBack;
 1891            }
 1892
 01893            byte first = _buffer[_consumed];
 1894
 1895            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01896            if (first <= JsonConstants.Space)
 01897            {
 01898                SkipWhiteSpace();
 01899                if (!HasMoreData())
 01900                {
 01901                    goto RollBack;
 1902                }
 01903                first = _buffer[_consumed];
 01904            }
 1905
 01906            if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None)
 01907            {
 01908                if (_readerOptions.AllowMultipleValues)
 01909                {
 01910                    return ReadFirstToken(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 1911                }
 1912
 01913                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
 1914            }
 1915
 01916            Debug.Assert(first != JsonConstants.Slash);
 1917
 01918            TokenStartIndex = _consumed;
 1919
 01920            if (first == JsonConstants.ListSeparator)
 01921            {
 1922                // A comma without some JSON value preceding it is invalid
 01923                if (_previousTokenType <= JsonTokenType.StartObject || _previousTokenType == JsonTokenType.StartArray ||
 01924                {
 01925                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueAfter
 1926                }
 1927
 01928                _consumed++;
 01929                _bytePositionInLine++;
 1930
 01931                if (_consumed >= (uint)_buffer.Length)
 01932                {
 01933                    if (IsLastSpan)
 01934                    {
 01935                        _consumed--;
 01936                        _bytePositionInLine--;
 01937                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1938                    }
 01939                    goto RollBack;
 1940                }
 01941                first = _buffer[_consumed];
 1942
 1943                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01944                if (first <= JsonConstants.Space)
 01945                {
 01946                    SkipWhiteSpace();
 1947                    // The next character must be a start of a property name or value.
 01948                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01949                    {
 01950                        goto RollBack;
 1951                    }
 01952                    first = _buffer[_consumed];
 01953                }
 1954
 01955                TokenStartIndex = _consumed;
 1956
 01957                if (first == JsonConstants.Slash)
 01958                {
 01959                    _trailingCommaBeforeComment = true;
 01960                    if (ConsumeComment())
 01961                    {
 01962                        goto Done;
 1963                    }
 1964                    else
 01965                    {
 01966                        goto RollBack;
 1967                    }
 1968                }
 1969
 01970                if (_inObject)
 01971                {
 01972                    if (first != JsonConstants.Quote)
 01973                    {
 01974                        if (first == JsonConstants.CloseBrace)
 01975                        {
 01976                            if (_readerOptions.AllowTrailingCommas)
 01977                            {
 01978                                EndObject();
 01979                                goto Done;
 1980                            }
 01981                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1982                        }
 1983
 01984                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1985                    }
 01986                    if (ConsumePropertyName())
 01987                    {
 01988                        goto Done;
 1989                    }
 1990                    else
 01991                    {
 01992                        goto RollBack;
 1993                    }
 1994                }
 1995                else
 01996                {
 01997                    if (first == JsonConstants.CloseBracket)
 01998                    {
 01999                        if (_readerOptions.AllowTrailingCommas)
 02000                        {
 02001                            EndArray();
 02002                            goto Done;
 2003                        }
 02004                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2005                    }
 2006
 02007                    if (ConsumeValue(first))
 02008                    {
 02009                        goto Done;
 2010                    }
 2011                    else
 02012                    {
 02013                        goto RollBack;
 2014                    }
 2015                }
 2016            }
 02017            else if (first == JsonConstants.CloseBrace)
 02018            {
 02019                EndObject();
 02020            }
 02021            else if (first == JsonConstants.CloseBracket)
 02022            {
 02023                EndArray();
 02024            }
 02025            else if (_tokenType == JsonTokenType.None)
 02026            {
 02027                if (ReadFirstToken(first))
 02028                {
 02029                    goto Done;
 2030                }
 2031                else
 02032                {
 02033                    goto RollBack;
 2034                }
 2035            }
 02036            else if (_tokenType == JsonTokenType.StartObject)
 02037            {
 02038                Debug.Assert(first != JsonConstants.CloseBrace);
 02039                if (first != JsonConstants.Quote)
 02040                {
 02041                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound, fi
 2042                }
 2043
 02044                int prevConsumed = _consumed;
 02045                long prevPosition = _bytePositionInLine;
 02046                long prevLineNumber = _lineNumber;
 02047                if (!ConsumePropertyName())
 02048                {
 2049                    // roll back potential changes
 02050                    _consumed = prevConsumed;
 02051                    _tokenType = JsonTokenType.StartObject;
 02052                    _bytePositionInLine = prevPosition;
 02053                    _lineNumber = prevLineNumber;
 02054                    goto RollBack;
 2055                }
 02056                goto Done;
 2057            }
 02058            else if (_tokenType == JsonTokenType.StartArray)
 02059            {
 02060                Debug.Assert(first != JsonConstants.CloseBracket);
 02061                if (!ConsumeValue(first))
 02062                {
 02063                    goto RollBack;
 2064                }
 02065                goto Done;
 2066            }
 02067            else if (_tokenType == JsonTokenType.PropertyName)
 02068            {
 02069                if (!ConsumeValue(first))
 02070                {
 02071                    goto RollBack;
 2072                }
 02073                goto Done;
 2074            }
 2075            else
 02076            {
 02077                Debug.Assert(_tokenType is JsonTokenType.EndArray or JsonTokenType.EndObject);
 02078                if (_inObject)
 02079                {
 02080                    Debug.Assert(first != JsonConstants.CloseBrace);
 02081                    if (first != JsonConstants.Quote)
 02082                    {
 02083                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2084                    }
 2085
 02086                    if (ConsumePropertyName())
 02087                    {
 02088                        goto Done;
 2089                    }
 2090                    else
 02091                    {
 02092                        goto RollBack;
 2093                    }
 2094                }
 2095                else
 02096                {
 02097                    Debug.Assert(first != JsonConstants.CloseBracket);
 2098
 02099                    if (ConsumeValue(first))
 02100                    {
 02101                        goto Done;
 2102                    }
 2103                    else
 02104                    {
 02105                        goto RollBack;
 2106                    }
 2107                }
 2108            }
 2109
 02110        Done:
 02111            return ConsumeTokenResult.Success;
 2112
 02113        RollBack:
 02114            return ConsumeTokenResult.NotEnoughDataRollBackState;
 02115        }
 2116
 2117        private bool SkipAllComments(scoped ref byte marker)
 02118        {
 02119            while (marker == JsonConstants.Slash)
 02120            {
 02121                if (SkipComment())
 02122                {
 02123                    if (!HasMoreData())
 02124                    {
 02125                        goto IncompleteNoRollback;
 2126                    }
 2127
 02128                    marker = _buffer[_consumed];
 2129
 2130                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02131                    if (marker <= JsonConstants.Space)
 02132                    {
 02133                        SkipWhiteSpace();
 02134                        if (!HasMoreData())
 02135                        {
 02136                            goto IncompleteNoRollback;
 2137                        }
 02138                        marker = _buffer[_consumed];
 02139                    }
 02140                }
 2141                else
 02142                {
 02143                    goto IncompleteNoRollback;
 2144                }
 02145            }
 02146            return true;
 2147
 02148        IncompleteNoRollback:
 02149            return false;
 02150        }
 2151
 2152        private bool SkipAllComments(scoped ref byte marker, ExceptionResource resource)
 02153        {
 02154            while (marker == JsonConstants.Slash)
 02155            {
 02156                if (SkipComment())
 02157                {
 2158                    // The next character must be a start of a property name or value.
 02159                    if (!HasMoreData(resource))
 02160                    {
 02161                        goto IncompleteRollback;
 2162                    }
 2163
 02164                    marker = _buffer[_consumed];
 2165
 2166                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02167                    if (marker <= JsonConstants.Space)
 02168                    {
 02169                        SkipWhiteSpace();
 2170                        // The next character must be a start of a property name or value.
 02171                        if (!HasMoreData(resource))
 02172                        {
 02173                            goto IncompleteRollback;
 2174                        }
 02175                        marker = _buffer[_consumed];
 02176                    }
 02177                }
 2178                else
 02179                {
 02180                    goto IncompleteRollback;
 2181                }
 02182            }
 02183            return true;
 2184
 02185        IncompleteRollback:
 02186            return false;
 02187        }
 2188
 2189        private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkipped(byte marker)
 02190        {
 02191            if (!SkipAllComments(ref marker))
 02192            {
 02193                goto IncompleteNoRollback;
 2194            }
 2195
 02196            TokenStartIndex = _consumed;
 2197
 02198            if (_tokenType == JsonTokenType.StartObject)
 02199            {
 02200                if (marker == JsonConstants.CloseBrace)
 02201                {
 02202                    EndObject();
 02203                }
 2204                else
 02205                {
 02206                    if (marker != JsonConstants.Quote)
 02207                    {
 02208                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2209                    }
 2210
 02211                    int prevConsumed = _consumed;
 02212                    long prevPosition = _bytePositionInLine;
 02213                    long prevLineNumber = _lineNumber;
 02214                    if (!ConsumePropertyName())
 02215                    {
 2216                        // roll back potential changes
 02217                        _consumed = prevConsumed;
 02218                        _tokenType = JsonTokenType.StartObject;
 02219                        _bytePositionInLine = prevPosition;
 02220                        _lineNumber = prevLineNumber;
 02221                        goto IncompleteNoRollback;
 2222                    }
 02223                    goto Done;
 2224                }
 02225            }
 02226            else if (_tokenType == JsonTokenType.StartArray)
 02227            {
 02228                if (marker == JsonConstants.CloseBracket)
 02229                {
 02230                    EndArray();
 02231                }
 2232                else
 02233                {
 02234                    if (!ConsumeValue(marker))
 02235                    {
 02236                        goto IncompleteNoRollback;
 2237                    }
 02238                    goto Done;
 2239                }
 02240            }
 02241            else if (_tokenType == JsonTokenType.PropertyName)
 02242            {
 02243                if (!ConsumeValue(marker))
 02244                {
 02245                    goto IncompleteNoRollback;
 2246                }
 02247                goto Done;
 2248            }
 02249            else if (_bitStack.CurrentDepth == 0)
 02250            {
 02251                if (_readerOptions.AllowMultipleValues)
 02252                {
 02253                    return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBac
 2254                }
 2255
 02256                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 2257            }
 02258            else if (marker == JsonConstants.ListSeparator)
 02259            {
 02260                _consumed++;
 02261                _bytePositionInLine++;
 2262
 02263                if (_consumed >= (uint)_buffer.Length)
 02264                {
 02265                    if (IsLastSpan)
 02266                    {
 02267                        _consumed--;
 02268                        _bytePositionInLine--;
 02269                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 2270                    }
 02271                    return ConsumeTokenResult.NotEnoughDataRollBackState;
 2272                }
 02273                marker = _buffer[_consumed];
 2274
 2275                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02276                if (marker <= JsonConstants.Space)
 02277                {
 02278                    SkipWhiteSpace();
 2279                    // The next character must be a start of a property name or value.
 02280                    if (!HasMoreData(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02281                    {
 02282                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2283                    }
 02284                    marker = _buffer[_consumed];
 02285                }
 2286
 02287                if (!SkipAllComments(ref marker, ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02288                {
 02289                    goto IncompleteRollback;
 2290                }
 2291
 02292                TokenStartIndex = _consumed;
 2293
 02294                if (_inObject)
 02295                {
 02296                    if (marker != JsonConstants.Quote)
 02297                    {
 02298                        if (marker == JsonConstants.CloseBrace)
 02299                        {
 02300                            if (_readerOptions.AllowTrailingCommas)
 02301                            {
 02302                                EndObject();
 02303                                goto Done;
 2304                            }
 02305                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 2306                        }
 2307
 02308                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2309                    }
 02310                    return ConsumePropertyName() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBack
 2311                }
 2312                else
 02313                {
 02314                    if (marker == JsonConstants.CloseBracket)
 02315                    {
 02316                        if (_readerOptions.AllowTrailingCommas)
 02317                        {
 02318                            EndArray();
 02319                            goto Done;
 2320                        }
 02321                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2322                    }
 2323
 02324                    return ConsumeValue(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackS
 2325                }
 2326            }
 02327            else if (marker == JsonConstants.CloseBrace)
 02328            {
 02329                EndObject();
 02330            }
 02331            else if (marker == JsonConstants.CloseBracket)
 02332            {
 02333                EndArray();
 02334            }
 2335            else
 02336            {
 02337                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 2338            }
 2339
 02340        Done:
 02341            return ConsumeTokenResult.Success;
 02342        IncompleteNoRollback:
 02343            return ConsumeTokenResult.IncompleteNoRollBackNecessary;
 02344        IncompleteRollback:
 02345            return ConsumeTokenResult.NotEnoughDataRollBackState;
 02346        }
 2347
 2348        private bool SkipComment()
 822349        {
 2350            // Create local copy to avoid bounds checks.
 822351            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 2352
 822353            if (localBuffer.Length > 0)
 822354            {
 822355                byte marker = localBuffer[0];
 822356                if (marker == JsonConstants.Slash)
 02357                {
 02358                    return SkipSingleLineComment(localBuffer.Slice(1), out _);
 2359                }
 822360                else if (marker == JsonConstants.Asterisk)
 02361                {
 02362                    return SkipMultiLineComment(localBuffer.Slice(1), out _);
 2363                }
 2364                else
 822365                {
 822366                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, JsonC
 2367                }
 2368            }
 2369
 02370            if (IsLastSpan)
 02371            {
 02372                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfValueNotFound, JsonConst
 2373            }
 02374            return false;
 02375        }
 2376
 2377        private bool SkipSingleLineComment(ReadOnlySpan<byte> localBuffer, out int idx)
 02378        {
 02379            idx = FindLineSeparator(localBuffer);
 2380            int toConsume;
 02381            if (idx != -1)
 02382            {
 02383                toConsume = idx;
 02384                if (localBuffer[idx] == JsonConstants.LineFeed)
 02385                {
 02386                    goto EndOfComment;
 2387                }
 2388
 2389                // If we are here, we have definintely found a \r. So now to check if \n follows.
 02390                Debug.Assert(localBuffer[idx] == JsonConstants.CarriageReturn);
 2391
 02392                if (idx < localBuffer.Length - 1)
 02393                {
 02394                    if (localBuffer[idx + 1] == JsonConstants.LineFeed)
 02395                    {
 02396                        toConsume++;
 02397                    }
 2398
 02399                    goto EndOfComment;
 2400                }
 2401
 02402                if (IsLastSpan)
 02403                {
 02404                    goto EndOfComment;
 2405                }
 2406                else
 02407                {
 2408                    // there might be LF in the next segment
 02409                    return false;
 2410                }
 2411            }
 2412
 02413            if (IsLastSpan)
 02414            {
 02415                idx = localBuffer.Length;
 02416                toConsume = idx;
 2417                // Assume everything on this line is a comment and there is no more data.
 02418                _bytePositionInLine += 2 + localBuffer.Length;
 02419                goto Done;
 2420            }
 2421            else
 02422            {
 02423                return false;
 2424            }
 2425
 02426        EndOfComment:
 02427            toConsume++;
 02428            _bytePositionInLine = 0;
 02429            _lineNumber++;
 2430
 02431        Done:
 02432            _consumed += 2 + toConsume;
 02433            return true;
 02434        }
 2435
 2436        private int FindLineSeparator(ReadOnlySpan<byte> localBuffer)
 02437        {
 02438            int totalIdx = 0;
 02439            while (true)
 02440            {
 02441                int idx = localBuffer.IndexOfAny(JsonConstants.LineFeed, JsonConstants.CarriageReturn, JsonConstants.Sta
 2442
 02443                if (idx == -1)
 02444                {
 02445                    return -1;
 2446                }
 2447
 02448                totalIdx += idx;
 2449
 02450                if (localBuffer[idx] != JsonConstants.StartingByteOfNonStandardSeparator)
 02451                {
 02452                    return totalIdx;
 2453                }
 2454
 02455                totalIdx++;
 02456                localBuffer = localBuffer.Slice(idx + 1);
 2457
 02458                ThrowOnDangerousLineSeparator(localBuffer);
 02459            }
 02460        }
 2461
 2462        // assumes first byte (JsonConstants.StartingByteOfNonStandardSeparator) is already read
 2463        private void ThrowOnDangerousLineSeparator(ReadOnlySpan<byte> localBuffer)
 02464        {
 2465            // \u2028 and \u2029 are considered respectively line and paragraph separators
 2466            // UTF-8 representation for them is E2, 80, A8/A9
 2467            // we have already read E2, we need to check for remaining 2 bytes
 2468
 02469            if (localBuffer.Length < 2)
 02470            {
 02471                return;
 2472            }
 2473
 02474            byte next = localBuffer[1];
 02475            if (localBuffer[0] == 0x80 && (next is 0xA8 or 0xA9))
 02476            {
 02477                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfLineSeparator);
 2478            }
 02479        }
 2480
 2481        private bool SkipMultiLineComment(ReadOnlySpan<byte> localBuffer, out int idx)
 02482        {
 02483            idx = 0;
 02484            while (true)
 02485            {
 02486                int foundIdx = localBuffer.Slice(idx).IndexOf(JsonConstants.Slash);
 02487                if (foundIdx == -1)
 02488                {
 02489                    if (IsLastSpan)
 02490                    {
 02491                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfCommentNotFound);
 2492                    }
 02493                    return false;
 2494                }
 02495                if (foundIdx != 0 && localBuffer[foundIdx + idx - 1] == JsonConstants.Asterisk)
 02496                {
 2497                    // foundIdx points just after '*' in the end-of-comment delimiter. Hence increment idx by one
 2498                    // position less to make it point right before beginning of end-of-comment delimiter i.e. */
 02499                    idx += foundIdx - 1;
 02500                    break;
 2501                }
 02502                idx += foundIdx + 1;
 02503            }
 2504
 2505            // Consume the /* and */ characters that are part of the multi-line comment.
 2506            // idx points right before the final '*' (which is right before the last '/'). Hence increment _consumed
 2507            // by 4 to exclude the start/end-of-comment delimiters.
 02508            _consumed += 4 + idx;
 2509
 02510            (int newLines, int newLineIndex) = JsonReaderHelper.CountNewLines(localBuffer.Slice(0, idx));
 02511            _lineNumber += newLines;
 02512            if (newLineIndex != -1)
 02513            {
 2514                // newLineIndex points at last newline character and byte positions in the new line start
 2515                // after that. Hence add 1 to skip the newline character.
 02516                _bytePositionInLine = idx - newLineIndex + 1;
 02517            }
 2518            else
 02519            {
 02520                _bytePositionInLine += 4 + idx;
 02521            }
 02522            return true;
 02523        }
 2524
 2525        private bool ConsumeComment()
 02526        {
 2527            // Create local copy to avoid bounds checks.
 02528            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 2529
 02530            if (localBuffer.Length > 0)
 02531            {
 02532                byte marker = localBuffer[0];
 02533                if (marker == JsonConstants.Slash)
 02534                {
 02535                    return ConsumeSingleLineComment(localBuffer.Slice(1), _consumed);
 2536                }
 02537                else if (marker == JsonConstants.Asterisk)
 02538                {
 02539                    return ConsumeMultiLineComment(localBuffer.Slice(1), _consumed);
 2540                }
 2541                else
 02542                {
 02543                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAtStartOfComment, m
 2544                }
 2545            }
 2546
 02547            if (IsLastSpan)
 02548            {
 02549                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReadingComment)
 2550            }
 02551            return false;
 02552        }
 2553
 2554        private bool ConsumeSingleLineComment(ReadOnlySpan<byte> localBuffer, int previousConsumed)
 02555        {
 02556            if (!SkipSingleLineComment(localBuffer, out int idx))
 02557            {
 02558                return false;
 2559            }
 2560
 2561            // Exclude the // at start of the comment. idx points right before the line separator
 2562            // at the end of the comment.
 02563            ValueSpan = _buffer.Slice(previousConsumed + 2, idx);
 02564            if (_tokenType != JsonTokenType.Comment)
 02565            {
 02566                _previousTokenType = _tokenType;
 02567            }
 02568            _tokenType = JsonTokenType.Comment;
 02569            return true;
 02570        }
 2571
 2572        private bool ConsumeMultiLineComment(ReadOnlySpan<byte> localBuffer, int previousConsumed)
 02573        {
 02574            if (!SkipMultiLineComment(localBuffer, out int idx))
 02575            {
 02576                return false;
 2577            }
 2578
 2579            // Exclude the /* at start of the comment. idx already points right before the terminal '*/'
 2580            // for the end of multiline comment.
 02581            ValueSpan = _buffer.Slice(previousConsumed + 2, idx);
 02582            if (_tokenType != JsonTokenType.Comment)
 02583            {
 02584                _previousTokenType = _tokenType;
 02585            }
 02586            _tokenType = JsonTokenType.Comment;
 02587            return true;
 02588        }
 2589
 2590        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 02591        private string DebuggerDisplay => $"TokenType = {DebugTokenType}, TokenStartIndex = {TokenStartIndex}, Consumed 
 2592
 2593        // Using TokenType.ToString() (or {TokenType}) fails to render in the debug window. The
 2594        // message "The runtime refused to evaluate the expression at this time." is shown. This
 2595        // is a workaround until we root cause and fix the issue.
 2596        private string DebugTokenType
 02597            => TokenType switch
 02598            {
 02599                JsonTokenType.Comment => nameof(JsonTokenType.Comment),
 02600                JsonTokenType.EndArray => nameof(JsonTokenType.EndArray),
 02601                JsonTokenType.EndObject => nameof(JsonTokenType.EndObject),
 02602                JsonTokenType.False => nameof(JsonTokenType.False),
 02603                JsonTokenType.None => nameof(JsonTokenType.None),
 02604                JsonTokenType.Null => nameof(JsonTokenType.Null),
 02605                JsonTokenType.Number => nameof(JsonTokenType.Number),
 02606                JsonTokenType.PropertyName => nameof(JsonTokenType.PropertyName),
 02607                JsonTokenType.StartArray => nameof(JsonTokenType.StartArray),
 02608                JsonTokenType.StartObject => nameof(JsonTokenType.StartObject),
 02609                JsonTokenType.String => nameof(JsonTokenType.String),
 02610                JsonTokenType.True => nameof(JsonTokenType.True),
 02611                _ => ((byte)TokenType).ToString()
 02612            };
 2613
 2614        private ReadOnlySpan<byte> GetUnescapedSpan()
 02615        {
 02616            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 02617            if (ValueIsEscaped)
 02618            {
 02619                span = JsonReaderHelper.GetUnescaped(span);
 02620            }
 2621
 02622            return span;
 02623        }
 2624    }
 2625}
 2626

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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)
 1188625        {
 1188626            _buffer = jsonData.First.Span;
 27
 1188628            _isFinalBlock = isFinalBlock;
 1188629            _isInputSequence = true;
 30
 1188631            _lineNumber = state._lineNumber;
 1188632            _bytePositionInLine = state._bytePositionInLine;
 1188633            _inObject = state._inObject;
 1188634            _isNotPrimitive = state._isNotPrimitive;
 1188635            ValueIsEscaped = state._valueIsEscaped;
 1188636            _trailingCommaBeforeComment = state._trailingCommaBeforeComment;
 1188637            _tokenType = state._tokenType;
 1188638            _previousTokenType = state._previousTokenType;
 1188639            _readerOptions = state._readerOptions;
 1188640            if (_readerOptions.MaxDepth == 0)
 28341            {
 28342                _readerOptions.MaxDepth = JsonReaderOptions.DefaultMaxDepth;  // If max depth is not set, revert to the 
 28343            }
 1188644            _bitStack = state._bitStack;
 45
 1188646            _consumed = 0;
 1188647            TokenStartIndex = 0;
 1188648            _totalConsumed = 0;
 49
 1188650            ValueSpan = ReadOnlySpan<byte>.Empty;
 51
 1188652            _sequence = jsonData;
 1188653            HasValueSequence = false;
 1188654            ValueSequence = ReadOnlySequence<byte>.Empty;
 55
 1188656            if (jsonData.IsSingleSegment)
 057            {
 058                _nextPosition = default;
 059                _currentPosition = jsonData.Start;
 060                _isLastSegment = isFinalBlock;
 061                _isMultiSegment = false;
 062            }
 63            else
 1188664            {
 1188665                _currentPosition = jsonData.Start;
 1188666                _nextPosition = _currentPosition;
 67
 1188668                bool firstSegmentIsEmpty = _buffer.Length == 0;
 1188669                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
 1188692                _isLastSegment = !jsonData.TryGet(ref _nextPosition, out _, advance: !firstSegmentIsEmpty) && isFinalBlo
 93
 1188694                Debug.Assert(!_nextPosition.Equals(_currentPosition));
 95
 1188696                _isMultiSegment = true;
 1188697            }
 1188698        }
 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()
 11957121        {
 11957122            bool retVal = false;
 11957123            HasValueSequence = false;
 11957124            ValueIsEscaped = false;
 11957125            ValueSpan = default;
 11957126            ValueSequence = default;
 127
 11957128            if (!HasMoreDataMultiSegment())
 0129            {
 0130                goto Done;
 131            }
 132
 11957133            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.
 11957139            if (first <= JsonConstants.Space)
 1806140            {
 1806141                SkipWhiteSpaceMultiSegment();
 1806142                if (!HasMoreDataMultiSegment())
 0143                {
 0144                    goto Done;
 145                }
 1806146                first = _buffer[_consumed];
 1806147            }
 148
 11957149            TokenStartIndex = BytesConsumed;
 150
 11957151            if (_tokenType == JsonTokenType.None)
 11886152            {
 11886153                goto ReadFirstToken;
 154            }
 155
 71156            if (first == JsonConstants.Slash)
 0157            {
 0158                retVal = ConsumeNextTokenOrRollbackMultiSegment(first);
 0159                goto Done;
 160            }
 161
 71162            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            }
 71194            else if (_tokenType == JsonTokenType.StartArray)
 35195            {
 35196                if (first == JsonConstants.CloseBracket)
 0197                {
 0198                    EndArray();
 0199                }
 200                else
 35201                {
 35202                    retVal = ConsumeValueMultiSegment(first);
 0203                    goto Done;
 204                }
 0205            }
 36206            else if (_tokenType == JsonTokenType.PropertyName)
 0207            {
 0208                retVal = ConsumeValueMultiSegment(first);
 0209                goto Done;
 210            }
 211            else
 36212            {
 36213                retVal = ConsumeNextTokenOrRollbackMultiSegment(first);
 0214                goto Done;
 215            }
 216
 0217            retVal = true;
 218
 252219        Done:
 252220            return retVal;
 221
 11886222        ReadFirstToken:
 11886223            retVal = ReadFirstTokenMultiSegment(first);
 252224            goto Done;
 252225        }
 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()
 13763251        {
 13763252            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            }
 13763271            return true;
 13763272        }
 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()
 1176299        {
 300            ReadOnlyMemory<byte> memory;
 1176301            while (true)
 1176302            {
 1176303                Debug.Assert(!_isMultiSegment || _currentPosition.GetObject() is not null);
 1176304                SequencePosition copy = _currentPosition;
 1176305                _currentPosition = _nextPosition;
 1176306                bool noMoreData = !_sequence.TryGet(ref _nextPosition, out memory, advance: true);
 1176307                if (noMoreData)
 126308                {
 126309                    _currentPosition = copy;
 126310                    _isLastSegment = true;
 126311                    return false;
 312                }
 1050313                if (memory.Length != 0)
 1050314                {
 1050315                    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
 1050323            if (_isFinalBlock)
 1050324            {
 1050325                _isLastSegment = !_sequence.TryGet(ref _nextPosition, out _, advance: false);
 1050326            }
 327
 1050328            _buffer = memory.Span;
 1050329            _totalConsumed += _consumed;
 1050330            _consumed = 0;
 331
 1050332            return true;
 1176333        }
 334
 335        private bool ReadFirstTokenMultiSegment(byte first)
 11886336        {
 11886337            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            }
 11886347            else if (first == JsonConstants.OpenBracket)
 168348            {
 168349                _bitStack.ResetFirstBit();
 168350                _tokenType = JsonTokenType.StartArray;
 168351                ValueSpan = _buffer.Slice(_consumed, 1);
 168352                _consumed++;
 168353                _bytePositionInLine++;
 168354                _isNotPrimitive = true;
 168355            }
 356            else
 11718357            {
 11718358                if (JsonHelpers.IsDigit(first) || first == '-')
 798359                {
 798360                    if (!TryGetNumberMultiSegment(_buffer.Slice(_consumed), out int numberOfBytes))
 0361                    {
 0362                        return false;
 363                    }
 84364                    _tokenType = JsonTokenType.Number;
 84365                    _consumed += numberOfBytes;
 84366                }
 10920367                else if (!ConsumeValueMultiSegment(first))
 0368                {
 0369                    return false;
 370                }
 371
 84372                _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray;
 373                // Intentionally fall out of the if-block to return true
 84374            }
 252375            return true;
 252376        }
 377
 378        private void SkipWhiteSpaceMultiSegment()
 1806379        {
 1890380            while (true)
 1890381            {
 1890382                SkipWhiteSpace();
 383
 1890384                if (_consumed < _buffer.Length)
 1806385                {
 1806386                    break;
 387                }
 388
 84389                if (!GetNextSpan())
 0390                {
 0391                    break;
 392                }
 84393            }
 1806394        }
 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)
 10955401        {
 10955402            while (true)
 10955403            {
 10955404                Debug.Assert((_trailingCommaBeforeComment && _readerOptions.CommentHandling == JsonCommentHandling.Allow
 10955405                Debug.Assert((_trailingCommaBeforeComment && marker != JsonConstants.Slash) || !_trailingCommaBeforeComm
 10955406                _trailingCommaBeforeComment = false;
 407
 10955408                if (marker == JsonConstants.Quote)
 252409                {
 252410                    return ConsumeStringMultiSegment();
 411                }
 10703412                else if (marker == JsonConstants.OpenBrace)
 0413                {
 0414                    StartObject();
 0415                }
 10703416                else if (marker == JsonConstants.OpenBracket)
 0417                {
 0418                    StartArray();
 0419                }
 10703420                else if (JsonHelpers.IsDigit(marker) || marker == '-')
 0421                {
 0422                    return ConsumeNumberMultiSegment();
 423                }
 10703424                else if (marker == 'f')
 126425                {
 126426                    return ConsumeLiteralMultiSegment(JsonConstants.FalseValue, JsonTokenType.False);
 427                }
 10577428                else if (marker == 't')
 0429                {
 0430                    return ConsumeLiteralMultiSegment(JsonConstants.TrueValue, JsonTokenType.True);
 431                }
 10577432                else if (marker == 'n')
 0433                {
 0434                    return ConsumeLiteralMultiSegment(JsonConstants.NullValue, JsonTokenType.Null);
 435                }
 436                else
 10577437                {
 10577438                    switch (_readerOptions.CommentHandling)
 439                    {
 440                        case JsonCommentHandling.Disallow:
 7912441                            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:
 2665455                            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 2665456                            if (marker == JsonConstants.Slash)
 82457                            {
 82458                                SequencePosition copy = _currentPosition;
 82459                                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                            }
 2583500                            break;
 501                    }
 10495502                    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)
 126511        {
 126512            ReadOnlySpan<byte> span = _buffer.Slice(_consumed);
 126513            Debug.Assert(span.Length > 0);
 126514            Debug.Assert(span[0] == 'n' || span[0] == 't' || span[0] == 'f');
 515
 126516            int consumed = literal.Length;
 517
 126518            if (!span.StartsWith(literal))
 126519            {
 126520                int prevConsumed = _consumed;
 126521                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
 126539        {
 126540            Debug.Assert(span.Length > 0 && span[0] == literal[0] && literal.Length <= JsonConstants.MaximumLiteralLengt
 541
 126542            Span<byte> readSoFar = stackalloc byte[JsonConstants.MaximumLiteralLength];
 126543            int written = 0;
 544
 126545            long prevTotalConsumed = _totalConsumed;
 126546            SequencePosition copy = _currentPosition;
 126547            if (span.Length >= literal.Length || IsLastSpan)
 42548            {
 42549                _bytePositionInLine += FindMismatch(span, literal);
 550
 42551                int amountToWrite = AmountToWrite(span, _bytePositionInLine, readSoFar, written);
 42552                span.Slice(0, amountToWrite).CopyTo(readSoFar);
 42553                written += amountToWrite;
 42554                goto Throw;
 555            }
 556            else
 84557            {
 84558                if (!literal.StartsWith(span))
 42559                {
 42560                    _bytePositionInLine += FindMismatch(span, literal);
 42561                    int amountToWrite = AmountToWrite(span, _bytePositionInLine, readSoFar, written);
 42562                    span.Slice(0, amountToWrite).CopyTo(readSoFar);
 42563                    written += amountToWrite;
 42564                    goto Throw;
 565                }
 566
 42567                ReadOnlySpan<byte> leftToMatch = literal.Slice(span.Length);
 568
 42569                SequencePosition startPosition = _currentPosition;
 42570                int startConsumed = _consumed;
 42571                int alreadyMatched = literal.Length - leftToMatch.Length;
 42572                while (true)
 42573                {
 42574                    _totalConsumed += alreadyMatched;
 42575                    _bytePositionInLine += alreadyMatched;
 42576                    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
 42588                    int amountToWrite = Math.Min(span.Length, readSoFar.Length - written);
 42589                    span.Slice(0, amountToWrite).CopyTo(readSoFar.Slice(written));
 42590                    written += amountToWrite;
 591
 42592                    span = _buffer;
 593
 42594                    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
 42604                    if (!leftToMatch.StartsWith(span))
 42605                    {
 42606                        _bytePositionInLine += FindMismatch(span, leftToMatch);
 607
 42608                        amountToWrite = AmountToWrite(span, _bytePositionInLine, readSoFar, written);
 42609                        span.Slice(0, amountToWrite).CopyTo(readSoFar.Slice(written));
 42610                        written += amountToWrite;
 611
 42612                        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
 126621            {
 126622                return Math.Min(
 126623                    readSoFar.Length - written,
 126624                    Math.Min(span.Length, (int)bytePositionInLine + 1));
 126625            }
 126626        Throw:
 126627            _totalConsumed = prevTotalConsumed;
 126628            consumed = default;
 126629            _currentPosition = copy;
 126630            throw GetInvalidLiteralMultiSegment(readSoFar.Slice(0, written).ToArray());
 0631        }
 632
 633        private static int FindMismatch(ReadOnlySpan<byte> span, ReadOnlySpan<byte> literal)
 126634        {
 126635            Debug.Assert(span.Length > 0);
 636
 637            int indexOfFirstMismatch;
 638
 639#if NET
 126640            indexOfFirstMismatch = span.CommonPrefixLength(literal);
 641#else
 642            int minLength = Math.Min(span.Length, literal.Length);
 643            for (indexOfFirstMismatch = 0; indexOfFirstMismatch < minLength; indexOfFirstMismatch++)
 644            {
 645                if (span[indexOfFirstMismatch] != literal[indexOfFirstMismatch])
 646                {
 647                    break;
 648                }
 649            }
 650#endif
 651
 126652            Debug.Assert(indexOfFirstMismatch >= 0 && indexOfFirstMismatch < literal.Length);
 653
 126654            return indexOfFirstMismatch;
 126655        }
 656
 657        private JsonException GetInvalidLiteralMultiSegment(ReadOnlySpan<byte> span)
 126658        {
 126659            byte firstByte = span[0];
 660
 661            ExceptionResource resource;
 126662            switch (firstByte)
 663            {
 664                case (byte)'t':
 0665                    resource = ExceptionResource.ExpectedTrue;
 0666                    break;
 667                case (byte)'f':
 126668                    resource = ExceptionResource.ExpectedFalse;
 126669                    break;
 670                default:
 0671                    Debug.Assert(firstByte == 'n');
 0672                    resource = ExceptionResource.ExpectedNull;
 0673                    break;
 674            }
 126675            return ThrowHelper.GetJsonReaderException(ref this, resource, nextByte: default, bytes: span);
 126676        }
 677
 678        private bool ConsumeNumberMultiSegment()
 0679        {
 0680            if (!TryGetNumberMultiSegment(_buffer.Slice(_consumed), out int consumed))
 0681            {
 0682                return false;
 683            }
 684
 0685            _tokenType = JsonTokenType.Number;
 0686            _consumed += consumed;
 687
 0688            if (_consumed >= (uint)_buffer.Length)
 0689            {
 0690                Debug.Assert(IsLastSpan);
 691
 692                // If there is no more data, and the JSON is not a single value, throw.
 0693                if (_isNotPrimitive)
 0694                {
 0695                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, _buffer
 696                }
 0697            }
 698
 699            // If there is more data and the JSON is not a single value, assert that there is an end of number delimiter
 700            // Else, if either the JSON is a single value XOR if there is no more data, don't assert anything since ther
 0701            Debug.Assert(
 0702                ((_consumed < _buffer.Length) &&
 0703                !_isNotPrimitive &&
 0704                JsonConstants.Delimiters.Contains(_buffer[_consumed]))
 0705                || (_isNotPrimitive ^ (_consumed >= (uint)_buffer.Length)));
 706
 0707            return true;
 0708        }
 709
 710        private bool ConsumePropertyNameMultiSegment()
 0711        {
 0712            _trailingCommaBeforeComment = false;
 713
 0714            if (!ConsumeStringMultiSegment())
 0715            {
 0716                return false;
 717            }
 718
 0719            if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 0720            {
 0721                return false;
 722            }
 723
 0724            byte first = _buffer[_consumed];
 725
 726            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 727            // We do not validate if 'first' is an invalid JSON byte here (such as control characters).
 728            // Those cases are captured below where we only accept ':'.
 0729            if (first <= JsonConstants.Space)
 0730            {
 0731                SkipWhiteSpaceMultiSegment();
 0732                if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedValueAfterPropertyNameNotFound))
 0733                {
 0734                    return false;
 735                }
 0736                first = _buffer[_consumed];
 0737            }
 738
 739            // The next character must be a key / value separator. Validate and skip.
 0740            if (first != JsonConstants.KeyValueSeparator)
 0741            {
 0742                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedSeparatorAfterPropertyNameNotFo
 743            }
 744
 0745            _consumed++;
 0746            _bytePositionInLine++;
 0747            _tokenType = JsonTokenType.PropertyName;
 0748            return true;
 0749        }
 750
 751        private bool ConsumeStringMultiSegment()
 252752        {
 252753            Debug.Assert(_buffer.Length >= _consumed + 1);
 252754            Debug.Assert(_buffer[_consumed] == JsonConstants.Quote);
 755
 756            // Create local copy to avoid bounds checks.
 252757            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed + 1);
 758
 759            // Vectorized search for either quote, backslash, or any control character.
 760            // If the first found byte is a quote, we have reached an end of string, and
 761            // can avoid validation.
 762            // Otherwise, in the uncommon case, iterate one character at a time and validate.
 252763            int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 764
 252765            if (idx >= 0)
 84766            {
 84767                byte foundByte = localBuffer[idx];
 84768                if (foundByte == JsonConstants.Quote)
 0769                {
 0770                    _bytePositionInLine += idx + 2; // Add 2 for the start and end quotes.
 0771                    ValueSpan = localBuffer.Slice(0, idx);
 0772                    HasValueSequence = false;
 0773                    ValueIsEscaped = false;
 0774                    _tokenType = JsonTokenType.String;
 0775                    _consumed += idx + 2;
 0776                    return true;
 777                }
 778                else
 84779                {
 84780                    return ConsumeStringAndValidateMultiSegment(localBuffer, idx);
 781                }
 782            }
 783            else
 168784            {
 168785                if (IsLastSpan)
 0786                {
 0787                    _bytePositionInLine += localBuffer.Length + 1;  // Account for the start quote
 0788                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 789                }
 168790                return ConsumeStringNextSegment();
 791            }
 0792        }
 793
 794        private bool ConsumeStringNextSegment()
 168795        {
 168796            PartialStateForRollback rollBackState = CaptureState();
 797
 798            SequencePosition end;
 168799            HasValueSequence = true;
 168800            int leftOver = _buffer.Length - _consumed;
 801
 546802            while (true)
 546803            {
 546804                if (!GetNextSpan())
 126805                {
 126806                    if (IsLastSpan)
 126807                    {
 126808                        _bytePositionInLine += leftOver;
 126809                        RollBackState(rollBackState, isError: true);
 126810                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 811                    }
 0812                    RollBackState(rollBackState);
 0813                    return false;
 814                }
 815
 816                //Create local copy to avoid bounds checks.
 420817                ReadOnlySpan<byte> localBuffer = _buffer;
 420818                int idx = localBuffer.IndexOfQuoteOrAnyControlOrBackSlash();
 819
 420820                if (idx >= 0)
 42821                {
 42822                    byte foundByte = localBuffer[idx];
 42823                    if (foundByte == JsonConstants.Quote)
 0824                    {
 0825                        end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + idx);
 0826                        _bytePositionInLine += leftOver + idx + 1;  // Add 1 for the end quote of the string.
 0827                        _totalConsumed += leftOver;
 0828                        _consumed = idx + 1;    // Add 1 for the end quote of the string.
 0829                        ValueIsEscaped = false;
 0830                        break;
 831                    }
 832                    else
 42833                    {
 42834                        _bytePositionInLine += leftOver + idx;
 42835                        ValueIsEscaped = true;
 836
 42837                        bool nextCharEscaped = false;
 42838                        while (true)
 42839                        {
 42840                        StartOfLoop:
 42841                            for (; idx < localBuffer.Length; idx++)
 42842                            {
 42843                                byte currentByte = localBuffer[idx];
 42844                                if (currentByte == JsonConstants.Quote)
 0845                                {
 0846                                    if (!nextCharEscaped)
 0847                                    {
 0848                                        goto Done;
 849                                    }
 0850                                    nextCharEscaped = false;
 0851                                }
 42852                                else if (currentByte == JsonConstants.BackSlash)
 0853                                {
 0854                                    nextCharEscaped = !nextCharEscaped;
 0855                                }
 42856                                else if (nextCharEscaped)
 0857                                {
 0858                                    int index = JsonConstants.EscapableChars.IndexOf(currentByte);
 0859                                    if (index == -1)
 0860                                    {
 0861                                        RollBackState(rollBackState, isError: true);
 0862                                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacte
 863                                    }
 864
 0865                                    if (currentByte == 'u')
 0866                                    {
 867                                        // Expecting 4 hex digits to follow the escaped 'u'
 0868                                        _bytePositionInLine++;  // move past the 'u'
 869
 0870                                        int numberOfHexDigits = 0;
 0871                                        int j = idx + 1;
 0872                                        while (true)
 0873                                        {
 0874                                            for (; j < localBuffer.Length; j++)
 0875                                            {
 0876                                                byte nextByte = localBuffer[j];
 0877                                                if (!JsonReaderHelper.IsHexDigit(nextByte))
 0878                                                {
 0879                                                    RollBackState(rollBackState, isError: true);
 0880                                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.Inv
 881                                                }
 0882                                                numberOfHexDigits++;
 0883                                                _bytePositionInLine++;
 0884                                                if (numberOfHexDigits >= 4)
 0885                                                {
 0886                                                    nextCharEscaped = false;
 0887                                                    idx = j + 1; // Skip the 4 hex digits, the for loop accounts for idx
 0888                                                    goto StartOfLoop;
 889                                                }
 0890                                            }
 891
 0892                                            if (!GetNextSpan())
 0893                                            {
 0894                                                if (IsLastSpan)
 0895                                                {
 0896                                                    RollBackState(rollBackState, isError: true);
 0897                                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.End
 898                                                }
 899
 900                                                // We found less than 4 hex digits.
 0901                                                RollBackState(rollBackState);
 0902                                                return false;
 903                                            }
 904
 0905                                            _totalConsumed += localBuffer.Length;
 906
 0907                                            localBuffer = _buffer;
 0908                                            j = 0;
 0909                                        }
 910                                    }
 0911                                    nextCharEscaped = false;
 0912                                }
 42913                                else if (currentByte < JsonConstants.Space)
 42914                                {
 42915                                    RollBackState(rollBackState, isError: true);
 42916                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWit
 917                                }
 918
 0919                                _bytePositionInLine++;
 0920                            }
 921
 0922                            if (!GetNextSpan())
 0923                            {
 0924                                if (IsLastSpan)
 0925                                {
 0926                                    RollBackState(rollBackState, isError: true);
 0927                                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound
 928                                }
 0929                                RollBackState(rollBackState);
 0930                                return false;
 931                            }
 932
 0933                            _totalConsumed += localBuffer.Length;
 0934                            localBuffer = _buffer;
 0935                            idx = 0;
 0936                        }
 937
 0938                    Done:
 0939                        _bytePositionInLine++;  // Add 1 for the end quote of the string.
 0940                        _consumed = idx + 1;    // Add 1 for the end quote of the string.
 0941                        _totalConsumed += leftOver;
 0942                        end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + idx);
 0943                        break;
 944                    }
 945                }
 946
 378947                _totalConsumed += localBuffer.Length;
 378948                _bytePositionInLine += localBuffer.Length;
 378949            }
 950
 0951            SequencePosition start = rollBackState.GetStartPosition(offset: 1); // Offset for the starting quote
 0952            ValueSequence = _sequence.Slice(start, end);
 0953            _tokenType = JsonTokenType.String;
 0954            return true;
 0955        }
 956
 957        // Found a backslash or control characters which are considered invalid within a string.
 958        // Search through the rest of the string one byte at a time.
 959        // https://tools.ietf.org/html/rfc8259#section-7
 960        private bool ConsumeStringAndValidateMultiSegment(ReadOnlySpan<byte> data, int idx)
 84961        {
 84962            Debug.Assert(idx >= 0 && idx < data.Length);
 84963            Debug.Assert(data[idx] != JsonConstants.Quote);
 84964            Debug.Assert(data[idx] == JsonConstants.BackSlash || data[idx] < JsonConstants.Space);
 965
 84966            PartialStateForRollback rollBackState = CaptureState();
 967
 968            SequencePosition end;
 84969            HasValueSequence = false;
 84970            int leftOverFromConsumed = _buffer.Length - _consumed;
 971
 84972            _bytePositionInLine += idx + 1; // Add 1 for the first quote
 973
 84974            bool nextCharEscaped = false;
 84975            while (true)
 84976            {
 84977            StartOfLoop:
 84978                for (; idx < data.Length; idx++)
 84979                {
 84980                    byte currentByte = data[idx];
 84981                    if (currentByte == JsonConstants.Quote)
 0982                    {
 0983                        if (!nextCharEscaped)
 0984                        {
 0985                            goto Done;
 986                        }
 0987                        nextCharEscaped = false;
 0988                    }
 84989                    else if (currentByte == JsonConstants.BackSlash)
 0990                    {
 0991                        nextCharEscaped = !nextCharEscaped;
 0992                    }
 84993                    else if (nextCharEscaped)
 0994                    {
 0995                        int index = JsonConstants.EscapableChars.IndexOf(currentByte);
 0996                        if (index == -1)
 0997                        {
 0998                            RollBackState(rollBackState, isError: true);
 0999                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAfterEscape
 1000                        }
 1001
 01002                        if (currentByte == 'u')
 01003                        {
 1004                            // Expecting 4 hex digits to follow the escaped 'u'
 01005                            _bytePositionInLine++;  // move past the 'u'
 1006
 01007                            int numberOfHexDigits = 0;
 01008                            int j = idx + 1;
 01009                            while (true)
 01010                            {
 01011                                for (; j < data.Length; j++)
 01012                                {
 01013                                    byte nextByte = data[j];
 01014                                    if (!JsonReaderHelper.IsHexDigit(nextByte))
 01015                                    {
 01016                                        RollBackState(rollBackState, isError: true);
 01017                                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidHexChara
 1018                                    }
 01019                                    numberOfHexDigits++;
 01020                                    _bytePositionInLine++;
 01021                                    if (numberOfHexDigits >= 4)
 01022                                    {
 01023                                        nextCharEscaped = false;
 01024                                        idx = j + 1; // Skip the 4 hex digits, the for loop accounts for idx incrementin
 01025                                        goto StartOfLoop;
 1026                                    }
 01027                                }
 1028
 01029                                if (!GetNextSpan())
 01030                                {
 01031                                    if (IsLastSpan)
 01032                                    {
 01033                                        RollBackState(rollBackState, isError: true);
 01034                                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotF
 1035                                    }
 1036
 1037                                    // We found less than 4 hex digits.
 01038                                    RollBackState(rollBackState);
 01039                                    return false;
 1040                                }
 1041
 1042                                // Do not add the left over for the first segment to total consumed
 01043                                if (HasValueSequence)
 01044                                {
 01045                                    _totalConsumed += data.Length;
 01046                                }
 1047
 01048                                data = _buffer;
 01049                                j = 0;
 01050                                HasValueSequence = true;
 01051                            }
 1052                        }
 01053                        nextCharEscaped = false;
 01054                    }
 841055                    else if (currentByte < JsonConstants.Space)
 841056                    {
 841057                        RollBackState(rollBackState, isError: true);
 841058                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterWithinString, c
 1059                    }
 1060
 01061                    _bytePositionInLine++;
 01062                }
 1063
 01064                if (!GetNextSpan())
 01065                {
 01066                    if (IsLastSpan)
 01067                    {
 01068                        RollBackState(rollBackState, isError: true);
 01069                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.EndOfStringNotFound);
 1070                    }
 01071                    RollBackState(rollBackState);
 01072                    return false;
 1073                }
 1074
 1075                // Do not add the left over for the first segment to total consumed
 01076                if (HasValueSequence)
 01077                {
 01078                    _totalConsumed += data.Length;
 01079                }
 1080
 01081                data = _buffer;
 01082                idx = 0;
 01083                HasValueSequence = true;
 01084            }
 1085
 01086        Done:
 01087            if (HasValueSequence)
 01088            {
 01089                _bytePositionInLine++;  // Add 1 for the end quote of the string.
 01090                _consumed = idx + 1;    // Add 1 for the end quote of the string.
 01091                _totalConsumed += leftOverFromConsumed;
 01092                end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + idx);
 01093                SequencePosition start = rollBackState.GetStartPosition(offset: 1); // Offset for the starting quote
 01094                ValueSequence = _sequence.Slice(start, end);
 01095            }
 1096            else
 01097            {
 01098                _bytePositionInLine++;  // Add 1 for the end quote
 01099                _consumed += idx + 2;
 01100                ValueSpan = data.Slice(0, idx);
 01101            }
 1102
 01103            ValueIsEscaped = true;
 01104            _tokenType = JsonTokenType.String;
 01105            return true;
 01106        }
 1107
 1108        private void RollBackState(scoped in PartialStateForRollback state, bool isError = false)
 9661109        {
 9661110            _totalConsumed = state._prevTotalConsumed;
 1111
 1112            // Don't roll back byte position in line for invalid JSON since that is provided
 1113            // to the user within the exception.
 9661114            if (!isError)
 01115            {
 01116                _bytePositionInLine = state._prevBytePositionInLine;
 01117            }
 1118
 9661119            _consumed = state._prevConsumed;
 9661120            _currentPosition = state._prevCurrentPosition;
 9661121        }
 1122
 1123        // https://tools.ietf.org/html/rfc7159#section-6
 1124        private bool TryGetNumberMultiSegment(ReadOnlySpan<byte> data, out int consumed)
 7981125        {
 1126            // TODO: https://github.com/dotnet/runtime/issues/27837
 7981127            Debug.Assert(data.Length > 0);
 1128
 7981129            PartialStateForRollback rollBackState = CaptureState();
 1130
 7981131            consumed = 0;
 7981132            int i = 0;
 1133
 7981134            ConsumeNumberResult signResult = ConsumeNegativeSignMultiSegment(ref data, ref i, rollBackState);
 7981135            if (signResult == ConsumeNumberResult.NeedMoreData)
 01136            {
 01137                RollBackState(rollBackState);
 01138                return false;
 1139            }
 1140
 7981141            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1142
 7981143            byte nextByte = data[i];
 7981144            Debug.Assert(nextByte is >= (byte)'0' and <= (byte)'9');
 1145
 7981146            if (nextByte == '0')
 2101147            {
 2101148                ConsumeNumberResult result = ConsumeZeroMultiSegment(ref data, ref i, rollBackState);
 841149                if (result == ConsumeNumberResult.NeedMoreData)
 01150                {
 01151                    RollBackState(rollBackState);
 01152                    return false;
 1153                }
 841154                if (result == ConsumeNumberResult.Success)
 01155                {
 01156                    goto Done;
 1157                }
 1158
 841159                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 841160                nextByte = data[i];
 841161            }
 1162            else
 5881163            {
 5881164                ConsumeNumberResult result = ConsumeIntegerDigitsMultiSegment(ref data, ref i);
 5881165                if (result == ConsumeNumberResult.NeedMoreData)
 01166                {
 01167                    RollBackState(rollBackState);
 01168                    return false;
 1169                }
 5881170                if (result == ConsumeNumberResult.Success)
 841171                {
 841172                    goto Done;
 1173                }
 1174
 5041175                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 5041176                nextByte = data[i];
 5041177                if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 5041178                {
 5041179                    RollBackState(rollBackState, isError: true);
 5041180                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, nextByt
 1181                }
 01182            }
 1183
 841184            Debug.Assert(nextByte is (byte)'.' or (byte)'E' or (byte)'e');
 1185
 841186            if (nextByte == '.')
 01187            {
 01188                i++;
 01189                _bytePositionInLine++;
 01190                ConsumeNumberResult result = ConsumeDecimalDigitsMultiSegment(ref data, ref i, rollBackState);
 01191                if (result == ConsumeNumberResult.NeedMoreData)
 01192                {
 01193                    RollBackState(rollBackState);
 01194                    return false;
 1195                }
 01196                if (result == ConsumeNumberResult.Success)
 01197                {
 01198                    goto Done;
 1199                }
 1200
 01201                Debug.Assert(result == ConsumeNumberResult.OperationIncomplete);
 01202                nextByte = data[i];
 01203                if (nextByte is not ((byte)'E' or (byte)'e'))
 01204                {
 01205                    RollBackState(rollBackState, isError: true);
 01206                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedNextDigitEValueNotFound, ne
 1207                }
 01208            }
 1209
 841210            Debug.Assert(nextByte is (byte)'E' or (byte)'e');
 841211            i++;
 841212            _bytePositionInLine++;
 1213
 841214            signResult = ConsumeSignMultiSegment(ref data, ref i, rollBackState);
 01215            if (signResult == ConsumeNumberResult.NeedMoreData)
 01216            {
 01217                RollBackState(rollBackState);
 01218                return false;
 1219            }
 1220
 01221            Debug.Assert(signResult == ConsumeNumberResult.OperationIncomplete);
 1222
 01223            i++;
 01224            _bytePositionInLine++;
 01225            ConsumeNumberResult resultExponent = ConsumeIntegerDigitsMultiSegment(ref data, ref i);
 01226            if (resultExponent == ConsumeNumberResult.NeedMoreData)
 01227            {
 01228                RollBackState(rollBackState);
 01229                return false;
 1230            }
 01231            if (resultExponent == ConsumeNumberResult.Success)
 01232            {
 01233                goto Done;
 1234            }
 1235
 01236            Debug.Assert(resultExponent == ConsumeNumberResult.OperationIncomplete);
 1237
 01238            RollBackState(rollBackState, isError: true);
 01239            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndOfDigitNotFound, data[i]);
 1240
 841241        Done:
 841242            if (HasValueSequence)
 841243            {
 841244                SequencePosition start = rollBackState.GetStartPosition();
 841245                SequencePosition end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() 
 841246                ValueSequence = _sequence.Slice(start, end);
 841247                consumed = i;
 841248            }
 1249            else
 01250            {
 01251                ValueSpan = data.Slice(0, i);
 01252                consumed = i;
 01253            }
 841254            return true;
 841255        }
 1256
 1257        private ConsumeNumberResult ConsumeNegativeSignMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scope
 7981258        {
 7981259            Debug.Assert(i == 0);
 7981260            byte nextByte = data[i];
 1261
 7981262            if (nextByte == '-')
 421263            {
 421264                i++;
 421265                _bytePositionInLine++;
 421266                if (i >= data.Length)
 01267                {
 01268                    if (IsLastSpan)
 01269                    {
 01270                        RollBackState(rollBackState, isError: true);
 01271                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1272                    }
 01273                    if (!GetNextSpan())
 01274                    {
 01275                        if (IsLastSpan)
 01276                        {
 01277                            RollBackState(rollBackState, isError: true);
 01278                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfD
 1279                        }
 01280                        return ConsumeNumberResult.NeedMoreData;
 1281                    }
 01282                    Debug.Assert(i == 1);
 01283                    _totalConsumed += i;
 01284                    HasValueSequence = true;
 01285                    i = 0;
 01286                    data = _buffer;
 01287                }
 1288
 421289                nextByte = data[i];
 421290                if (!JsonHelpers.IsDigit(nextByte))
 01291                {
 01292                    RollBackState(rollBackState, isError: true);
 01293                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nex
 1294                }
 421295            }
 7981296            return ConsumeNumberResult.OperationIncomplete;
 7981297        }
 1298
 1299        private ConsumeNumberResult ConsumeZeroMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scoped in Par
 2101300        {
 2101301            Debug.Assert(data[i] == (byte)'0');
 2101302            Debug.Assert(i == 0 || i == 1);
 2101303            i++;
 2101304            _bytePositionInLine++;
 1305            byte nextByte;
 2101306            if (i < data.Length)
 1681307            {
 1681308                nextByte = data[i];
 1681309                if (JsonConstants.Delimiters.Contains(nextByte))
 01310                {
 01311                    return ConsumeNumberResult.Success;
 1312                }
 1681313            }
 1314            else
 421315            {
 421316                if (IsLastSpan)
 01317                {
 1318                    // A payload containing a single value: "0" is valid
 1319                    // If we are dealing with multi-value JSON,
 1320                    // ConsumeNumber will validate that we have a delimiter following the "0".
 01321                    return ConsumeNumberResult.Success;
 1322                }
 1323
 421324                if (!GetNextSpan())
 01325                {
 01326                    if (IsLastSpan)
 01327                    {
 01328                        return ConsumeNumberResult.Success;
 1329                    }
 01330                    return ConsumeNumberResult.NeedMoreData;
 1331                }
 1332
 421333                _totalConsumed += i;
 421334                HasValueSequence = true;
 421335                i = 0;
 421336                data = _buffer;
 421337                nextByte = data[i];
 421338                if (JsonConstants.Delimiters.Contains(nextByte))
 01339                {
 01340                    return ConsumeNumberResult.Success;
 1341                }
 421342            }
 2101343            nextByte = data[i];
 2101344            if (nextByte is not ((byte)'.' or (byte)'E' or (byte)'e'))
 1261345            {
 1261346                RollBackState(rollBackState, isError: true);
 1261347                ThrowHelper.ThrowJsonReaderException(ref this,
 1261348                    JsonHelpers.IsInRangeInclusive(nextByte, '0', '9') ? ExceptionResource.InvalidLeadingZeroInNumber : 
 1261349                    nextByte);
 1350            }
 1351
 841352            return ConsumeNumberResult.OperationIncomplete;
 841353        }
 1354
 1355        private ConsumeNumberResult ConsumeIntegerDigitsMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i)
 5881356        {
 5881357            byte nextByte = default;
 5881358            int counter = 0;
 26041359            for (; i < data.Length; i++)
 12601360            {
 12601361                nextByte = data[i];
 12601362                if (!JsonHelpers.IsDigit(nextByte))
 2521363                {
 2521364                    break;
 1365                }
 10081366                counter++;
 10081367            }
 5881368            if (i >= data.Length)
 3361369            {
 3361370                if (IsLastSpan)
 01371                {
 1372                    // A payload containing a single value of integers (e.g. "12") is valid
 1373                    // If we are dealing with multi-value JSON,
 1374                    // ConsumeNumber will validate that we have a delimiter following the integer.
 01375                    _bytePositionInLine += counter;
 01376                    return ConsumeNumberResult.Success;
 1377                }
 1378
 4201379                while (true)
 4201380                {
 4201381                    if (!GetNextSpan())
 01382                    {
 01383                        if (IsLastSpan)
 01384                        {
 01385                            _bytePositionInLine += counter;
 01386                            return ConsumeNumberResult.Success;
 1387                        }
 01388                        return ConsumeNumberResult.NeedMoreData;
 1389                    }
 1390
 4201391                    _totalConsumed += i;
 4201392                    _bytePositionInLine += counter;
 4201393                    counter = 0;
 4201394                    HasValueSequence = true;
 4201395                    i = 0;
 4201396                    data = _buffer;
 30241397                    for (; i < data.Length; i++)
 16381398                    {
 16381399                        nextByte = data[i];
 16381400                        if (!JsonHelpers.IsDigit(nextByte))
 3361401                        {
 3361402                            break;
 1403                        }
 13021404                    }
 4201405                    _bytePositionInLine += i;
 4201406                    if (i >= data.Length)
 841407                    {
 841408                        if (IsLastSpan)
 01409                        {
 01410                            return ConsumeNumberResult.Success;
 1411                        }
 841412                    }
 1413                    else
 3361414                    {
 3361415                        break;
 1416                    }
 841417                }
 1418
 3361419            }
 1420            else
 2521421            {
 2521422                _bytePositionInLine += counter;
 2521423            }
 1424
 5881425            if (JsonConstants.Delimiters.Contains(nextByte))
 841426            {
 841427                return ConsumeNumberResult.Success;
 1428            }
 1429
 5041430            return ConsumeNumberResult.OperationIncomplete;
 5881431        }
 1432
 1433        private ConsumeNumberResult ConsumeDecimalDigitsMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scop
 01434        {
 01435            if (i >= data.Length)
 01436            {
 01437                if (IsLastSpan)
 01438                {
 01439                    RollBackState(rollBackState, isError: true);
 01440                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1441                }
 01442                if (!GetNextSpan())
 01443                {
 01444                    if (IsLastSpan)
 01445                    {
 01446                        RollBackState(rollBackState, isError: true);
 01447                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1448                    }
 01449                    return ConsumeNumberResult.NeedMoreData;
 1450                }
 01451                _totalConsumed += i;
 01452                HasValueSequence = true;
 01453                i = 0;
 01454                data = _buffer;
 01455            }
 01456            byte nextByte = data[i];
 01457            if (!JsonHelpers.IsDigit(nextByte))
 01458            {
 01459                RollBackState(rollBackState, isError: true);
 01460                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterDecimal, next
 1461            }
 01462            i++;
 01463            _bytePositionInLine++;
 01464            return ConsumeIntegerDigitsMultiSegment(ref data, ref i);
 01465        }
 1466
 1467        private ConsumeNumberResult ConsumeSignMultiSegment(ref ReadOnlySpan<byte> data, scoped ref int i, scoped in Par
 841468        {
 841469            if (i >= data.Length)
 421470            {
 421471                if (IsLastSpan)
 01472                {
 01473                    RollBackState(rollBackState, isError: true);
 01474                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData);
 1475                }
 1476
 421477                if (!GetNextSpan())
 01478                {
 01479                    if (IsLastSpan)
 01480                    {
 01481                        RollBackState(rollBackState, isError: true);
 01482                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1483                    }
 01484                    return ConsumeNumberResult.NeedMoreData;
 1485                }
 421486                _totalConsumed += i;
 421487                HasValueSequence = true;
 421488                i = 0;
 421489                data = _buffer;
 421490            }
 1491
 841492            byte nextByte = data[i];
 841493            if (nextByte is (byte)'+' or (byte)'-')
 01494            {
 01495                i++;
 01496                _bytePositionInLine++;
 01497                if (i >= data.Length)
 01498                {
 01499                    if (IsLastSpan)
 01500                    {
 01501                        RollBackState(rollBackState, isError: true);
 01502                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfData)
 1503                    }
 1504
 01505                    if (!GetNextSpan())
 01506                    {
 01507                        if (IsLastSpan)
 01508                        {
 01509                            RollBackState(rollBackState, isError: true);
 01510                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundEndOfD
 1511                        }
 01512                        return ConsumeNumberResult.NeedMoreData;
 1513                    }
 01514                    _totalConsumed += i;
 01515                    HasValueSequence = true;
 01516                    i = 0;
 01517                    data = _buffer;
 01518                }
 01519                nextByte = data[i];
 01520            }
 1521
 841522            if (!JsonHelpers.IsDigit(nextByte))
 841523            {
 841524                RollBackState(rollBackState, isError: true);
 841525                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.RequiredDigitNotFoundAfterSign, nextByt
 1526            }
 1527
 01528            return ConsumeNumberResult.OperationIncomplete;
 01529        }
 1530
 1531        private bool ConsumeNextTokenOrRollbackMultiSegment(byte marker)
 361532        {
 361533            long prevTotalConsumed = _totalConsumed;
 361534            int prevConsumed = _consumed;
 361535            long prevPosition = _bytePositionInLine;
 361536            long prevLineNumber = _lineNumber;
 361537            JsonTokenType prevTokenType = _tokenType;
 361538            SequencePosition prevSequencePosition = _currentPosition;
 361539            bool prevTrailingCommaBeforeComment = _trailingCommaBeforeComment;
 361540            ConsumeTokenResult result = ConsumeNextTokenMultiSegment(marker);
 01541            if (result == ConsumeTokenResult.Success)
 01542            {
 01543                return true;
 1544            }
 01545            if (result == ConsumeTokenResult.NotEnoughDataRollBackState)
 01546            {
 01547                _consumed = prevConsumed;
 01548                _tokenType = prevTokenType;
 01549                _bytePositionInLine = prevPosition;
 01550                _lineNumber = prevLineNumber;
 01551                _totalConsumed = prevTotalConsumed;
 01552                _currentPosition = prevSequencePosition;
 01553                _trailingCommaBeforeComment = prevTrailingCommaBeforeComment;
 01554            }
 01555            return false;
 01556        }
 1557
 1558        /// <summary>
 1559        /// This method consumes the next token regardless of whether we are inside an object or an array.
 1560        /// For an object, it reads the next property name token. For an array, it just reads the next value.
 1561        /// </summary>
 1562        private ConsumeTokenResult ConsumeNextTokenMultiSegment(byte marker)
 361563        {
 361564            if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
 01565            {
 01566                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
 01567                {
 01568                    if (marker == JsonConstants.Slash)
 01569                    {
 01570                        return SkipOrConsumeCommentMultiSegmentWithRollback() ? ConsumeTokenResult.Success : ConsumeToke
 1571                    }
 01572                    if (_tokenType == JsonTokenType.Comment)
 01573                    {
 01574                        return ConsumeNextTokenFromLastNonCommentTokenMultiSegment();
 1575                    }
 01576                }
 1577                else
 01578                {
 01579                    Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
 01580                    return ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiSegment(marker);
 1581                }
 01582            }
 1583
 361584            if (_bitStack.CurrentDepth == 0)
 361585            {
 361586                if (_readerOptions.AllowMultipleValues)
 01587                {
 01588                    return ReadFirstTokenMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoug
 1589                }
 1590
 361591                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 1592            }
 1593
 01594            if (marker == JsonConstants.ListSeparator)
 01595            {
 01596                _consumed++;
 01597                _bytePositionInLine++;
 1598
 01599                if (_consumed >= (uint)_buffer.Length)
 01600                {
 01601                    if (IsLastSpan)
 01602                    {
 01603                        _consumed--;
 01604                        _bytePositionInLine--;
 01605                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1606                    }
 01607                    if (!GetNextSpan())
 01608                    {
 01609                        if (IsLastSpan)
 01610                        {
 01611                            _consumed--;
 01612                            _bytePositionInLine--;
 01613                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrVa
 1614                        }
 01615                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1616                    }
 01617                }
 01618                byte first = _buffer[_consumed];
 1619
 1620                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01621                if (first <= JsonConstants.Space)
 01622                {
 01623                    SkipWhiteSpaceMultiSegment();
 1624                    // The next character must be a start of a property name or value.
 01625                    if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01626                    {
 01627                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 1628                    }
 01629                    first = _buffer[_consumed];
 01630                }
 1631
 01632                TokenStartIndex = BytesConsumed;
 1633
 01634                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow && first == JsonConstants.Slash)
 01635                {
 01636                    _trailingCommaBeforeComment = true;
 01637                    return SkipOrConsumeCommentMultiSegmentWithRollback() ? ConsumeTokenResult.Success : ConsumeTokenRes
 1638                }
 1639
 01640                if (_inObject)
 01641                {
 01642                    if (first != JsonConstants.Quote)
 01643                    {
 01644                        if (first == JsonConstants.CloseBrace)
 01645                        {
 01646                            if (_readerOptions.AllowTrailingCommas)
 01647                            {
 01648                                EndObject();
 01649                                return ConsumeTokenResult.Success;
 1650                            }
 01651                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1652                        }
 01653                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1654                    }
 01655                    return ConsumePropertyNameMultiSegment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnough
 1656                }
 1657                else
 01658                {
 01659                    if (first == JsonConstants.CloseBracket)
 01660                    {
 01661                        if (_readerOptions.AllowTrailingCommas)
 01662                        {
 01663                            EndArray();
 01664                            return ConsumeTokenResult.Success;
 1665                        }
 01666                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1667                    }
 01668                    return ConsumeValueMultiSegment(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDa
 1669                }
 1670            }
 01671            else if (marker == JsonConstants.CloseBrace)
 01672            {
 01673                EndObject();
 01674            }
 01675            else if (marker == JsonConstants.CloseBracket)
 01676            {
 01677                EndArray();
 01678            }
 1679            else
 01680            {
 01681                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 1682            }
 01683            return ConsumeTokenResult.Success;
 01684        }
 1685
 1686        private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentTokenMultiSegment()
 01687        {
 01688            Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Allow);
 01689            Debug.Assert(_tokenType == JsonTokenType.Comment);
 1690
 01691            if (JsonReaderHelper.IsTokenTypePrimitive(_previousTokenType))
 01692            {
 01693                _tokenType = _inObject ? JsonTokenType.StartObject : JsonTokenType.StartArray;
 01694            }
 1695            else
 01696            {
 01697                _tokenType = _previousTokenType;
 01698            }
 1699
 01700            Debug.Assert(_tokenType != JsonTokenType.Comment);
 1701
 01702            if (!HasMoreDataMultiSegment())
 01703            {
 01704                goto RollBack;
 1705            }
 1706
 01707            byte first = _buffer[_consumed];
 1708
 1709            // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01710            if (first <= JsonConstants.Space)
 01711            {
 01712                SkipWhiteSpaceMultiSegment();
 01713                if (!HasMoreDataMultiSegment())
 01714                {
 01715                    goto RollBack;
 1716                }
 01717                first = _buffer[_consumed];
 01718            }
 1719
 01720            if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None)
 01721            {
 01722                if (_readerOptions.AllowMultipleValues)
 01723                {
 01724                    return ReadFirstTokenMultiSegment(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnough
 1725                }
 1726
 01727                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
 1728            }
 1729
 01730            Debug.Assert(first != JsonConstants.Slash);
 1731
 01732            TokenStartIndex = BytesConsumed;
 1733
 01734            if (first == JsonConstants.ListSeparator)
 01735            {
 1736                // A comma without some JSON value preceding it is invalid
 01737                if (_previousTokenType <= JsonTokenType.StartObject || _previousTokenType == JsonTokenType.StartArray ||
 01738                {
 01739                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueAfter
 1740                }
 1741
 01742                _consumed++;
 01743                _bytePositionInLine++;
 1744
 01745                if (_consumed >= (uint)_buffer.Length)
 01746                {
 01747                    if (IsLastSpan)
 01748                    {
 01749                        _consumed--;
 01750                        _bytePositionInLine--;
 01751                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 1752                    }
 01753                    if (!GetNextSpan())
 01754                    {
 01755                        if (IsLastSpan)
 01756                        {
 01757                            _consumed--;
 01758                            _bytePositionInLine--;
 01759                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrVa
 1760                        }
 01761                        goto RollBack;
 1762                    }
 01763                }
 01764                first = _buffer[_consumed];
 1765
 1766                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01767                if (first <= JsonConstants.Space)
 01768                {
 01769                    SkipWhiteSpaceMultiSegment();
 1770                    // The next character must be a start of a property name or value.
 01771                    if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 01772                    {
 01773                        goto RollBack;
 1774                    }
 01775                    first = _buffer[_consumed];
 01776                }
 1777
 01778                TokenStartIndex = BytesConsumed;
 1779
 01780                if (first == JsonConstants.Slash)
 01781                {
 01782                    _trailingCommaBeforeComment = true;
 01783                    if (SkipOrConsumeCommentMultiSegmentWithRollback())
 01784                    {
 01785                        goto Done;
 1786                    }
 1787                    else
 01788                    {
 01789                        goto RollBack;
 1790                    }
 1791                }
 1792
 01793                if (_inObject)
 01794                {
 01795                    if (first != JsonConstants.Quote)
 01796                    {
 01797                        if (first == JsonConstants.CloseBrace)
 01798                        {
 01799                            if (_readerOptions.AllowTrailingCommas)
 01800                            {
 01801                                EndObject();
 01802                                goto Done;
 1803                            }
 01804                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 1805                        }
 1806
 01807                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1808                    }
 01809                    if (ConsumePropertyNameMultiSegment())
 01810                    {
 01811                        goto Done;
 1812                    }
 1813                    else
 01814                    {
 01815                        goto RollBack;
 1816                    }
 1817                }
 1818                else
 01819                {
 01820                    if (first == JsonConstants.CloseBracket)
 01821                    {
 01822                        if (_readerOptions.AllowTrailingCommas)
 01823                        {
 01824                            EndArray();
 01825                            goto Done;
 1826                        }
 01827                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 1828                    }
 1829
 01830                    if (ConsumeValueMultiSegment(first))
 01831                    {
 01832                        goto Done;
 1833                    }
 1834                    else
 01835                    {
 01836                        goto RollBack;
 1837                    }
 1838                }
 1839            }
 01840            else if (first == JsonConstants.CloseBrace)
 01841            {
 01842                EndObject();
 01843            }
 01844            else if (first == JsonConstants.CloseBracket)
 01845            {
 01846                EndArray();
 01847            }
 01848            else if (_tokenType == JsonTokenType.None)
 01849            {
 01850                if (ReadFirstTokenMultiSegment(first))
 01851                {
 01852                    goto Done;
 1853                }
 1854                else
 01855                {
 01856                    goto RollBack;
 1857                }
 1858            }
 01859            else if (_tokenType == JsonTokenType.StartObject)
 01860            {
 01861                Debug.Assert(first != JsonConstants.CloseBrace);
 01862                if (first != JsonConstants.Quote)
 01863                {
 01864                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound, fi
 1865                }
 1866
 01867                long prevTotalConsumed = _totalConsumed;
 01868                int prevConsumed = _consumed;
 01869                long prevPosition = _bytePositionInLine;
 01870                long prevLineNumber = _lineNumber;
 01871                if (!ConsumePropertyNameMultiSegment())
 01872                {
 1873                    // roll back potential changes
 01874                    _consumed = prevConsumed;
 01875                    _tokenType = JsonTokenType.StartObject;
 01876                    _bytePositionInLine = prevPosition;
 01877                    _lineNumber = prevLineNumber;
 01878                    _totalConsumed = prevTotalConsumed;
 01879                    goto RollBack;
 1880                }
 01881                goto Done;
 1882            }
 01883            else if (_tokenType == JsonTokenType.StartArray)
 01884            {
 01885                Debug.Assert(first != JsonConstants.CloseBracket);
 01886                if (!ConsumeValueMultiSegment(first))
 01887                {
 01888                    goto RollBack;
 1889                }
 01890                goto Done;
 1891            }
 01892            else if (_tokenType == JsonTokenType.PropertyName)
 01893            {
 01894                if (!ConsumeValueMultiSegment(first))
 01895                {
 01896                    goto RollBack;
 1897                }
 01898                goto Done;
 1899            }
 1900            else
 01901            {
 01902                Debug.Assert(_tokenType == JsonTokenType.EndArray || _tokenType == JsonTokenType.EndObject);
 01903                if (_inObject)
 01904                {
 01905                    Debug.Assert(first != JsonConstants.CloseBrace);
 01906                    if (first != JsonConstants.Quote)
 01907                    {
 01908                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 1909                    }
 1910
 01911                    if (ConsumePropertyNameMultiSegment())
 01912                    {
 01913                        goto Done;
 1914                    }
 1915                    else
 01916                    {
 01917                        goto RollBack;
 1918                    }
 1919                }
 1920                else
 01921                {
 01922                    Debug.Assert(first != JsonConstants.CloseBracket);
 1923
 01924                    if (ConsumeValueMultiSegment(first))
 01925                    {
 01926                        goto Done;
 1927                    }
 1928                    else
 01929                    {
 01930                        goto RollBack;
 1931                    }
 1932                }
 1933            }
 1934
 01935        Done:
 01936            return ConsumeTokenResult.Success;
 1937
 01938        RollBack:
 01939            return ConsumeTokenResult.NotEnoughDataRollBackState;
 01940        }
 1941
 1942        private bool SkipAllCommentsMultiSegment(scoped ref byte marker)
 01943        {
 01944            while (marker == JsonConstants.Slash)
 01945            {
 01946                if (SkipOrConsumeCommentMultiSegmentWithRollback())
 01947                {
 01948                    if (!HasMoreDataMultiSegment())
 01949                    {
 01950                        goto IncompleteNoRollback;
 1951                    }
 1952
 01953                    marker = _buffer[_consumed];
 1954
 1955                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01956                    if (marker <= JsonConstants.Space)
 01957                    {
 01958                        SkipWhiteSpaceMultiSegment();
 01959                        if (!HasMoreDataMultiSegment())
 01960                        {
 01961                            goto IncompleteNoRollback;
 1962                        }
 01963                        marker = _buffer[_consumed];
 01964                    }
 01965                }
 1966                else
 01967                {
 01968                    goto IncompleteNoRollback;
 1969                }
 01970            }
 01971            return true;
 1972
 01973        IncompleteNoRollback:
 01974            return false;
 01975        }
 1976
 1977        private bool SkipAllCommentsMultiSegment(scoped ref byte marker, ExceptionResource resource)
 01978        {
 01979            while (marker == JsonConstants.Slash)
 01980            {
 01981                if (SkipOrConsumeCommentMultiSegmentWithRollback())
 01982                {
 1983                    // The next character must be a start of a property name or value.
 01984                    if (!HasMoreDataMultiSegment(resource))
 01985                    {
 01986                        goto IncompleteRollback;
 1987                    }
 1988
 01989                    marker = _buffer[_consumed];
 1990
 1991                    // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 01992                    if (marker <= JsonConstants.Space)
 01993                    {
 01994                        SkipWhiteSpaceMultiSegment();
 1995                        // The next character must be a start of a property name or value.
 01996                        if (!HasMoreDataMultiSegment(resource))
 01997                        {
 01998                            goto IncompleteRollback;
 1999                        }
 02000                        marker = _buffer[_consumed];
 02001                    }
 02002                }
 2003                else
 02004                {
 02005                    goto IncompleteRollback;
 2006                }
 02007            }
 02008            return true;
 2009
 02010        IncompleteRollback:
 02011            return false;
 02012        }
 2013
 2014        private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiSegment(byte marker)
 02015        {
 02016            if (!SkipAllCommentsMultiSegment(ref marker))
 02017            {
 02018                goto IncompleteNoRollback;
 2019            }
 2020
 02021            TokenStartIndex = BytesConsumed;
 2022
 02023            if (_tokenType == JsonTokenType.StartObject)
 02024            {
 02025                if (marker == JsonConstants.CloseBrace)
 02026                {
 02027                    EndObject();
 02028                }
 2029                else
 02030                {
 02031                    if (marker != JsonConstants.Quote)
 02032                    {
 02033                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2034                    }
 2035
 02036                    long prevTotalConsumed = _totalConsumed;
 02037                    int prevConsumed = _consumed;
 02038                    long prevPosition = _bytePositionInLine;
 02039                    long prevLineNumber = _lineNumber;
 02040                    SequencePosition copy = _currentPosition;
 02041                    if (!ConsumePropertyNameMultiSegment())
 02042                    {
 2043                        // roll back potential changes
 02044                        _consumed = prevConsumed;
 02045                        _tokenType = JsonTokenType.StartObject;
 02046                        _bytePositionInLine = prevPosition;
 02047                        _lineNumber = prevLineNumber;
 02048                        _totalConsumed = prevTotalConsumed;
 02049                        _currentPosition = copy;
 02050                        goto IncompleteNoRollback;
 2051                    }
 02052                    goto Done;
 2053                }
 02054            }
 02055            else if (_tokenType == JsonTokenType.StartArray)
 02056            {
 02057                if (marker == JsonConstants.CloseBracket)
 02058                {
 02059                    EndArray();
 02060                }
 2061                else
 02062                {
 02063                    if (!ConsumeValueMultiSegment(marker))
 02064                    {
 02065                        goto IncompleteNoRollback;
 2066                    }
 02067                    goto Done;
 2068                }
 02069            }
 02070            else if (_tokenType == JsonTokenType.PropertyName)
 02071            {
 02072                if (!ConsumeValueMultiSegment(marker))
 02073                {
 02074                    goto IncompleteNoRollback;
 2075                }
 02076                goto Done;
 2077            }
 02078            else if (_bitStack.CurrentDepth == 0)
 02079            {
 02080                if (_readerOptions.AllowMultipleValues)
 02081                {
 02082                    return ReadFirstTokenMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoug
 2083                }
 2084
 02085                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
 2086            }
 02087            else if (marker == JsonConstants.ListSeparator)
 02088            {
 02089                _consumed++;
 02090                _bytePositionInLine++;
 2091
 02092                if (_consumed >= (uint)_buffer.Length)
 02093                {
 02094                    if (IsLastSpan)
 02095                    {
 02096                        _consumed--;
 02097                        _bytePositionInLine--;
 02098                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrValueN
 2099                    }
 02100                    if (!GetNextSpan())
 02101                    {
 02102                        if (IsLastSpan)
 02103                        {
 02104                            _consumed--;
 02105                            _bytePositionInLine--;
 02106                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyOrVa
 2107                        }
 02108                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2109                    }
 02110                }
 02111                marker = _buffer[_consumed];
 2112
 2113                // This check is done as an optimization to avoid calling SkipWhiteSpace when not necessary.
 02114                if (marker <= JsonConstants.Space)
 02115                {
 02116                    SkipWhiteSpaceMultiSegment();
 2117                    // The next character must be a start of a property name or value.
 02118                    if (!HasMoreDataMultiSegment(ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02119                    {
 02120                        return ConsumeTokenResult.NotEnoughDataRollBackState;
 2121                    }
 02122                    marker = _buffer[_consumed];
 02123                }
 2124
 02125                if (!SkipAllCommentsMultiSegment(ref marker, ExceptionResource.ExpectedStartOfPropertyOrValueNotFound))
 02126                {
 02127                    goto IncompleteRollback;
 2128                }
 2129
 02130                TokenStartIndex = BytesConsumed;
 2131
 02132                if (_inObject)
 02133                {
 02134                    if (marker != JsonConstants.Quote)
 02135                    {
 02136                        if (marker == JsonConstants.CloseBrace)
 02137                        {
 02138                            if (_readerOptions.AllowTrailingCommas)
 02139                            {
 02140                                EndObject();
 02141                                goto Done;
 2142                            }
 02143                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBefo
 2144                        }
 2145
 02146                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedStartOfPropertyNotFound
 2147                    }
 02148                    return ConsumePropertyNameMultiSegment() ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnough
 2149                }
 2150                else
 02151                {
 02152                    if (marker == JsonConstants.CloseBracket)
 02153                    {
 02154                        if (_readerOptions.AllowTrailingCommas)
 02155                        {
 02156                            EndArray();
 02157                            goto Done;
 2158                        }
 02159                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.TrailingCommaNotAllowedBeforeAr
 2160                    }
 02161                    return ConsumeValueMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughD
 2162                }
 2163            }
 02164            else if (marker == JsonConstants.CloseBrace)
 02165            {
 02166                EndObject();
 02167            }
 02168            else if (marker == JsonConstants.CloseBracket)
 02169            {
 02170                EndArray();
 02171            }
 2172            else
 02173            {
 02174                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.FoundInvalidCharacter, marker);
 2175            }
 2176
 02177        Done:
 02178            return ConsumeTokenResult.Success;
 02179        IncompleteNoRollback:
 02180            return ConsumeTokenResult.IncompleteNoRollBackNecessary;
 02181        IncompleteRollback:
 02182            return ConsumeTokenResult.NotEnoughDataRollBackState;
 02183        }
 2184
 2185        private bool SkipOrConsumeCommentMultiSegmentWithRollback()
 02186        {
 02187            long prevTotalConsumed = BytesConsumed;
 02188            SequencePosition start = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetInteger() + 
 02189            bool skipSucceeded = SkipCommentMultiSegment(out int tailBytesToIgnore);
 2190
 02191            if (skipSucceeded)
 02192            {
 02193                Debug.Assert(
 02194                    _readerOptions.CommentHandling == JsonCommentHandling.Allow ||
 02195                    _readerOptions.CommentHandling == JsonCommentHandling.Skip);
 2196
 02197                if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
 02198                {
 02199                    SequencePosition end = new SequencePosition(_currentPosition.GetObject(), _currentPosition.GetIntege
 2200
 02201                    ReadOnlySequence<byte> commentSequence = _sequence.Slice(start, end);
 02202                    commentSequence = commentSequence.Slice(2, commentSequence.Length - 2 - tailBytesToIgnore);
 02203                    HasValueSequence = !commentSequence.IsSingleSegment;
 2204
 02205                    if (HasValueSequence)
 02206                    {
 02207                        ValueSequence = commentSequence;
 02208                    }
 2209                    else
 02210                    {
 02211                        ValueSpan = commentSequence.First.Span;
 02212                    }
 2213
 02214                    if (_tokenType != JsonTokenType.Comment)
 02215                    {
 02216                        _previousTokenType = _tokenType;
 02217                    }
 2218
 02219                    _tokenType = JsonTokenType.Comment;
 02220                }
 02221            }
 2222            else
 02223            {
 02224                _totalConsumed = prevTotalConsumed;
 2225                // Note: BytesConsumed = _totalConsumed + _consumed
 2226                // Changing _consumed and _totalConsumed to original values might not work correctly
 2227                // since _consumed is tracking position in the current sequence
 2228                // and current sequence might not be the same as we could've called GetNextSpan.
 2229                // Since we return false we do not expect these APIs to be called again
 2230                // so the values are ok to be slightly incorrect as long as the sum remains the same
 2231                // if we don't reset this value to zero the BytesConsumed might be reported incorrectly.
 02232                _consumed = 0;
 02233            }
 2234
 02235            return skipSucceeded;
 02236        }
 2237
 2238        private bool SkipCommentMultiSegment(out int tailBytesToIgnore)
 822239        {
 822240            _consumed++;
 822241            _bytePositionInLine++;
 2242            // Create local copy to avoid bounds checks.
 822243            ReadOnlySpan<byte> localBuffer = _buffer.Slice(_consumed);
 2244
 822245            if (localBuffer.Length == 0)
 02246            {
 02247                if (IsLastSpan)
 02248                {
 02249                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReadingComm
 2250                }
 2251
 02252                if (!GetNextSpan())
 02253                {
 02254                    if (IsLastSpan)
 02255                    {
 02256                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReading
 2257                    }
 2258
 02259                    tailBytesToIgnore = 0;
 02260                    return false;
 2261                }
 2262
 02263                localBuffer = _buffer;
 02264            }
 2265
 822266            byte marker = localBuffer[0];
 822267            if (marker is not (JsonConstants.Slash or JsonConstants.Asterisk))
 822268            {
 822269                ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidCharacterAtStartOfComment, marke
 2270            }
 2271
 02272            bool multiLine = marker == JsonConstants.Asterisk;
 2273
 02274            _consumed++;
 02275            _bytePositionInLine++;
 02276            localBuffer = localBuffer.Slice(1);
 2277
 02278            if (localBuffer.Length == 0)
 02279            {
 02280                if (IsLastSpan)
 02281                {
 02282                    tailBytesToIgnore = 0;
 2283
 02284                    if (multiLine)
 02285                    {
 02286                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReading
 2287                    }
 2288
 02289                    return true;
 2290                }
 2291
 02292                if (!GetNextSpan())
 02293                {
 02294                    tailBytesToIgnore = 0;
 2295
 02296                    if (IsLastSpan)
 02297                    {
 02298                        if (multiLine)
 02299                        {
 02300                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileRea
 2301                        }
 2302
 02303                        return true;
 2304                    }
 2305
 02306                    return false;
 2307                }
 2308
 02309                localBuffer = _buffer;
 02310            }
 2311
 02312            if (multiLine)
 02313            {
 02314                tailBytesToIgnore = 2;
 02315                return SkipMultiLineCommentMultiSegment(localBuffer);
 2316            }
 2317            else
 02318            {
 02319                return SkipSingleLineCommentMultiSegment(localBuffer, out tailBytesToIgnore);
 2320            }
 02321        }
 2322
 2323        private bool SkipSingleLineCommentMultiSegment(ReadOnlySpan<byte> localBuffer, out int tailBytesToSkip)
 02324        {
 02325            bool expectLF = false;
 02326            int dangerousLineSeparatorBytesConsumed = 0;
 02327            tailBytesToSkip = 0;
 2328
 02329            while (true)
 02330            {
 02331                if (expectLF)
 02332                {
 02333                    if (localBuffer[0] == JsonConstants.LineFeed)
 02334                    {
 02335                        tailBytesToSkip++;
 02336                        _consumed++;
 02337                    }
 2338
 02339                    break;
 2340                }
 2341
 02342                int idx = FindLineSeparatorMultiSegment(localBuffer, ref dangerousLineSeparatorBytesConsumed);
 02343                Debug.Assert(dangerousLineSeparatorBytesConsumed is >= 0 and <= 2);
 2344
 02345                if (idx != -1)
 02346                {
 02347                    tailBytesToSkip++;
 02348                    _consumed += idx + 1;
 02349                    _bytePositionInLine += idx + 1;
 2350
 02351                    if (localBuffer[idx] == JsonConstants.LineFeed)
 02352                    {
 02353                        break;
 2354                    }
 2355
 2356                    // If we are here, we have definintely found a \r. So now to check if \n follows.
 02357                    Debug.Assert(localBuffer[idx] == JsonConstants.CarriageReturn);
 2358
 02359                    if (idx < localBuffer.Length - 1)
 02360                    {
 02361                        if (localBuffer[idx + 1] == JsonConstants.LineFeed)
 02362                        {
 02363                            tailBytesToSkip++;
 02364                            _consumed++;
 02365                            _bytePositionInLine++;
 02366                        }
 2367
 02368                        break;
 2369                    }
 2370
 02371                    expectLF = true;
 02372                }
 2373                else
 02374                {
 02375                    _consumed += localBuffer.Length;
 02376                    _bytePositionInLine += localBuffer.Length;
 02377                }
 2378
 02379                if (IsLastSpan)
 02380                {
 02381                    if (expectLF)
 02382                    {
 02383                        break;
 2384                    }
 2385
 02386                    return true;
 2387                }
 2388
 02389                if (!GetNextSpan())
 02390                {
 02391                    if (IsLastSpan)
 02392                    {
 02393                        if (expectLF)
 02394                        {
 02395                            break;
 2396                        }
 2397
 02398                        return true;
 2399                    }
 2400                    else
 02401                    {
 02402                        return false;
 2403                    }
 2404                }
 2405
 02406                localBuffer = _buffer;
 02407            }
 2408
 02409            _bytePositionInLine = 0;
 02410            _lineNumber++;
 2411
 02412            return true;
 02413        }
 2414
 2415        private int FindLineSeparatorMultiSegment(ReadOnlySpan<byte> localBuffer, scoped ref int dangerousLineSeparatorB
 02416        {
 02417            Debug.Assert(dangerousLineSeparatorBytesConsumed >= 0 && dangerousLineSeparatorBytesConsumed <= 2);
 2418
 02419            if (dangerousLineSeparatorBytesConsumed != 0)
 02420            {
 02421                ThrowOnDangerousLineSeparatorMultiSegment(localBuffer, ref dangerousLineSeparatorBytesConsumed);
 2422
 02423                if (dangerousLineSeparatorBytesConsumed != 0)
 02424                {
 2425                    // this can only happen if localBuffer size is 1 and we have previously only consumed 1 byte
 2426                    // or localBuffer is 0
 02427                    Debug.Assert(dangerousLineSeparatorBytesConsumed >= 1 && dangerousLineSeparatorBytesConsumed <= 2 &&
 02428                    return -1;
 2429                }
 02430            }
 2431
 02432            int totalIdx = 0;
 02433            while (true)
 02434            {
 02435                int idx = localBuffer.IndexOfAny(JsonConstants.LineFeed, JsonConstants.CarriageReturn, JsonConstants.Sta
 02436                dangerousLineSeparatorBytesConsumed = 0;
 2437
 02438                if (idx == -1)
 02439                {
 02440                    return -1;
 2441                }
 2442
 02443                if (localBuffer[idx] != JsonConstants.StartingByteOfNonStandardSeparator)
 02444                {
 02445                    return totalIdx + idx;
 2446                }
 2447
 02448                int p = idx + 1;
 02449                localBuffer = localBuffer.Slice(p);
 02450                totalIdx += p;
 2451
 02452                dangerousLineSeparatorBytesConsumed++;
 02453                ThrowOnDangerousLineSeparatorMultiSegment(localBuffer, ref dangerousLineSeparatorBytesConsumed);
 2454
 02455                if (dangerousLineSeparatorBytesConsumed != 0)
 02456                {
 2457                    // this can only happen in the end of the local buffer
 02458                    Debug.Assert(localBuffer.Length < 2);
 02459                    return -1;
 2460                }
 02461            }
 02462        }
 2463
 2464        // assumes first byte (JsonConstants.UnexpectedEndOfLineSeparator) is already read
 2465        private void ThrowOnDangerousLineSeparatorMultiSegment(ReadOnlySpan<byte> localBuffer, scoped ref int dangerousL
 02466        {
 02467            Debug.Assert(dangerousLineSeparatorBytesConsumed == 1 || dangerousLineSeparatorBytesConsumed == 2);
 2468
 2469            // \u2028 and \u2029 are considered respectively line and paragraph separators
 2470            // UTF-8 representation for them is E2, 80, A8/A9
 2471            // we have already read E2 and maybe 80 we need to check for remaining 1 or 2 bytes
 2472
 02473            if (localBuffer.IsEmpty)
 02474            {
 02475                return;
 2476            }
 2477
 02478            if (dangerousLineSeparatorBytesConsumed == 1)
 02479            {
 02480                if (localBuffer[0] == 0x80)
 02481                {
 02482                    localBuffer = localBuffer.Slice(1);
 02483                    dangerousLineSeparatorBytesConsumed++;
 2484
 02485                    if (localBuffer.IsEmpty)
 02486                    {
 02487                        return;
 2488                    }
 02489                }
 2490                else
 02491                {
 2492                    // no match
 02493                    dangerousLineSeparatorBytesConsumed = 0;
 02494                    return;
 2495                }
 02496            }
 2497
 02498            if (dangerousLineSeparatorBytesConsumed == 2)
 02499            {
 02500                byte lastByte = localBuffer[0];
 02501                if (lastByte is 0xA8 or 0xA9)
 02502                {
 02503                    ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfLineSeparator);
 2504                }
 2505                else
 02506                {
 2507                    // no match
 02508                    dangerousLineSeparatorBytesConsumed = 0;
 02509                    return;
 2510                }
 2511            }
 02512        }
 2513
 2514        private bool SkipMultiLineCommentMultiSegment(ReadOnlySpan<byte> localBuffer)
 02515        {
 02516            bool expectSlash = false;
 02517            bool ignoreNextLfForLineTracking = false;
 2518
 02519            while (true)
 02520            {
 02521                Debug.Assert(localBuffer.Length > 0);
 2522
 02523                if (expectSlash)
 02524                {
 02525                    if (localBuffer[0] == JsonConstants.Slash)
 02526                    {
 02527                        _consumed++;
 02528                        _bytePositionInLine++;
 02529                        return true;
 2530                    }
 2531
 02532                    expectSlash = false;
 02533                }
 2534
 02535                if (ignoreNextLfForLineTracking)
 02536                {
 02537                    if (localBuffer[0] == JsonConstants.LineFeed)
 02538                    {
 02539                        _consumed++;
 02540                        localBuffer = localBuffer.Slice(1);
 02541                    }
 2542
 02543                    ignoreNextLfForLineTracking = false;
 02544                }
 2545
 02546                int idx = localBuffer.IndexOfAny(JsonConstants.Asterisk, JsonConstants.LineFeed, JsonConstants.CarriageR
 2547
 02548                if (idx != -1)
 02549                {
 02550                    int nextIdx = idx + 1;
 02551                    byte marker = localBuffer[idx];
 02552                    localBuffer = localBuffer.Slice(nextIdx);
 2553
 02554                    _consumed += nextIdx;
 2555
 02556                    switch (marker)
 2557                    {
 2558                        case JsonConstants.Asterisk:
 02559                            expectSlash = true;
 02560                            _bytePositionInLine += nextIdx;
 02561                            break;
 2562                        case JsonConstants.LineFeed:
 02563                            _bytePositionInLine = 0;
 02564                            _lineNumber++;
 02565                            break;
 2566                        default:
 02567                            Debug.Assert(marker == JsonConstants.CarriageReturn);
 02568                            _bytePositionInLine = 0;
 02569                            _lineNumber++;
 02570                            ignoreNextLfForLineTracking = true;
 02571                            break;
 2572                    }
 02573                }
 2574                else
 02575                {
 02576                    _consumed += localBuffer.Length;
 02577                    _bytePositionInLine += localBuffer.Length;
 02578                    localBuffer = ReadOnlySpan<byte>.Empty;
 02579                }
 2580
 02581                if (localBuffer.IsEmpty)
 02582                {
 02583                    if (IsLastSpan)
 02584                    {
 02585                        ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileReading
 2586                    }
 2587
 02588                    if (!GetNextSpan())
 02589                    {
 02590                        if (IsLastSpan)
 02591                        {
 02592                            ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.UnexpectedEndOfDataWhileRea
 2593                        }
 2594                        else
 02595                        {
 02596                            return false;
 2597                        }
 2598                    }
 2599
 02600                    localBuffer = _buffer;
 02601                    Debug.Assert(!localBuffer.IsEmpty);
 02602                }
 02603            }
 02604        }
 2605
 2606        private PartialStateForRollback CaptureState() =>
 10502607            new PartialStateForRollback(_totalConsumed, _bytePositionInLine, _consumed, _currentPosition);
 2608
 2609        private readonly struct PartialStateForRollback
 2610        {
 2611            public readonly long _prevTotalConsumed;
 2612            public readonly long _prevBytePositionInLine;
 2613            public readonly int _prevConsumed;
 2614            public readonly SequencePosition _prevCurrentPosition;
 2615
 2616            public PartialStateForRollback(long totalConsumed, long bytePositionInLine, int consumed, SequencePosition c
 10502617            {
 10502618                _prevTotalConsumed = totalConsumed;
 10502619                _prevBytePositionInLine = bytePositionInLine;
 10502620                _prevConsumed = consumed;
 10502621                _prevCurrentPosition = currentPosition;
 10502622            }
 2623
 2624            public SequencePosition GetStartPosition(int offset = 0)
 842625            {
 842626                return new SequencePosition(_prevCurrentPosition.GetObject(), _prevCurrentPosition.GetInteger() + _prevC
 842627            }
 2628        }
 2629    }
 2630}
 2631

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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()
 2428        {
 2429            if (TokenType == JsonTokenType.Null)
 030            {
 031                return null;
 32            }
 33
 2434            if (TokenType != JsonTokenType.String && TokenType != JsonTokenType.PropertyName)
 2435            {
 2436                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)
 1280        {
 1281            Debug.Assert(_tokenType is JsonTokenType.String or JsonTokenType.PropertyName or JsonTokenType.Number);
 1282            Debug.Assert(_tokenType != JsonTokenType.Number || !ValueIsEscaped, "Numbers can't contain escape characters
 83
 84            int bytesWritten;
 85
 1286            if (ValueIsEscaped)
 087            {
 088                if (!TryCopyEscapedString(utf8Destination, out bytesWritten))
 089                {
 090                    utf8Destination.Slice(0, bytesWritten).Clear();
 091                    ThrowHelper.ThrowArgumentException_DestinationTooShort();
 92                }
 093            }
 94            else
 1295            {
 1296                if (HasValueSequence)
 697                {
 698                    ReadOnlySequence<byte> valueSequence = ValueSequence;
 699                    valueSequence.CopyTo(utf8Destination);
 6100                    bytesWritten = (int)valueSequence.Length;
 6101                }
 102                else
 6103                {
 6104                    ReadOnlySpan<byte> valueSpan = ValueSpan;
 6105                    valueSpan.CopyTo(utf8Destination);
 6106                    bytesWritten = valueSpan.Length;
 6107                }
 12108            }
 109
 12110            JsonReaderHelper.ValidateUtf8(utf8Destination.Slice(0, bytesWritten));
 12111            return bytesWritten;
 12112        }
 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()
 14258        {
 14259            JsonTokenType type = TokenType;
 14260            if (type == JsonTokenType.True)
 0261            {
 0262                Debug.Assert((HasValueSequence ? ValueSequence.ToArray() : ValueSpan).Length == 4);
 0263                return true;
 264            }
 14265            else if (type != JsonTokenType.False)
 14266            {
 14267                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()
 36287        {
 36288            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()
 12311        {
 12312            if (!TryGetByte(out byte value))
 4313            {
 4314                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()
 12346        {
 12347            if (!TryGetSByte(out sbyte value))
 4348            {
 4349                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()
 12380        {
 12381            if (!TryGetInt16(out short value))
 4382            {
 4383                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()
 16414        {
 16415            if (!TryGetInt32(out int value))
 0416            {
 0417                ThrowHelper.ThrowFormatException(NumericType.Int32);
 418            }
 8419            return value;
 8420        }
 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()
 12448        {
 12449            if (!TryGetInt64(out long value))
 0450            {
 0451                ThrowHelper.ThrowFormatException(NumericType.Int64);
 452            }
 4453            return value;
 4454        }
 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()
 12483        {
 12484            if (!TryGetUInt16(out ushort value))
 4485            {
 4486                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()
 12518        {
 12519            if (!TryGetUInt32(out uint value))
 0520            {
 0521                ThrowHelper.ThrowFormatException(NumericType.UInt32);
 522            }
 4523            return value;
 4524        }
 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()
 12553        {
 12554            if (!TryGetUInt64(out ulong value))
 0555            {
 0556                ThrowHelper.ThrowFormatException(NumericType.UInt64);
 557            }
 4558            return value;
 4559        }
 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()
 12586        {
 12587            if (!TryGetSingle(out float value))
 0588            {
 0589                ThrowHelper.ThrowFormatException(NumericType.Single);
 590            }
 4591            return value;
 4592        }
 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()
 12643        {
 12644            if (!TryGetDouble(out double value))
 0645            {
 0646                ThrowHelper.ThrowFormatException(NumericType.Double);
 647            }
 4648            return value;
 4649        }
 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()
 12700        {
 12701            if (!TryGetDecimal(out decimal value))
 0702            {
 0703                ThrowHelper.ThrowFormatException(NumericType.Decimal);
 704            }
 4705            return value;
 4706        }
 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()
 16732        {
 16733            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()
 12765        {
 12766            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()
 12798        {
 12799            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)
 36828        {
 36829            if (TokenType != JsonTokenType.String)
 36830            {
 36831                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)
 16856        {
 16857            if (TokenType != JsonTokenType.Number)
 8858            {
 8859                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 860            }
 861
 8862            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 8863            return TryGetByteCore(out value, span);
 8864        }
 865
 866        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 867        internal static bool TryGetByteCore(out byte value, ReadOnlySpan<byte> span)
 8868        {
 8869            if (Utf8Parser.TryParse(span, out byte tmp, out int bytesConsumed)
 8870                && span.Length == bytesConsumed)
 0871            {
 0872                value = tmp;
 0873                return true;
 874            }
 875
 8876            value = 0;
 8877            return false;
 8878        }
 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)
 16892        {
 16893            if (TokenType != JsonTokenType.Number)
 8894            {
 8895                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 896            }
 897
 8898            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 8899            return TryGetSByteCore(out value, span);
 8900        }
 901
 902        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 903        internal static bool TryGetSByteCore(out sbyte value, ReadOnlySpan<byte> span)
 8904        {
 8905            if (Utf8Parser.TryParse(span, out sbyte tmp, out int bytesConsumed)
 8906                && span.Length == bytesConsumed)
 0907            {
 0908                value = tmp;
 0909                return true;
 910            }
 911
 8912            value = 0;
 8913            return false;
 8914        }
 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)
 16927        {
 16928            if (TokenType != JsonTokenType.Number)
 8929            {
 8930                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 931            }
 932
 8933            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 8934            return TryGetInt16Core(out value, span);
 8935        }
 936
 937        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 938        internal static bool TryGetInt16Core(out short value, ReadOnlySpan<byte> span)
 8939        {
 8940            if (Utf8Parser.TryParse(span, out short tmp, out int bytesConsumed)
 8941                && span.Length == bytesConsumed)
 0942            {
 0943                value = tmp;
 0944                return true;
 945            }
 946
 8947            value = 0;
 8948            return false;
 8949        }
 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)
 28962        {
 28963            if (TokenType != JsonTokenType.Number)
 8964            {
 8965                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 966            }
 967
 20968            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 20969            return TryGetInt32Core(out value, span);
 20970        }
 971
 972        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 973        internal static bool TryGetInt32Core(out int value, ReadOnlySpan<byte> span)
 20974        {
 20975            if (Utf8Parser.TryParse(span, out int tmp, out int bytesConsumed)
 20976                && span.Length == bytesConsumed)
 20977            {
 20978                value = tmp;
 20979                return true;
 980            }
 981
 0982            value = 0;
 0983            return false;
 20984        }
 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)
 16997        {
 16998            if (TokenType != JsonTokenType.Number)
 8999            {
 81000                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1001            }
 1002
 81003            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 81004            return TryGetInt64Core(out value, span);
 81005        }
 1006
 1007        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1008        internal static bool TryGetInt64Core(out long value, ReadOnlySpan<byte> span)
 81009        {
 81010            if (Utf8Parser.TryParse(span, out long tmp, out int bytesConsumed)
 81011                && span.Length == bytesConsumed)
 81012            {
 81013                value = tmp;
 81014                return true;
 1015            }
 1016
 01017            value = 0;
 01018            return false;
 81019        }
 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)
 121033        {
 121034            if (TokenType != JsonTokenType.Number)
 81035            {
 81036                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1037            }
 1038
 41039            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 41040            return TryGetUInt16Core(out value, span);
 41041        }
 1042
 1043        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1044        internal static bool TryGetUInt16Core(out ushort value, ReadOnlySpan<byte> span)
 41045        {
 41046            if (Utf8Parser.TryParse(span, out ushort tmp, out int bytesConsumed)
 41047                && span.Length == bytesConsumed)
 01048            {
 01049                value = tmp;
 01050                return true;
 1051            }
 1052
 41053            value = 0;
 41054            return false;
 41055        }
 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)
 121069        {
 121070            if (TokenType != JsonTokenType.Number)
 81071            {
 81072                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1073            }
 1074
 41075            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 41076            return TryGetUInt32Core(out value, span);
 41077        }
 1078
 1079        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1080        internal static bool TryGetUInt32Core(out uint value, ReadOnlySpan<byte> span)
 41081        {
 41082            if (Utf8Parser.TryParse(span, out uint tmp, out int bytesConsumed)
 41083                && span.Length == bytesConsumed)
 41084            {
 41085                value = tmp;
 41086                return true;
 1087            }
 1088
 01089            value = 0;
 01090            return false;
 41091        }
 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)
 121105        {
 121106            if (TokenType != JsonTokenType.Number)
 81107            {
 81108                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1109            }
 1110
 41111            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 41112            return TryGetUInt64Core(out value, span);
 41113        }
 1114
 1115        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1116        internal static bool TryGetUInt64Core(out ulong value, ReadOnlySpan<byte> span)
 41117        {
 41118            if (Utf8Parser.TryParse(span, out ulong tmp, out int bytesConsumed)
 41119                && span.Length == bytesConsumed)
 41120            {
 41121                value = tmp;
 41122                return true;
 1123            }
 1124
 01125            value = 0;
 01126            return false;
 41127        }
 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)
 161140        {
 161141            if (TokenType != JsonTokenType.Number)
 81142            {
 81143                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1144            }
 1145
 81146            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 1147
 81148            if (Utf8Parser.TryParse(span, out float tmp, out int bytesConsumed)
 81149                && span.Length == bytesConsumed)
 81150            {
 81151                value = tmp;
 81152                return true;
 1153            }
 1154
 01155            value = 0;
 01156            return false;
 81157        }
 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)
 161170        {
 161171            if (TokenType != JsonTokenType.Number)
 81172            {
 81173                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1174            }
 1175
 81176            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 1177
 81178            if (Utf8Parser.TryParse(span, out double tmp, out int bytesConsumed)
 81179                && span.Length == bytesConsumed)
 81180            {
 81181                value = tmp;
 81182                return true;
 1183            }
 1184
 01185            value = 0;
 01186            return false;
 81187        }
 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)
 161200        {
 161201            if (TokenType != JsonTokenType.Number)
 81202            {
 81203                ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(TokenType);
 1204            }
 1205
 81206            ReadOnlySpan<byte> span = HasValueSequence ? ValueSequence.ToArray() : ValueSpan;
 81207            return TryGetDecimalCore(out value, span);
 81208        }
 1209
 1210        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1211        internal static bool TryGetDecimalCore(out decimal value, ReadOnlySpan<byte> span)
 81212        {
 81213            if (Utf8Parser.TryParse(span, out decimal tmp, out int bytesConsumed)
 81214                && span.Length == bytesConsumed)
 81215            {
 81216                value = tmp;
 81217                return true;
 1218            }
 1219
 01220            value = 0;
 01221            return false;
 81222        }
 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)
 161235        {
 161236            if (TokenType != JsonTokenType.String)
 161237            {
 161238                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)
 121280        {
 121281            if (TokenType != JsonTokenType.String)
 121282            {
 121283                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)
 121326        {
 121327            if (TokenType != JsonTokenType.String)
 121328            {
 121329                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}
 1361

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