< Summary

Line coverage
27%
Covered lines: 454
Uncovered lines: 1168
Coverable lines: 1622
Total lines: 3077
Line coverage: 27.9%
Branch coverage
25%
Covered branches: 110
Total branches: 440
Branch coverage: 25%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .ctor(...)83.33%66100%
File 1: Dispose()50%121268.18%
File 1: WriteTo(...)100%110%
File 1: GetJsonTokenType(...)100%11100%
File 1: ValueIsEscaped(...)50%44100%
File 1: GetArrayLength(...)100%110%
File 1: GetPropertyCount(...)100%110%
File 1: GetArrayIndexElement(...)0%10100%
File 1: GetEndIndex(...)0%440%
File 1: GetRootRawValue()100%110%
File 1: GetRawValue(...)66.66%6657.14%
File 1: GetPropertyRawValue(...)0%440%
File 1: GetString(...)0%440%
File 1: TextEquals(...)0%660%
File 1: TextEquals(...)37.5%161652%
File 1: GetNameOfPropertyValue(...)100%110%
File 1: GetPropertyNameRaw(...)100%110%
File 1: TryGetValue(...)0%220%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)0%440%
File 1: TryGetValue(...)100%110%
File 1: TryGetValue(...)100%110%
File 1: TryGetValue(...)100%110%
File 1: GetRawValueAsString(...)100%110%
File 1: GetPropertyRawValueAsString(...)100%110%
File 1: CloneElement(...)100%110%
File 1: WriteElementTo(...)0%12120%
File 1: WriteComplexElement(...)0%14140%
File 1: UnescapeString(...)0%440%
File 1: ClearAndReturn(...)0%220%
File 1: WritePropertyName(...)100%110%
File 1: WritePropertyName(...)100%110%
File 1: WriteString(...)100%110%
File 1: Parse(...)57.14%282855.85%
File 1: ValidateNoDuplicateProperties(...)0%440%
File 1: ValidateDuplicatePropertiesCore(...)0%22220%
File 1: CheckNotDisposed()50%2260%
File 1: CheckExpectedType(...)50%2260%
File 1: CheckSupportedOptions(...)50%4466.66%
File 2: .ctor(...)50%22100%
File 3: .ctor(...)100%11100%
File 3: .ctor(...)100%110%
File 3: CreateRented(...)50%4466.66%
File 3: CreateLocked(...)100%11100%
File 3: Dispose()50%2277.77%
File 3: CompleteAllocations()100%88100%
File 3: Append(...)100%44100%
File 3: Enlarge()50%44100%
File 3: AssertValidIndex(...)50%44100%
File 3: SetLength(...)100%11100%
File 3: SetNumberOfRows(...)50%22100%
File 3: SetHasComplexChildren(...)100%110%
File 3: FindIndexOfFirstUnsetSizeOrLength(...)50%22100%
File 3: FindOpenElement(...)50%6681.81%
File 3: Get(...)100%11100%
File 3: GetJsonTokenType(...)100%11100%
File 3: CopySegment(...)0%16160%
File 4: Parse(...)100%110%
File 4: Parse(...)0%220%
File 4: Parse(...)100%110%
File 4: ParseRented(...)100%110%
File 4: ParseValue(...)100%110%
File 4: ParseValue(...)100%110%
File 4: ParseValue(...)100%110%
File 4: ParseAsync(...)100%110%
File 4: ParseAsyncCore()100%110%
File 4: ParseAsyncCoreUnrented()100%110%
File 4: Parse(...)100%110%
File 4: ParseValue(...)100%110%
File 4: Parse(...)100%110%
File 4: TryParseValue(...)100%110%
File 4: ParseValue(...)100%11100%
File 4: ParseValue(...)100%11100%
File 4: TryParseValue(...)55.35%565669.07%
File 4: CreateForLiteral(...)0%10100%
File 4: Parse(...)50%2266.66%
File 4: ParseUnrented(...)70%101089.65%
File 4: ReadToEnd(...)0%16160%
File 4: ReadToEndAsync()0%16160%
File 5: .ctor()100%110%
File 5: SetCapacity(...)0%440%
File 5: AddPropertyName(...)0%10100%
File 5: SwitchToHashSet(...)0%880%
File 5: Reset()0%220%
File 5: Dispose()100%110%
File 5: .cctor()100%110%
File 5: Equals(...)0%220%
File 5: GetHashCode(...)100%110%
File 6: .ctor(...)100%11100%
File 7: .ctor(...)100%11100%
File 7: Dispose()100%22100%
File 7: Push(...)50%2262.5%
File 7: Pop()100%11100%
File 7: Enlarge()100%110%
File 8: TryGetNamedPropertyValue(...)0%10100%
File 8: TryGetNamedPropertyValue(...)0%220%
File 8: TryGetNamedPropertyValue(...)0%20200%

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.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.Collections.Generic;
 7using System.Diagnostics;
 8using System.Diagnostics.CodeAnalysis;
 9using System.Threading;
 10
 11namespace System.Text.Json
 12{
 13    /// <summary>
 14    ///   Represents the structure of a JSON value in a lightweight, read-only form.
 15    /// </summary>
 16    /// <remarks>
 17    ///   This class utilizes resources from pooled memory to minimize the garbage collector (GC)
 18    ///   impact in high-usage scenarios. Failure to properly Dispose this object will result in
 19    ///   the memory not being returned to the pool, which will cause an increase in GC impact across
 20    ///   various parts of the framework.
 21    /// </remarks>
 22    public sealed partial class JsonDocument : IDisposable
 23    {
 24        private ReadOnlyMemory<byte> _utf8Json;
 25        private MetadataDb _parsedData;
 26
 27        private byte[]? _extraRentedArrayPoolBytes;
 28        private PooledByteBufferWriter? _extraPooledByteBufferWriter;
 29
 2830        internal bool IsDisposable { get; }
 31
 32        /// <summary>
 33        ///   The <see cref="JsonElement"/> representing the value of the document.
 34        /// </summary>
 96635        public JsonElement RootElement => new JsonElement(this, 0);
 36
 140037        private JsonDocument(
 140038            ReadOnlyMemory<byte> utf8Json,
 140039            MetadataDb parsedData,
 140040            byte[]? extraRentedArrayPoolBytes = null,
 140041            PooledByteBufferWriter? extraPooledByteBufferWriter = null,
 140042            bool isDisposable = true)
 140043        {
 140044            Debug.Assert(!utf8Json.IsEmpty);
 45
 46            // Both rented values better be null if we're not disposable.
 140047            Debug.Assert(isDisposable ||
 140048                (extraRentedArrayPoolBytes == null && extraPooledByteBufferWriter == null));
 49
 50            // Both rented values can't be specified.
 140051            Debug.Assert(extraRentedArrayPoolBytes == null || extraPooledByteBufferWriter == null);
 52
 140053            _utf8Json = utf8Json;
 140054            _parsedData = parsedData;
 140055            _extraRentedArrayPoolBytes = extraRentedArrayPoolBytes;
 140056            _extraPooledByteBufferWriter = extraPooledByteBufferWriter;
 140057            IsDisposable = isDisposable;
 140058        }
 59
 60        /// <inheritdoc />
 61        public void Dispose()
 2862        {
 2863            int length = _utf8Json.Length;
 2864            if (length == 0 || !IsDisposable)
 065            {
 066                return;
 67            }
 68
 2869            _parsedData.Dispose();
 2870            _utf8Json = ReadOnlyMemory<byte>.Empty;
 71
 2872            if (_extraRentedArrayPoolBytes != null)
 2873            {
 2874                byte[]? extraRentedBytes = Interlocked.Exchange<byte[]?>(ref _extraRentedArrayPoolBytes, null);
 75
 2876                if (extraRentedBytes != null)
 2877                {
 78                    // When "extra rented bytes exist" it contains the document,
 79                    // and thus needs to be cleared before being returned.
 2880                    extraRentedBytes.AsSpan(0, length).Clear();
 2881                    ArrayPool<byte>.Shared.Return(extraRentedBytes);
 2882                }
 2883            }
 084            else if (_extraPooledByteBufferWriter != null)
 085            {
 086                PooledByteBufferWriter? extraBufferWriter = Interlocked.Exchange<PooledByteBufferWriter?>(ref _extraPool
 087                extraBufferWriter?.Dispose();
 088            }
 2889        }
 90
 91        /// <summary>
 92        ///  Write the document into the provided writer as a JSON value.
 93        /// </summary>
 94        /// <param name="writer"></param>
 95        /// <exception cref="ArgumentNullException">
 96        ///   The <paramref name="writer"/> parameter is <see langword="null"/>.
 97        /// </exception>
 98        /// <exception cref="InvalidOperationException">
 99        ///   This <see cref="RootElement"/>'s <see cref="JsonElement.ValueKind"/> would result in an invalid JSON.
 100        /// </exception>
 101        /// <exception cref="ObjectDisposedException">
 102        ///   The parent <see cref="JsonDocument"/> has been disposed.
 103        /// </exception>
 104        public void WriteTo(Utf8JsonWriter writer)
 0105        {
 0106            ArgumentNullException.ThrowIfNull(writer);
 107
 0108            RootElement.WriteTo(writer);
 0109        }
 110
 111        internal JsonTokenType GetJsonTokenType(int index)
 145112        {
 145113            CheckNotDisposed();
 114
 145115            return _parsedData.GetJsonTokenType(index);
 145116        }
 117
 118        internal bool ValueIsEscaped(int index, bool isPropertyName)
 3119        {
 3120            CheckNotDisposed();
 121
 3122            int matchIndex = isPropertyName ? index - DbRow.Size : index;
 3123            DbRow row = _parsedData.Get(matchIndex);
 3124            Debug.Assert(!isPropertyName || row.TokenType is JsonTokenType.PropertyName);
 125
 3126            return row.HasComplexChildren;
 3127        }
 128
 129        internal int GetArrayLength(int index)
 0130        {
 0131            CheckNotDisposed();
 132
 0133            DbRow row = _parsedData.Get(index);
 134
 0135            CheckExpectedType(JsonTokenType.StartArray, row.TokenType);
 136
 0137            return row.SizeOrLength;
 0138        }
 139
 140        internal int GetPropertyCount(int index)
 0141        {
 0142            CheckNotDisposed();
 143
 0144            DbRow row = _parsedData.Get(index);
 145
 0146            CheckExpectedType(JsonTokenType.StartObject, row.TokenType);
 147
 0148            return row.SizeOrLength;
 0149        }
 150
 151        internal JsonElement GetArrayIndexElement(int currentIndex, int arrayIndex)
 0152        {
 0153            CheckNotDisposed();
 154
 0155            DbRow row = _parsedData.Get(currentIndex);
 156
 0157            CheckExpectedType(JsonTokenType.StartArray, row.TokenType);
 158
 0159            int arrayLength = row.SizeOrLength;
 160
 0161            if ((uint)arrayIndex >= (uint)arrayLength)
 0162            {
 0163                throw new IndexOutOfRangeException();
 164            }
 165
 0166            if (!row.HasComplexChildren)
 0167            {
 168                // Since we wouldn't be here without having completed the document parse, and we
 169                // already vetted the index against the length, this new index will always be
 170                // within the table.
 0171                return new JsonElement(this, currentIndex + ((arrayIndex + 1) * DbRow.Size));
 172            }
 173
 0174            int elementCount = 0;
 0175            int objectOffset = currentIndex + DbRow.Size;
 176
 0177            for (; objectOffset < _parsedData.Length; objectOffset += DbRow.Size)
 0178            {
 0179                if (arrayIndex == elementCount)
 0180                {
 0181                    return new JsonElement(this, objectOffset);
 182                }
 183
 0184                row = _parsedData.Get(objectOffset);
 185
 0186                if (!row.IsSimpleValue)
 0187                {
 0188                    objectOffset += DbRow.Size * row.NumberOfRows;
 0189                }
 190
 0191                elementCount++;
 0192            }
 193
 0194            Debug.Fail(
 0195                $"Ran out of database searching for array index {arrayIndex} from {currentIndex} when length was {arrayL
 196            throw new IndexOutOfRangeException();
 0197        }
 198
 199        internal int GetEndIndex(int index, bool includeEndElement)
 0200        {
 0201            CheckNotDisposed();
 202
 0203            DbRow row = _parsedData.Get(index);
 204
 0205            if (row.IsSimpleValue)
 0206            {
 0207                return index + DbRow.Size;
 208            }
 209
 0210            int endIndex = index + DbRow.Size * row.NumberOfRows;
 211
 0212            if (includeEndElement)
 0213            {
 0214                endIndex += DbRow.Size;
 0215            }
 216
 0217            return endIndex;
 0218        }
 219
 220        internal ReadOnlyMemory<byte> GetRootRawValue()
 0221        {
 0222            return GetRawValue(0, includeQuotes: true);
 0223        }
 224
 225        internal ReadOnlyMemory<byte> GetRawValue(int index, bool includeQuotes)
 73226        {
 73227            CheckNotDisposed();
 228
 73229            DbRow row = _parsedData.Get(index);
 230
 73231            if (row.IsSimpleValue)
 73232            {
 73233                if (includeQuotes && row.TokenType == JsonTokenType.String)
 0234                {
 235                    // Start one character earlier than the value (the open quote)
 236                    // End one character after the value (the close quote)
 0237                    return _utf8Json.Slice(row.Location - 1, row.SizeOrLength + 2);
 238                }
 239
 73240                return _utf8Json.Slice(row.Location, row.SizeOrLength);
 241            }
 242
 0243            int endElementIdx = GetEndIndex(index, includeEndElement: false);
 0244            int start = row.Location;
 0245            row = _parsedData.Get(endElementIdx);
 0246            return _utf8Json.Slice(start, row.Location - start + row.SizeOrLength);
 73247        }
 248
 249        private ReadOnlyMemory<byte> GetPropertyRawValue(int valueIndex)
 0250        {
 0251            CheckNotDisposed();
 252
 253            // The property name is stored one row before the value
 0254            DbRow row = _parsedData.Get(valueIndex - DbRow.Size);
 0255            Debug.Assert(row.TokenType == JsonTokenType.PropertyName);
 256
 257            // Subtract one for the open quote.
 0258            int start = row.Location - 1;
 259            int end;
 260
 0261            row = _parsedData.Get(valueIndex);
 262
 0263            if (row.IsSimpleValue)
 0264            {
 0265                end = row.Location + row.SizeOrLength;
 266
 267                // If the value was a string, pick up the terminating quote.
 0268                if (row.TokenType == JsonTokenType.String)
 0269                {
 0270                    end++;
 0271                }
 272
 0273                return _utf8Json.Slice(start, end - start);
 274            }
 275
 0276            int endElementIdx = GetEndIndex(valueIndex, includeEndElement: false);
 0277            row = _parsedData.Get(endElementIdx);
 0278            end = row.Location + row.SizeOrLength;
 0279            return _utf8Json.Slice(start, end - start);
 0280        }
 281
 282        internal string? GetString(int index, JsonTokenType expectedType)
 0283        {
 0284            CheckNotDisposed();
 285
 0286            DbRow row = _parsedData.Get(index);
 287
 0288            JsonTokenType tokenType = row.TokenType;
 289
 0290            if (tokenType == JsonTokenType.Null)
 0291            {
 0292                return null;
 293            }
 294
 0295            CheckExpectedType(expectedType, tokenType);
 296
 0297            ReadOnlySpan<byte> data = _utf8Json.Span;
 0298            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 299
 0300            return row.HasComplexChildren
 0301                ? JsonReaderHelper.GetUnescapedString(segment)
 0302                : JsonReaderHelper.TranscodeHelper(segment);
 0303        }
 304
 305        internal bool TextEquals(int index, ReadOnlySpan<char> otherText, bool isPropertyName)
 0306        {
 0307            CheckNotDisposed();
 308
 0309            byte[]? otherUtf8TextArray = null;
 310
 0311            int length = checked(otherText.Length * JsonConstants.MaxExpansionFactorWhileTranscoding);
 0312            Span<byte> otherUtf8Text = length <= JsonConstants.StackallocByteThreshold ?
 0313                stackalloc byte[JsonConstants.StackallocByteThreshold] :
 0314                (otherUtf8TextArray = ArrayPool<byte>.Shared.Rent(length));
 315
 0316            OperationStatus status = JsonWriterHelper.ToUtf8(otherText, otherUtf8Text, out int written);
 0317            Debug.Assert(status != OperationStatus.DestinationTooSmall);
 318            bool result;
 0319            if (status == OperationStatus.InvalidData)
 0320            {
 0321                result = false;
 0322            }
 323            else
 0324            {
 0325                Debug.Assert(status == OperationStatus.Done);
 0326                result = TextEquals(index, otherUtf8Text.Slice(0, written), isPropertyName, shouldUnescape: true);
 0327            }
 328
 0329            if (otherUtf8TextArray != null)
 0330            {
 0331                otherUtf8Text.Slice(0, written).Clear();
 0332                ArrayPool<byte>.Shared.Return(otherUtf8TextArray);
 0333            }
 334
 0335            return result;
 0336        }
 337
 338        internal bool TextEquals(int index, ReadOnlySpan<byte> otherUtf8Text, bool isPropertyName, bool shouldUnescape)
 3339        {
 3340            CheckNotDisposed();
 341
 3342            int matchIndex = isPropertyName ? index - DbRow.Size : index;
 343
 3344            DbRow row = _parsedData.Get(matchIndex);
 345
 3346            CheckExpectedType(
 3347                isPropertyName ? JsonTokenType.PropertyName : JsonTokenType.String,
 3348                row.TokenType);
 349
 3350            ReadOnlySpan<byte> data = _utf8Json.Span;
 3351            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 352
 3353            if (otherUtf8Text.Length > segment.Length || (!shouldUnescape && otherUtf8Text.Length != segment.Length))
 0354            {
 0355                return false;
 356            }
 357
 3358            if (row.HasComplexChildren && shouldUnescape)
 0359            {
 0360                if (otherUtf8Text.Length < segment.Length / JsonConstants.MaxExpansionFactorWhileEscaping)
 0361                {
 0362                    return false;
 363                }
 364
 0365                int idx = segment.IndexOf(JsonConstants.BackSlash);
 0366                Debug.Assert(idx != -1);
 367
 0368                if (!otherUtf8Text.StartsWith(segment.Slice(0, idx)))
 0369                {
 0370                    return false;
 371                }
 372
 0373                return JsonReaderHelper.UnescapeAndCompare(segment.Slice(idx), otherUtf8Text.Slice(idx));
 374            }
 375
 3376            return segment.SequenceEqual(otherUtf8Text);
 3377        }
 378
 379        internal string GetNameOfPropertyValue(int index)
 0380        {
 381            // The property name is one row before the property value
 0382            return GetString(index - DbRow.Size, JsonTokenType.PropertyName)!;
 0383        }
 384
 385        internal ReadOnlySpan<byte> GetPropertyNameRaw(int index)
 0386        {
 0387            CheckNotDisposed();
 388
 0389            DbRow row = _parsedData.Get(index - DbRow.Size);
 0390            Debug.Assert(row.TokenType is JsonTokenType.PropertyName);
 391
 0392            return _utf8Json.Span.Slice(row.Location, row.SizeOrLength);
 0393        }
 394
 395        internal bool TryGetValue(int index, [NotNullWhen(true)] out byte[]? value)
 0396        {
 0397            CheckNotDisposed();
 398
 0399            DbRow row = _parsedData.Get(index);
 400
 0401            CheckExpectedType(JsonTokenType.String, row.TokenType);
 402
 0403            ReadOnlySpan<byte> data = _utf8Json.Span;
 0404            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 405
 406            // Segment needs to be unescaped
 0407            if (row.HasComplexChildren)
 0408            {
 0409                return JsonReaderHelper.TryGetUnescapedBase64Bytes(segment, out value);
 410            }
 411
 0412            Debug.Assert(!segment.Contains(JsonConstants.BackSlash));
 0413            return JsonReaderHelper.TryDecodeBase64(segment, out value);
 0414        }
 415
 416        internal bool TryGetValue(int index, out sbyte value)
 0417        {
 0418            CheckNotDisposed();
 419
 0420            DbRow row = _parsedData.Get(index);
 421
 0422            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 423
 0424            ReadOnlySpan<byte> data = _utf8Json.Span;
 0425            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 426
 0427            if (Utf8Parser.TryParse(segment, out sbyte tmp, out int consumed) &&
 0428                consumed == segment.Length)
 0429            {
 0430                value = tmp;
 0431                return true;
 432            }
 433
 0434            value = 0;
 0435            return false;
 0436        }
 437
 438        internal bool TryGetValue(int index, out byte value)
 0439        {
 0440            CheckNotDisposed();
 441
 0442            DbRow row = _parsedData.Get(index);
 443
 0444            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 445
 0446            ReadOnlySpan<byte> data = _utf8Json.Span;
 0447            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 448
 0449            if (Utf8Parser.TryParse(segment, out byte tmp, out int consumed) &&
 0450                consumed == segment.Length)
 0451            {
 0452                value = tmp;
 0453                return true;
 454            }
 455
 0456            value = 0;
 0457            return false;
 0458        }
 459
 460        internal bool TryGetValue(int index, out short value)
 0461        {
 0462            CheckNotDisposed();
 463
 0464            DbRow row = _parsedData.Get(index);
 465
 0466            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 467
 0468            ReadOnlySpan<byte> data = _utf8Json.Span;
 0469            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 470
 0471            if (Utf8Parser.TryParse(segment, out short tmp, out int consumed) &&
 0472                consumed == segment.Length)
 0473            {
 0474                value = tmp;
 0475                return true;
 476            }
 477
 0478            value = 0;
 0479            return false;
 0480        }
 481
 482        internal bool TryGetValue(int index, out ushort value)
 0483        {
 0484            CheckNotDisposed();
 485
 0486            DbRow row = _parsedData.Get(index);
 487
 0488            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 489
 0490            ReadOnlySpan<byte> data = _utf8Json.Span;
 0491            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 492
 0493            if (Utf8Parser.TryParse(segment, out ushort tmp, out int consumed) &&
 0494                consumed == segment.Length)
 0495            {
 0496                value = tmp;
 0497                return true;
 498            }
 499
 0500            value = 0;
 0501            return false;
 0502        }
 503
 504        internal bool TryGetValue(int index, out int value)
 0505        {
 0506            CheckNotDisposed();
 507
 0508            DbRow row = _parsedData.Get(index);
 509
 0510            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 511
 0512            ReadOnlySpan<byte> data = _utf8Json.Span;
 0513            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 514
 0515            if (Utf8Parser.TryParse(segment, out int tmp, out int consumed) &&
 0516                consumed == segment.Length)
 0517            {
 0518                value = tmp;
 0519                return true;
 520            }
 521
 0522            value = 0;
 0523            return false;
 0524        }
 525
 526        internal bool TryGetValue(int index, out uint value)
 0527        {
 0528            CheckNotDisposed();
 529
 0530            DbRow row = _parsedData.Get(index);
 531
 0532            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 533
 0534            ReadOnlySpan<byte> data = _utf8Json.Span;
 0535            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 536
 0537            if (Utf8Parser.TryParse(segment, out uint tmp, out int consumed) &&
 0538                consumed == segment.Length)
 0539            {
 0540                value = tmp;
 0541                return true;
 542            }
 543
 0544            value = 0;
 0545            return false;
 0546        }
 547
 548        internal bool TryGetValue(int index, out long value)
 0549        {
 0550            CheckNotDisposed();
 551
 0552            DbRow row = _parsedData.Get(index);
 553
 0554            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 555
 0556            ReadOnlySpan<byte> data = _utf8Json.Span;
 0557            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 558
 0559            if (Utf8Parser.TryParse(segment, out long tmp, out int consumed) &&
 0560                consumed == segment.Length)
 0561            {
 0562                value = tmp;
 0563                return true;
 564            }
 565
 0566            value = 0;
 0567            return false;
 0568        }
 569
 570        internal bool TryGetValue(int index, out ulong value)
 0571        {
 0572            CheckNotDisposed();
 573
 0574            DbRow row = _parsedData.Get(index);
 575
 0576            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 577
 0578            ReadOnlySpan<byte> data = _utf8Json.Span;
 0579            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 580
 0581            if (Utf8Parser.TryParse(segment, out ulong tmp, out int consumed) &&
 0582                consumed == segment.Length)
 0583            {
 0584                value = tmp;
 0585                return true;
 586            }
 587
 0588            value = 0;
 0589            return false;
 0590        }
 591
 592        internal bool TryGetValue(int index, out double value)
 0593        {
 0594            CheckNotDisposed();
 595
 0596            DbRow row = _parsedData.Get(index);
 597
 0598            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 599
 0600            ReadOnlySpan<byte> data = _utf8Json.Span;
 0601            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 602
 0603            if (Utf8Parser.TryParse(segment, out double tmp, out int bytesConsumed) &&
 0604                segment.Length == bytesConsumed)
 0605            {
 0606                value = tmp;
 0607                return true;
 608            }
 609
 0610            value = 0;
 0611            return false;
 0612        }
 613
 614        internal bool TryGetValue(int index, out float value)
 0615        {
 0616            CheckNotDisposed();
 617
 0618            DbRow row = _parsedData.Get(index);
 619
 0620            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 621
 0622            ReadOnlySpan<byte> data = _utf8Json.Span;
 0623            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 624
 0625            if (Utf8Parser.TryParse(segment, out float tmp, out int bytesConsumed) &&
 0626                segment.Length == bytesConsumed)
 0627            {
 0628                value = tmp;
 0629                return true;
 630            }
 631
 0632            value = 0;
 0633            return false;
 0634        }
 635
 636        internal bool TryGetValue(int index, out decimal value)
 0637        {
 0638            CheckNotDisposed();
 639
 0640            DbRow row = _parsedData.Get(index);
 641
 0642            CheckExpectedType(JsonTokenType.Number, row.TokenType);
 643
 0644            ReadOnlySpan<byte> data = _utf8Json.Span;
 0645            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 646
 0647            if (Utf8Parser.TryParse(segment, out decimal tmp, out int bytesConsumed) &&
 0648                segment.Length == bytesConsumed)
 0649            {
 0650                value = tmp;
 0651                return true;
 652            }
 653
 0654            value = 0;
 0655            return false;
 0656        }
 657
 658        internal bool TryGetValue(int index, out DateTime value)
 0659        {
 0660            CheckNotDisposed();
 661
 0662            DbRow row = _parsedData.Get(index);
 663
 0664            CheckExpectedType(JsonTokenType.String, row.TokenType);
 665
 0666            ReadOnlySpan<byte> data = _utf8Json.Span;
 0667            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 668
 0669            return JsonReaderHelper.TryGetValue(segment, row.HasComplexChildren, out value);
 0670        }
 671
 672        internal bool TryGetValue(int index, out DateTimeOffset value)
 0673        {
 0674            CheckNotDisposed();
 675
 0676            DbRow row = _parsedData.Get(index);
 677
 0678            CheckExpectedType(JsonTokenType.String, row.TokenType);
 679
 0680            ReadOnlySpan<byte> data = _utf8Json.Span;
 0681            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 682
 0683            return JsonReaderHelper.TryGetValue(segment, row.HasComplexChildren, out value);
 0684        }
 685
 686        internal bool TryGetValue(int index, out Guid value)
 0687        {
 0688            CheckNotDisposed();
 689
 0690            DbRow row = _parsedData.Get(index);
 691
 0692            CheckExpectedType(JsonTokenType.String, row.TokenType);
 693
 0694            ReadOnlySpan<byte> data = _utf8Json.Span;
 0695            ReadOnlySpan<byte> segment = data.Slice(row.Location, row.SizeOrLength);
 696
 0697            return JsonReaderHelper.TryGetValue(segment, row.HasComplexChildren, out value);
 0698        }
 699
 700        internal string GetRawValueAsString(int index)
 0701        {
 0702            ReadOnlyMemory<byte> segment = GetRawValue(index, includeQuotes: true);
 0703            return JsonReaderHelper.TranscodeHelper(segment.Span);
 0704        }
 705
 706        internal string GetPropertyRawValueAsString(int valueIndex)
 0707        {
 0708            ReadOnlyMemory<byte> segment = GetPropertyRawValue(valueIndex);
 0709            return JsonReaderHelper.TranscodeHelper(segment.Span);
 0710        }
 711
 712        internal JsonElement CloneElement(int index)
 0713        {
 0714            int endIndex = GetEndIndex(index, true);
 0715            MetadataDb newDb = _parsedData.CopySegment(index, endIndex);
 0716            ReadOnlyMemory<byte> segmentCopy = GetRawValue(index, includeQuotes: true).ToArray();
 717
 0718            JsonDocument newDocument =
 0719                new JsonDocument(
 0720                    segmentCopy,
 0721                    newDb,
 0722                    extraRentedArrayPoolBytes: null,
 0723                    extraPooledByteBufferWriter: null,
 0724                    isDisposable: false);
 725
 0726            return newDocument.RootElement;
 0727        }
 728
 729        internal void WriteElementTo(
 730            int index,
 731            Utf8JsonWriter writer)
 0732        {
 0733            CheckNotDisposed();
 734
 0735            DbRow row = _parsedData.Get(index);
 736
 0737            switch (row.TokenType)
 738            {
 739                case JsonTokenType.StartObject:
 0740                    writer.WriteStartObject();
 0741                    WriteComplexElement(index, writer);
 0742                    return;
 743                case JsonTokenType.StartArray:
 0744                    writer.WriteStartArray();
 0745                    WriteComplexElement(index, writer);
 0746                    return;
 747                case JsonTokenType.String:
 0748                    WriteString(row, writer);
 0749                    return;
 750                case JsonTokenType.Number:
 0751                    writer.WriteNumberValue(_utf8Json.Slice(row.Location, row.SizeOrLength).Span);
 0752                    return;
 753                case JsonTokenType.True:
 0754                    writer.WriteBooleanValue(value: true);
 0755                    return;
 756                case JsonTokenType.False:
 0757                    writer.WriteBooleanValue(value: false);
 0758                    return;
 759                case JsonTokenType.Null:
 0760                    writer.WriteNullValue();
 0761                    return;
 762            }
 763
 0764            Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
 0765        }
 766
 767        private void WriteComplexElement(int index, Utf8JsonWriter writer)
 0768        {
 0769            int endIndex = GetEndIndex(index, true);
 770
 0771            for (int i = index + DbRow.Size; i < endIndex; i += DbRow.Size)
 0772            {
 0773                DbRow row = _parsedData.Get(i);
 774
 775                // All of the types which don't need the value span
 0776                switch (row.TokenType)
 777                {
 778                    case JsonTokenType.String:
 0779                        WriteString(row, writer);
 0780                        continue;
 781                    case JsonTokenType.Number:
 0782                        writer.WriteNumberValue(_utf8Json.Slice(row.Location, row.SizeOrLength).Span);
 0783                        continue;
 784                    case JsonTokenType.True:
 0785                        writer.WriteBooleanValue(value: true);
 0786                        continue;
 787                    case JsonTokenType.False:
 0788                        writer.WriteBooleanValue(value: false);
 0789                        continue;
 790                    case JsonTokenType.Null:
 0791                        writer.WriteNullValue();
 0792                        continue;
 793                    case JsonTokenType.StartObject:
 0794                        writer.WriteStartObject();
 0795                        continue;
 796                    case JsonTokenType.EndObject:
 0797                        writer.WriteEndObject();
 0798                        continue;
 799                    case JsonTokenType.StartArray:
 0800                        writer.WriteStartArray();
 0801                        continue;
 802                    case JsonTokenType.EndArray:
 0803                        writer.WriteEndArray();
 0804                        continue;
 805                    case JsonTokenType.PropertyName:
 0806                        WritePropertyName(row, writer);
 0807                        continue;
 808                }
 809
 0810                Debug.Fail($"Unexpected encounter with JsonTokenType {row.TokenType}");
 811            }
 0812        }
 813
 814        private ReadOnlySpan<byte> UnescapeString(in DbRow row, out ArraySegment<byte> rented)
 0815        {
 0816            Debug.Assert(row.TokenType == JsonTokenType.String || row.TokenType == JsonTokenType.PropertyName);
 0817            int loc = row.Location;
 0818            int length = row.SizeOrLength;
 0819            ReadOnlySpan<byte> text = _utf8Json.Slice(loc, length).Span;
 820
 0821            if (!row.HasComplexChildren)
 0822            {
 0823                rented = default;
 0824                return text;
 825            }
 826
 0827            byte[] rent = ArrayPool<byte>.Shared.Rent(length);
 0828            JsonReaderHelper.Unescape(text, rent, out int written);
 0829            rented = new ArraySegment<byte>(rent, 0, written);
 0830            return rented.AsSpan();
 0831        }
 832
 833        private static void ClearAndReturn(ArraySegment<byte> rented)
 0834        {
 0835            if (rented.Array != null)
 0836            {
 0837                rented.AsSpan().Clear();
 0838                ArrayPool<byte>.Shared.Return(rented.Array);
 0839            }
 0840        }
 841
 842        internal void WritePropertyName(int index, Utf8JsonWriter writer)
 0843        {
 0844            CheckNotDisposed();
 845
 0846            DbRow row = _parsedData.Get(index - DbRow.Size);
 0847            Debug.Assert(row.TokenType == JsonTokenType.PropertyName);
 0848            WritePropertyName(row, writer);
 0849        }
 850
 851        private void WritePropertyName(in DbRow row, Utf8JsonWriter writer)
 0852        {
 0853            ArraySegment<byte> rented = default;
 854
 855            try
 0856            {
 0857                writer.WritePropertyName(UnescapeString(row, out rented));
 0858            }
 859            finally
 0860            {
 0861                ClearAndReturn(rented);
 0862            }
 0863        }
 864
 865        private void WriteString(in DbRow row, Utf8JsonWriter writer)
 0866        {
 0867            ArraySegment<byte> rented = default;
 868
 869            try
 0870            {
 0871                writer.WriteStringValue(UnescapeString(row, out rented));
 0872            }
 873            finally
 0874            {
 0875                ClearAndReturn(rented);
 0876            }
 0877        }
 878
 879        private static void Parse(
 880            ReadOnlySpan<byte> utf8JsonSpan,
 881            JsonReaderOptions readerOptions,
 882            ref MetadataDb database,
 883            ref StackRowStack stack)
 1400884        {
 1400885            bool inArray = false;
 1400886            int arrayItemsOrPropertyCount = 0;
 1400887            int numberOfRowsForMembers = 0;
 1400888            int numberOfRowsForValues = 0;
 889
 1400890            Utf8JsonReader reader = new Utf8JsonReader(
 1400891                utf8JsonSpan,
 1400892                isFinalBlock: true,
 1400893                new JsonReaderState(options: readerOptions));
 894
 2938895            while (reader.Read())
 1538896            {
 1538897                JsonTokenType tokenType = reader.TokenType;
 898
 899                // Since the input payload is contained within a Span,
 900                // token start index can never be larger than int.MaxValue (i.e. utf8JsonSpan.Length).
 1538901                Debug.Assert(reader.TokenStartIndex <= int.MaxValue);
 1538902                int tokenStart = (int)reader.TokenStartIndex;
 903
 1538904                if (tokenType == JsonTokenType.StartObject)
 0905                {
 0906                    if (inArray)
 0907                    {
 0908                        arrayItemsOrPropertyCount++;
 0909                    }
 910
 0911                    numberOfRowsForValues++;
 0912                    database.Append(tokenType, tokenStart, DbRow.UnknownSize);
 0913                    var row = new StackRow(arrayItemsOrPropertyCount, numberOfRowsForMembers + 1);
 0914                    stack.Push(row);
 0915                    arrayItemsOrPropertyCount = 0;
 0916                    numberOfRowsForMembers = 0;
 0917                }
 1538918                else if (tokenType == JsonTokenType.EndObject)
 0919                {
 0920                    int rowIndex = database.FindIndexOfFirstUnsetSizeOrLength(JsonTokenType.StartObject);
 921
 0922                    numberOfRowsForValues++;
 0923                    numberOfRowsForMembers++;
 0924                    database.SetLength(rowIndex, arrayItemsOrPropertyCount);
 925
 0926                    int newRowIndex = database.Length;
 0927                    database.Append(tokenType, tokenStart, reader.ValueSpan.Length);
 0928                    database.SetNumberOfRows(rowIndex, numberOfRowsForMembers);
 0929                    database.SetNumberOfRows(newRowIndex, numberOfRowsForMembers);
 930
 0931                    StackRow row = stack.Pop();
 0932                    arrayItemsOrPropertyCount = row.SizeOrLength;
 0933                    numberOfRowsForMembers += row.NumberOfRows;
 0934                }
 1538935                else if (tokenType == JsonTokenType.StartArray)
 138936                {
 138937                    if (inArray)
 0938                    {
 0939                        arrayItemsOrPropertyCount++;
 0940                    }
 941
 138942                    numberOfRowsForMembers++;
 138943                    database.Append(tokenType, tokenStart, DbRow.UnknownSize);
 138944                    var row = new StackRow(arrayItemsOrPropertyCount, numberOfRowsForValues + 1);
 138945                    stack.Push(row);
 138946                    arrayItemsOrPropertyCount = 0;
 138947                    numberOfRowsForValues = 0;
 138948                }
 1400949                else if (tokenType == JsonTokenType.EndArray)
 138950                {
 138951                    int rowIndex = database.FindIndexOfFirstUnsetSizeOrLength(JsonTokenType.StartArray);
 952
 138953                    numberOfRowsForValues++;
 138954                    numberOfRowsForMembers++;
 138955                    database.SetLength(rowIndex, arrayItemsOrPropertyCount);
 138956                    database.SetNumberOfRows(rowIndex, numberOfRowsForValues);
 957
 958                    // If the array item count is (e.g.) 12 and the number of rows is (e.g.) 13
 959                    // then the extra row is just this EndArray item, so the array was made up
 960                    // of simple values.
 961                    //
 962                    // If the off-by-one relationship does not hold, then one of the values was
 963                    // more than one row, making it a complex object.
 964                    //
 965                    // This check is similar to tracking the start array and painting it when
 966                    // StartObject or StartArray is encountered, but avoids the mixed state
 967                    // where "UnknownSize" implies "has complex children".
 138968                    if (arrayItemsOrPropertyCount + 1 != numberOfRowsForValues)
 0969                    {
 0970                        database.SetHasComplexChildren(rowIndex);
 0971                    }
 972
 138973                    int newRowIndex = database.Length;
 138974                    database.Append(tokenType, tokenStart, reader.ValueSpan.Length);
 138975                    database.SetNumberOfRows(newRowIndex, numberOfRowsForValues);
 976
 138977                    StackRow row = stack.Pop();
 138978                    arrayItemsOrPropertyCount = row.SizeOrLength;
 138979                    numberOfRowsForValues += row.NumberOfRows;
 138980                }
 1262981                else if (tokenType == JsonTokenType.PropertyName)
 0982                {
 0983                    numberOfRowsForValues++;
 0984                    numberOfRowsForMembers++;
 0985                    arrayItemsOrPropertyCount++;
 986
 987                    // Adding 1 to skip the start quote will never overflow
 0988                    Debug.Assert(tokenStart < int.MaxValue);
 989
 0990                    database.Append(tokenType, tokenStart + 1, reader.ValueSpan.Length);
 991
 0992                    if (reader.ValueIsEscaped)
 0993                    {
 0994                        database.SetHasComplexChildren(database.Length - DbRow.Size);
 0995                    }
 996
 0997                    Debug.Assert(!inArray);
 0998                }
 999                else
 12621000                {
 12621001                    Debug.Assert(tokenType >= JsonTokenType.String && tokenType <= JsonTokenType.Null);
 12621002                    numberOfRowsForValues++;
 12621003                    numberOfRowsForMembers++;
 1004
 12621005                    if (inArray)
 01006                    {
 01007                        arrayItemsOrPropertyCount++;
 01008                    }
 1009
 12621010                    if (tokenType == JsonTokenType.String)
 8461011                    {
 1012                        // Adding 1 to skip the start quote will never overflow
 8461013                        Debug.Assert(tokenStart < int.MaxValue);
 1014
 8461015                        database.Append(tokenType, tokenStart + 1, reader.ValueSpan.Length);
 1016
 8461017                        if (reader.ValueIsEscaped)
 01018                        {
 01019                            database.SetHasComplexChildren(database.Length - DbRow.Size);
 01020                        }
 8461021                    }
 1022                    else
 4161023                    {
 4161024                        database.Append(tokenType, tokenStart, reader.ValueSpan.Length);
 4161025                    }
 12621026                }
 1027
 15381028                inArray = reader.IsInArray;
 15381029            }
 1030
 14001031            Debug.Assert(reader.BytesConsumed == utf8JsonSpan.Length);
 14001032            database.CompleteAllocations();
 14001033        }
 1034
 1035        private static void ValidateNoDuplicateProperties(JsonDocument document)
 01036        {
 01037            if (document.RootElement.ValueKind is JsonValueKind.Array or JsonValueKind.Object)
 01038            {
 01039                ValidateDuplicatePropertiesCore(document);
 01040            }
 01041        }
 1042
 1043        private static void ValidateDuplicatePropertiesCore(JsonDocument document)
 01044        {
 01045            Debug.Assert(document.RootElement.ValueKind is JsonValueKind.Array or JsonValueKind.Object);
 1046
 01047            using PropertyNameSet propertyNameSet = new PropertyNameSet();
 1048
 01049            Stack<int> traversalPath = new Stack<int>();
 01050            int? databaseIndexOflastProcessedChild = null;
 1051
 01052            traversalPath.Push(document.RootElement.MetadataDbIndex);
 1053
 1054            do
 01055            {
 01056                JsonElement curr = new JsonElement(document, traversalPath.Peek());
 1057
 01058                switch (curr.ValueKind)
 1059                {
 1060                    case JsonValueKind.Object:
 01061                    {
 01062                        JsonElement.ObjectEnumerator enumerator = new(curr, databaseIndexOflastProcessedChild ?? -1);
 1063
 01064                        while (enumerator.MoveNext())
 01065                        {
 01066                            if (enumerator.Current.Value.ValueKind is JsonValueKind.Object or JsonValueKind.Array)
 01067                            {
 01068                                traversalPath.Push(enumerator.Current.Value.MetadataDbIndex);
 01069                                databaseIndexOflastProcessedChild = null;
 01070                                goto continueOuter;
 1071                            }
 01072                        }
 1073
 1074                        // No more children, so process the current element.
 01075                        enumerator.Reset();
 01076                        propertyNameSet.SetCapacity(curr.GetPropertyCount());
 1077
 01078                        foreach (JsonProperty property in enumerator)
 01079                        {
 01080                            propertyNameSet.AddPropertyName(property, document);
 01081                        }
 1082
 01083                        propertyNameSet.Reset();
 01084                        databaseIndexOflastProcessedChild = traversalPath.Pop();
 01085                        break;
 1086                    }
 1087                    case JsonValueKind.Array:
 01088                    {
 01089                        JsonElement.ArrayEnumerator enumerator = new(curr, databaseIndexOflastProcessedChild ?? -1);
 1090
 01091                        while (enumerator.MoveNext())
 01092                        {
 01093                            if (enumerator.Current.ValueKind is JsonValueKind.Object or JsonValueKind.Array)
 01094                            {
 01095                                traversalPath.Push(enumerator.Current.MetadataDbIndex);
 01096                                databaseIndexOflastProcessedChild = null;
 01097                                goto continueOuter;
 1098                            }
 01099                        }
 1100
 01101                        databaseIndexOflastProcessedChild = traversalPath.Pop();
 01102                        break;
 1103                    }
 1104                    default:
 01105                        Debug.Fail($"Expected only complex children but got {curr.ValueKind}");
 1106                        ThrowHelper.ThrowJsonException();
 1107                        break;
 1108                }
 1109
 01110            continueOuter:
 01111                ;
 01112            } while (traversalPath.Count is not 0);
 01113        }
 1114
 1115        private void CheckNotDisposed()
 2241116        {
 2241117            if (_utf8Json.IsEmpty)
 01118            {
 01119                ThrowHelper.ThrowObjectDisposedException_JsonDocument();
 1120            }
 2241121        }
 1122
 1123        private static void CheckExpectedType(JsonTokenType expected, JsonTokenType actual)
 31124        {
 31125            if (expected != actual)
 01126            {
 01127                ThrowHelper.ThrowJsonElementWrongTypeException(expected, actual);
 1128            }
 31129        }
 1130
 1131        private static void CheckSupportedOptions(
 1132            JsonReaderOptions readerOptions,
 1133            string paramName)
 26421134        {
 1135            // Since these are coming from a valid instance of Utf8JsonReader, the JsonReaderOptions must already be val
 26421136            Debug.Assert(readerOptions.CommentHandling >= 0 && readerOptions.CommentHandling <= JsonCommentHandling.Allo
 1137
 26421138            if (readerOptions.CommentHandling == JsonCommentHandling.Allow)
 01139            {
 01140                throw new ArgumentException(SR.JsonDocumentDoesNotSupportComments, paramName);
 1141            }
 26421142        }
 1143    }
 1144}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.DbRow.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.Diagnostics;
 5using System.Runtime.InteropServices;
 6using System.Runtime.CompilerServices;
 7
 8namespace System.Text.Json
 9{
 10    public sealed partial class JsonDocument
 11    {
 12        [StructLayout(LayoutKind.Sequential)]
 13        internal readonly struct DbRow
 14        {
 15            internal const int Size = 12;
 16
 17            // Sign bit is currently unassigned
 18            private readonly int _location;
 19
 20            // Sign bit is used for "HasComplexChildren" (StartArray)
 21            private readonly int _sizeOrLengthUnion;
 22
 23            // Top nybble is JsonTokenType
 24            // remaining nybbles are the number of rows to skip to get to the next value
 25            // This isn't limiting on the number of rows, since Span.MaxLength / sizeof(DbRow) can't
 26            // exceed that range.
 27            private readonly int _numberOfRowsAndTypeUnion;
 28
 29            /// <summary>
 30            /// Index into the payload
 31            /// </summary>
 7632            internal int Location => _location;
 33
 34            /// <summary>
 35            /// length of text in JSON payload (or number of elements if its a JSON array)
 36            /// </summary>
 7637            internal int SizeOrLength => _sizeOrLengthUnion & int.MaxValue;
 38
 13839            internal bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize;
 40
 41            /// <summary>
 42            /// String/PropertyName: Unescaping is required.
 43            /// Array: At least one element is an object/array.
 44            /// Otherwise; false
 45            /// </summary>
 646            internal bool HasComplexChildren => _sizeOrLengthUnion < 0;
 47
 48            internal int NumberOfRows =>
 049                _numberOfRowsAndTypeUnion & 0x0FFFFFFF; // Number of rows that the current JSON element occupies within 
 50
 28451            internal JsonTokenType TokenType => (JsonTokenType)(unchecked((uint)_numberOfRowsAndTypeUnion) >> 28);
 52
 53            internal const int UnknownSize = -1;
 54
 55            internal DbRow(JsonTokenType jsonTokenType, int location, int sizeOrLength)
 153856            {
 153857                Debug.Assert(jsonTokenType > JsonTokenType.None && jsonTokenType <= JsonTokenType.Null);
 153858                Debug.Assert((byte)jsonTokenType < 1 << 4);
 153859                Debug.Assert(location >= 0);
 153860                Debug.Assert(sizeOrLength >= UnknownSize);
 153861                Debug.Assert(Unsafe.SizeOf<DbRow>() == Size);
 62
 153863                _location = location;
 153864                _sizeOrLengthUnion = sizeOrLength;
 153865                _numberOfRowsAndTypeUnion = (int)jsonTokenType << 28;
 153866            }
 67
 7368            internal bool IsSimpleValue => TokenType >= JsonTokenType.PropertyName;
 69        }
 70    }
 71}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.MetadataDb.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.InteropServices;
 7using System.Threading;
 8
 9// We need to target netstandard2.0, so keep using ref for MemoryMarshal.Write
 10// CS9191: The 'ref' modifier for argument 2 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' 
 11#pragma warning disable CS9191
 12
 13namespace System.Text.Json
 14{
 15    public sealed partial class JsonDocument
 16    {
 17        // The database for the parsed structure of a JSON document.
 18        //
 19        // Every token from the document gets a row, which has one of the following forms:
 20        //
 21        // Number
 22        // * First int
 23        //   * Top bit is unassigned / always clear
 24        //   * 31 bits for token offset
 25        // * Second int
 26        //   * Top bit is set if the number uses scientific notation
 27        //   * 31 bits for the token length
 28        // * Third int
 29        //   * 4 bits JsonTokenType
 30        //   * 28 bits unassigned / always clear
 31        //
 32        // String, PropertyName
 33        // * First int
 34        //   * Top bit is unassigned / always clear
 35        //   * 31 bits for token offset
 36        // * Second int
 37        //   * Top bit is set if the string requires unescaping
 38        //   * 31 bits for the token length
 39        // * Third int
 40        //   * 4 bits JsonTokenType
 41        //   * 28 bits unassigned / always clear
 42        //
 43        // Other value types (True, False, Null)
 44        // * First int
 45        //   * Top bit is unassigned / always clear
 46        //   * 31 bits for token offset
 47        // * Second int
 48        //   * Top bit is unassigned / always clear
 49        //   * 31 bits for the token length
 50        // * Third int
 51        //   * 4 bits JsonTokenType
 52        //   * 28 bits unassigned / always clear
 53        //
 54        // EndObject / EndArray
 55        // * First int
 56        //   * Top bit is unassigned / always clear
 57        //   * 31 bits for token offset
 58        // * Second int
 59        //   * Top bit is unassigned / always clear
 60        //   * 31 bits for the token length (always 1, effectively unassigned)
 61        // * Third int
 62        //   * 4 bits JsonTokenType
 63        //   * 28 bits for the number of rows until the previous value (never 0)
 64        //
 65        // StartObject
 66        // * First int
 67        //   * Top bit is unassigned / always clear
 68        //   * 31 bits for token offset
 69        // * Second int
 70        //   * Top bit is unassigned / always clear
 71        //   * 31 bits for the number of properties in this object
 72        // * Third int
 73        //   * 4 bits JsonTokenType
 74        //   * 28 bits for the number of rows until the next value (never 0)
 75        //
 76        // StartArray
 77        // * First int
 78        //   * Top bit is unassigned / always clear
 79        //   * 31 bits for token offset
 80        // * Second int
 81        //   * Top bit is set if the array contains other arrays or objects ("complex" types)
 82        //   * 31 bits for the number of elements in this array
 83        // * Third int
 84        //   * 4 bits JsonTokenType
 85        //   * 28 bits for the number of rows until the next value (never 0)
 86        private struct MetadataDb : IDisposable
 87        {
 88            private const int SizeOrLengthOffset = 4;
 89            private const int NumberOfRowsOffset = 8;
 90
 958891            internal int Length { get; private set; }
 92            private byte[] _data;
 93
 94            private bool _convertToAlloc; // Convert the rented data to an alloc when complete.
 95            private bool _isLocked; // Is the array the correct fixed size.
 96            // _isLocked _convertToAlloc truth table:
 97            // false     false  Standard flow. Size is not known and renting used throughout lifetime.
 98            // true      false  Used by JsonElement.ParseValue() for primitives and JsonDocument.Clone(). Size is known 
 99            // false     true   Used by JsonElement.ParseValue() for arrays and objects. Renting used until size is know
 100            // true      true   not valid
 101
 102            private MetadataDb(byte[] initialDb, bool isLocked, bool convertToAlloc)
 1400103            {
 1400104                _data = initialDb;
 1400105                _isLocked = isLocked;
 1400106                _convertToAlloc = convertToAlloc;
 1400107                Length = 0;
 1400108            }
 109
 110            internal MetadataDb(byte[] completeDb)
 0111            {
 0112                _data = completeDb;
 0113                _isLocked = true;
 0114                _convertToAlloc = false;
 0115                Length = completeDb.Length;
 0116            }
 117
 118            internal static MetadataDb CreateRented(int payloadLength, bool convertToAlloc)
 588119            {
 120                // Assume that a token happens approximately every 12 bytes.
 121                // int estimatedTokens = payloadLength / 12
 122                // now acknowledge that the number of bytes we need per token is 12.
 123                // So that's just the payload length.
 124                //
 125                // Add one row worth of data since we need at least one row for a primitive type.
 588126                int initialSize = payloadLength + DbRow.Size;
 127
 128                // Stick with ArrayPool's rent/return range if it looks feasible.
 129                // If it's wrong, we'll just grow and copy as we would if the tokens
 130                // were more frequent anyways.
 131                const int OneMegabyte = 1024 * 1024;
 132
 588133                if (initialSize > OneMegabyte && initialSize <= 4 * OneMegabyte)
 0134                {
 0135                    initialSize = OneMegabyte;
 0136                }
 137
 588138                byte[] data = ArrayPool<byte>.Shared.Rent(initialSize);
 588139                return new MetadataDb(data, isLocked: false, convertToAlloc);
 588140            }
 141
 142            internal static MetadataDb CreateLocked(int payloadLength)
 812143            {
 144                // Add one row worth of data since we need at least one row for a primitive type.
 812145                int size = payloadLength + DbRow.Size;
 146
 812147                byte[] data = new byte[size];
 812148                return new MetadataDb(data, isLocked: true, convertToAlloc: false);
 812149            }
 150
 151            public void Dispose()
 28152            {
 28153                byte[]? data = Interlocked.Exchange(ref _data, null!);
 28154                if (data == null)
 0155                {
 0156                    return;
 157                }
 158
 28159                Debug.Assert(!_isLocked, "Dispose called on a locked database");
 160
 161                // The data in this rented buffer only conveys the positions and
 162                // lengths of tokens in a document, but no content; so it does not
 163                // need to be cleared.
 28164                ArrayPool<byte>.Shared.Return(data);
 28165                Length = 0;
 28166            }
 167
 168            /// <summary>
 169            /// If using array pools, trim excess if necessary.
 170            /// If not using array pools, release the temporary array pool and alloc.
 171            /// </summary>
 172            internal void CompleteAllocations()
 1400173            {
 1400174                if (!_isLocked)
 588175                {
 588176                    if (_convertToAlloc)
 110177                    {
 110178                        Debug.Assert(_data != null);
 110179                        byte[] returnBuf = _data;
 110180                        _data = _data.AsSpan(0, Length).ToArray();
 110181                        _isLocked = true;
 110182                        _convertToAlloc = false;
 183
 184                        // The data in this rented buffer only conveys the positions and
 185                        // lengths of tokens in a document, but no content; so it does not
 186                        // need to be cleared.
 110187                        ArrayPool<byte>.Shared.Return(returnBuf);
 110188                    }
 189                    else
 478190                    {
 191                        // There's a chance that the size we have is the size we'd get for this
 192                        // amount of usage (particularly if Enlarge ever got called); and there's
 193                        // the small copy-cost associated with trimming anyways. "Is half-empty" is
 194                        // just a rough metric for "is trimming worth it?".
 478195                        if (Length <= _data.Length / 2)
 184196                        {
 184197                            byte[] newRent = ArrayPool<byte>.Shared.Rent(Length);
 184198                            byte[] returnBuf = newRent;
 199
 184200                            if (newRent.Length < _data.Length)
 184201                            {
 184202                                Buffer.BlockCopy(_data, 0, newRent, 0, Length);
 184203                                returnBuf = _data;
 184204                                _data = newRent;
 184205                            }
 206
 207                            // The data in this rented buffer only conveys the positions and
 208                            // lengths of tokens in a document, but no content; so it does not
 209                            // need to be cleared.
 184210                            ArrayPool<byte>.Shared.Return(returnBuf);
 184211                        }
 478212                    }
 588213                }
 1400214            }
 215
 216            internal void Append(JsonTokenType tokenType, int startLocation, int length)
 1538217            {
 218                // StartArray or StartObject should have length -1, otherwise the length should not be -1.
 1538219                Debug.Assert(
 1538220                    (tokenType == JsonTokenType.StartArray || tokenType == JsonTokenType.StartObject) ==
 1538221                    (length == DbRow.UnknownSize));
 222
 1538223                if (Length >= _data.Length - DbRow.Size)
 138224                {
 138225                    Enlarge();
 138226                }
 227
 1538228                DbRow row = new DbRow(tokenType, startLocation, length);
 1538229                MemoryMarshal.Write(_data.AsSpan(Length), ref row);
 1538230                Length += DbRow.Size;
 1538231            }
 232
 233            private void Enlarge()
 138234            {
 138235                Debug.Assert(!_isLocked, "Appending to a locked database");
 236
 138237                byte[] toReturn = _data;
 238
 239                // Allow the data to grow up to maximum possible capacity (~2G bytes) before encountering overflow.
 240                // Note: Array.MaxLength exists only on .NET 6 or greater,
 241                // so for the other versions value is hardcoded
 242                const int MaxArrayLength = 0x7FFFFFC7;
 243#if NET
 138244                Debug.Assert(MaxArrayLength == Array.MaxLength);
 245#endif
 246
 138247                int newCapacity = toReturn.Length * 2;
 248
 249                // Note that this check works even when newCapacity overflowed thanks to the (uint) cast
 138250                if ((uint)newCapacity > MaxArrayLength) newCapacity = MaxArrayLength;
 251
 252                // If the maximum capacity has already been reached,
 253                // then set the new capacity to be larger than what is possible
 254                // so that ArrayPool.Rent throws an OutOfMemoryException for us.
 138255                if (newCapacity == toReturn.Length) newCapacity = int.MaxValue;
 256
 138257                _data = ArrayPool<byte>.Shared.Rent(newCapacity);
 138258                Buffer.BlockCopy(toReturn, 0, _data, 0, toReturn.Length);
 259
 260                // The data in this rented buffer only conveys the positions and
 261                // lengths of tokens in a document, but no content; so it does not
 262                // need to be cleared.
 138263                ArrayPool<byte>.Shared.Return(toReturn);
 138264            }
 265
 266            [Conditional("DEBUG")]
 267            private void AssertValidIndex(int index)
 638268            {
 638269                Debug.Assert(index >= 0);
 638270                Debug.Assert(index <= Length - DbRow.Size, $"index {index} is out of bounds");
 638271                Debug.Assert(index % DbRow.Size == 0, $"index {index} is not at a record start position");
 638272            }
 273
 274            internal void SetLength(int index, int length)
 138275            {
 138276                AssertValidIndex(index);
 138277                Debug.Assert(length >= 0);
 138278                Span<byte> destination = _data.AsSpan(index + SizeOrLengthOffset);
 138279                MemoryMarshal.Write(destination, ref length);
 138280            }
 281
 282            internal void SetNumberOfRows(int index, int numberOfRows)
 276283            {
 276284                AssertValidIndex(index);
 276285                Debug.Assert(numberOfRows >= 1 && numberOfRows <= 0x0FFFFFFF);
 286
 276287                Span<byte> dataPos = _data.AsSpan(index + NumberOfRowsOffset);
 276288                int current = MemoryMarshal.Read<int>(dataPos);
 289
 290                // Persist the most significant nybble
 276291                int value = (current & unchecked((int)0xF0000000)) | numberOfRows;
 276292                MemoryMarshal.Write(dataPos, ref value);
 276293            }
 294
 295            internal void SetHasComplexChildren(int index)
 0296            {
 0297                AssertValidIndex(index);
 298
 299                // The HasComplexChildren bit is the most significant bit of "SizeOrLength"
 0300                Span<byte> dataPos = _data.AsSpan(index + SizeOrLengthOffset);
 0301                int current = MemoryMarshal.Read<int>(dataPos);
 302
 0303                int value = current | unchecked((int)0x80000000);
 0304                MemoryMarshal.Write(dataPos, ref value);
 0305            }
 306
 307            internal int FindIndexOfFirstUnsetSizeOrLength(JsonTokenType lookupType)
 138308            {
 138309                Debug.Assert(lookupType == JsonTokenType.StartObject || lookupType == JsonTokenType.StartArray);
 138310                return FindOpenElement(lookupType);
 138311            }
 312
 313            private int FindOpenElement(JsonTokenType lookupType)
 138314            {
 138315                Span<byte> data = _data.AsSpan(0, Length);
 316
 276317                for (int i = Length - DbRow.Size; i >= 0; i -= DbRow.Size)
 138318                {
 138319                    DbRow row = MemoryMarshal.Read<DbRow>(data.Slice(i));
 320
 138321                    if (row.IsUnknownSize && row.TokenType == lookupType)
 138322                    {
 138323                        return i;
 324                    }
 0325                }
 326
 327                // We should never reach here.
 0328                Debug.Fail($"Unable to find expected {lookupType} token");
 329                return -1;
 138330            }
 331
 332            internal DbRow Get(int index)
 79333            {
 79334                AssertValidIndex(index);
 79335                return MemoryMarshal.Read<DbRow>(_data.AsSpan(index));
 79336            }
 337
 338            internal JsonTokenType GetJsonTokenType(int index)
 145339            {
 145340                AssertValidIndex(index);
 145341                uint union = MemoryMarshal.Read<uint>(_data.AsSpan(index + NumberOfRowsOffset));
 342
 145343                return (JsonTokenType)(union >> 28);
 145344            }
 345
 346            internal MetadataDb CopySegment(int startIndex, int endIndex)
 0347            {
 0348                Debug.Assert(
 0349                    endIndex > startIndex,
 0350                    $"endIndex={endIndex} was at or before startIndex={startIndex}");
 351
 0352                AssertValidIndex(startIndex);
 0353                Debug.Assert(endIndex <= Length);
 354
 0355                DbRow start = Get(startIndex);
 356#if DEBUG
 0357                DbRow end = Get(endIndex - DbRow.Size);
 358
 0359                if (start.TokenType == JsonTokenType.StartObject)
 0360                {
 0361                    Debug.Assert(
 0362                        end.TokenType == JsonTokenType.EndObject,
 0363                        $"StartObject paired with {end.TokenType}");
 0364                }
 0365                else if (start.TokenType == JsonTokenType.StartArray)
 0366                {
 0367                    Debug.Assert(
 0368                        end.TokenType == JsonTokenType.EndArray,
 0369                        $"StartArray paired with {end.TokenType}");
 0370                }
 371                else
 0372                {
 0373                    Debug.Assert(
 0374                        startIndex + DbRow.Size == endIndex,
 0375                        $"{start.TokenType} should have been one row");
 0376                }
 377#endif
 378
 0379                int length = endIndex - startIndex;
 380
 0381                byte[] newDatabase = new byte[length];
 0382                _data.AsSpan(startIndex, length).CopyTo(newDatabase);
 383
 0384                Span<int> newDbInts = MemoryMarshal.Cast<byte, int>(newDatabase.AsSpan());
 0385                int locationOffset = newDbInts[0];
 386
 387                // Need to nudge one forward to account for the hidden quote on the string.
 0388                if (start.TokenType == JsonTokenType.String)
 0389                {
 0390                    locationOffset--;
 0391                }
 392
 0393                for (int i = (length - DbRow.Size) / sizeof(int); i >= 0; i -= DbRow.Size / sizeof(int))
 0394                {
 0395                    Debug.Assert(newDbInts[i] >= locationOffset);
 0396                    newDbInts[i] -= locationOffset;
 0397                }
 398
 0399                return new MetadataDb(newDatabase);
 0400            }
 401        }
 402    }
 403}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.Parse.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.Diagnostics.CodeAnalysis;
 7using System.IO;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Text.Json
 12{
 13    public sealed partial class JsonDocument
 14    {
 15        // Cached unrented documents for literal values.
 16        private static JsonDocument? s_nullLiteral;
 17        private static JsonDocument? s_trueLiteral;
 18        private static JsonDocument? s_falseLiteral;
 19
 20        private const int UnseekableStreamInitialRentSize = 4096;
 21
 22        /// <summary>
 23        ///   Parse memory as UTF-8 encoded text representing a single JSON value into a JsonDocument.
 24        /// </summary>
 25        /// <remarks>
 26        ///   <para>
 27        ///     The <see cref="ReadOnlyMemory{T}"/> value will be used for the entire lifetime of the
 28        ///     JsonDocument object, and the caller must ensure that the data therein does not change during
 29        ///     the object lifetime.
 30        ///   </para>
 31        ///
 32        ///   <para>
 33        ///     Because the input is considered to be text, a UTF-8 Byte-Order-Mark (BOM) must not be present.
 34        ///   </para>
 35        /// </remarks>
 36        /// <param name="utf8Json">JSON text to parse.</param>
 37        /// <param name="options">Options to control the reader behavior during parsing.</param>
 38        /// <returns>
 39        ///   A JsonDocument representation of the JSON value.
 40        /// </returns>
 41        /// <exception cref="JsonException">
 42        ///   <paramref name="utf8Json"/> does not represent a valid single JSON value.
 43        /// </exception>
 44        /// <exception cref="ArgumentException">
 45        ///   <paramref name="options"/> contains unsupported options.
 46        /// </exception>
 47        public static JsonDocument Parse(ReadOnlyMemory<byte> utf8Json, JsonDocumentOptions options = default)
 048        {
 049            return Parse(utf8Json, options.GetReaderOptions(), allowDuplicateProperties: options.AllowDuplicatePropertie
 050        }
 51
 52        /// <summary>
 53        ///   Parse a sequence as UTF-8 encoded text representing a single JSON value into a JsonDocument.
 54        /// </summary>
 55        /// <remarks>
 56        ///   <para>
 57        ///     The <see cref="ReadOnlySequence{T}"/> may be used for the entire lifetime of the
 58        ///     JsonDocument object, and the caller must ensure that the data therein does not change during
 59        ///     the object lifetime.
 60        ///   </para>
 61        ///
 62        ///   <para>
 63        ///     Because the input is considered to be text, a UTF-8 Byte-Order-Mark (BOM) must not be present.
 64        ///   </para>
 65        /// </remarks>
 66        /// <param name="utf8Json">JSON text to parse.</param>
 67        /// <param name="options">Options to control the reader behavior during parsing.</param>
 68        /// <returns>
 69        ///   A JsonDocument representation of the JSON value.
 70        /// </returns>
 71        /// <exception cref="JsonException">
 72        ///   <paramref name="utf8Json"/> does not represent a valid single JSON value.
 73        /// </exception>
 74        /// <exception cref="ArgumentException">
 75        ///   <paramref name="options"/> contains unsupported options.
 76        /// </exception>
 77        public static JsonDocument Parse(ReadOnlySequence<byte> utf8Json, JsonDocumentOptions options = default)
 078        {
 079            JsonReaderOptions readerOptions = options.GetReaderOptions();
 80
 081            if (utf8Json.IsSingleSegment)
 082            {
 083                return Parse(utf8Json.First, readerOptions, allowDuplicateProperties: options.AllowDuplicateProperties);
 84            }
 85
 086            int length = checked((int)utf8Json.Length);
 087            byte[] utf8Bytes = ArrayPool<byte>.Shared.Rent(length);
 88
 89            try
 090            {
 091                utf8Json.CopyTo(utf8Bytes.AsSpan());
 092                return Parse(
 093                    utf8Bytes.AsMemory(0, length),
 094                    readerOptions,
 095                    utf8Bytes,
 096                    allowDuplicateProperties: options.AllowDuplicateProperties);
 97            }
 098            catch
 099            {
 100                // Holds document content, clear it before returning it.
 0101                utf8Bytes.AsSpan(0, length).Clear();
 0102                ArrayPool<byte>.Shared.Return(utf8Bytes);
 0103                throw;
 104            }
 0105        }
 106
 107        /// <summary>
 108        ///   Parse a <see cref="Stream"/> as UTF-8 encoded data representing a single JSON value into a
 109        ///   JsonDocument.  The Stream will be read to completion.
 110        /// </summary>
 111        /// <param name="utf8Json">JSON data to parse.</param>
 112        /// <param name="options">Options to control the reader behavior during parsing.</param>
 113        /// <returns>
 114        ///   A JsonDocument representation of the JSON value.
 115        /// </returns>
 116        /// <exception cref="JsonException">
 117        ///   <paramref name="utf8Json"/> does not represent a valid single JSON value.
 118        /// </exception>
 119        /// <exception cref="ArgumentException">
 120        ///   <paramref name="options"/> contains unsupported options.
 121        /// </exception>
 122        public static JsonDocument Parse(Stream utf8Json, JsonDocumentOptions options = default)
 0123        {
 0124            ArgumentNullException.ThrowIfNull(utf8Json);
 125
 0126            ArraySegment<byte> drained = ReadToEnd(utf8Json);
 0127            Debug.Assert(drained.Array != null);
 128            try
 0129            {
 0130                return Parse(
 0131                    drained.AsMemory(),
 0132                    options.GetReaderOptions(),
 0133                    drained.Array,
 0134                    allowDuplicateProperties: options.AllowDuplicateProperties);
 135            }
 0136            catch
 0137            {
 138                // Holds document content, clear it before returning it.
 0139                drained.AsSpan().Clear();
 0140                ArrayPool<byte>.Shared.Return(drained.Array);
 0141                throw;
 142            }
 0143        }
 144
 145        internal static JsonDocument ParseRented(PooledByteBufferWriter utf8Json, JsonDocumentOptions options = default)
 0146        {
 0147            return Parse(
 0148                utf8Json.WrittenMemory,
 0149                options.GetReaderOptions(),
 0150                extraRentedArrayPoolBytes: null,
 0151                extraPooledByteBufferWriter: utf8Json,
 0152                allowDuplicateProperties: options.AllowDuplicateProperties);
 0153        }
 154
 155        internal static JsonDocument ParseValue(Stream utf8Json, JsonDocumentOptions options)
 0156        {
 0157            Debug.Assert(utf8Json != null);
 158
 0159            ArraySegment<byte> drained = ReadToEnd(utf8Json);
 0160            Debug.Assert(drained.Array != null);
 161
 0162            byte[] owned = new byte[drained.Count];
 0163            Buffer.BlockCopy(drained.Array, 0, owned, 0, drained.Count);
 164
 165            // Holds document content, clear it before returning it.
 0166            drained.AsSpan().Clear();
 0167            ArrayPool<byte>.Shared.Return(drained.Array);
 168
 0169            return ParseUnrented(
 0170                owned.AsMemory(),
 0171                options.GetReaderOptions(),
 0172                allowDuplicateProperties: options.AllowDuplicateProperties);
 0173        }
 174
 175        internal static JsonDocument ParseValue(ReadOnlySpan<byte> utf8Json, JsonDocumentOptions options)
 0176        {
 0177            byte[] owned = new byte[utf8Json.Length];
 0178            utf8Json.CopyTo(owned);
 179
 0180            return ParseUnrented(
 0181                owned.AsMemory(),
 0182                options.GetReaderOptions(),
 0183                allowDuplicateProperties: options.AllowDuplicateProperties);
 0184        }
 185
 186        internal static JsonDocument ParseValue(string json, JsonDocumentOptions options)
 0187        {
 0188            Debug.Assert(json != null);
 0189            return ParseValue(json.AsSpan(), options);
 0190        }
 191
 192        /// <summary>
 193        ///   Parse a <see cref="Stream"/> as UTF-8 encoded data representing a single JSON value into a
 194        ///   JsonDocument.  The Stream will be read to completion.
 195        /// </summary>
 196        /// <param name="utf8Json">JSON data to parse.</param>
 197        /// <param name="options">Options to control the reader behavior during parsing.</param>
 198        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
 199        /// <returns>
 200        ///   A Task to produce a JsonDocument representation of the JSON value.
 201        /// </returns>
 202        /// <exception cref="JsonException">
 203        ///   <paramref name="utf8Json"/> does not represent a valid single JSON value.
 204        /// </exception>
 205        /// <exception cref="ArgumentException">
 206        ///   <paramref name="options"/> contains unsupported options.
 207        /// </exception>
 208        public static Task<JsonDocument> ParseAsync(
 209            Stream utf8Json,
 210            JsonDocumentOptions options = default,
 211            CancellationToken cancellationToken = default)
 0212        {
 0213            ArgumentNullException.ThrowIfNull(utf8Json);
 214
 0215            return ParseAsyncCore(utf8Json, options, cancellationToken);
 0216        }
 217
 218        private static async Task<JsonDocument> ParseAsyncCore(
 219            Stream utf8Json,
 220            JsonDocumentOptions options = default,
 221            CancellationToken cancellationToken = default)
 0222        {
 0223            ArraySegment<byte> drained = await ReadToEndAsync(utf8Json, cancellationToken).ConfigureAwait(false);
 0224            Debug.Assert(drained.Array != null);
 225            try
 0226            {
 0227                return Parse(
 0228                    drained.AsMemory(),
 0229                    options.GetReaderOptions(),
 0230                    drained.Array,
 0231                    allowDuplicateProperties: options.AllowDuplicateProperties);
 232            }
 0233            catch
 0234            {
 235                // Holds document content, clear it before returning it.
 0236                drained.AsSpan().Clear();
 0237                ArrayPool<byte>.Shared.Return(drained.Array);
 0238                throw;
 239            }
 0240        }
 241
 242        internal static async Task<JsonDocument> ParseAsyncCoreUnrented(
 243            Stream utf8Json,
 244            JsonDocumentOptions options = default,
 245            CancellationToken cancellationToken = default)
 0246        {
 0247            ArraySegment<byte> drained = await ReadToEndAsync(utf8Json, cancellationToken).ConfigureAwait(false);
 0248            Debug.Assert(drained.Array != null);
 249
 0250            byte[] owned = new byte[drained.Count];
 0251            Buffer.BlockCopy(drained.Array, 0, owned, 0, drained.Count);
 252
 253            // Holds document content, clear it before returning it.
 0254            drained.AsSpan().Clear();
 0255            ArrayPool<byte>.Shared.Return(drained.Array);
 256
 0257            return ParseUnrented(
 0258                owned.AsMemory(),
 0259                options.GetReaderOptions(),
 0260                allowDuplicateProperties: options.AllowDuplicateProperties);
 0261        }
 262
 263        /// <summary>
 264        ///   Parses text representing a single JSON value into a JsonDocument.
 265        /// </summary>
 266        /// <remarks>
 267        ///   The <see cref="ReadOnlyMemory{T}"/> value may be used for the entire lifetime of the
 268        ///   JsonDocument object, and the caller must ensure that the data therein does not change during
 269        ///   the object lifetime.
 270        /// </remarks>
 271        /// <param name="json">JSON text to parse.</param>
 272        /// <param name="options">Options to control the reader behavior during parsing.</param>
 273        /// <returns>
 274        ///   A JsonDocument representation of the JSON value.
 275        /// </returns>
 276        /// <exception cref="JsonException">
 277        ///   <paramref name="json"/> does not represent a valid single JSON value.
 278        /// </exception>
 279        /// <exception cref="ArgumentException">
 280        ///   <paramref name="options"/> contains unsupported options.
 281        /// </exception>
 282        public static JsonDocument Parse([StringSyntax(StringSyntaxAttribute.Json)] ReadOnlyMemory<char> json, JsonDocum
 0283        {
 0284            ReadOnlySpan<char> jsonChars = json.Span;
 0285            int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(jsonChars);
 0286            byte[] utf8Bytes = ArrayPool<byte>.Shared.Rent(expectedByteCount);
 287
 288            try
 0289            {
 0290                int actualByteCount = JsonReaderHelper.GetUtf8FromText(jsonChars, utf8Bytes);
 0291                Debug.Assert(expectedByteCount == actualByteCount);
 292
 0293                return Parse(
 0294                    utf8Bytes.AsMemory(0, actualByteCount),
 0295                    options.GetReaderOptions(),
 0296                    utf8Bytes,
 0297                    allowDuplicateProperties: options.AllowDuplicateProperties);
 298            }
 0299            catch
 0300            {
 301                // Holds document content, clear it before returning it.
 0302                utf8Bytes.AsSpan(0, expectedByteCount).Clear();
 0303                ArrayPool<byte>.Shared.Return(utf8Bytes);
 0304                throw;
 305            }
 0306        }
 307
 308        internal static JsonDocument ParseValue(ReadOnlySpan<char> json, JsonDocumentOptions options)
 0309        {
 0310            int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(json);
 311            byte[] owned;
 0312            byte[] utf8Bytes = ArrayPool<byte>.Shared.Rent(expectedByteCount);
 313
 314            try
 0315            {
 0316                int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8Bytes);
 0317                Debug.Assert(expectedByteCount == actualByteCount);
 318
 0319                owned = new byte[actualByteCount];
 0320                Buffer.BlockCopy(utf8Bytes, 0, owned, 0, actualByteCount);
 0321            }
 322            finally
 0323            {
 324                // Holds document content, clear it before returning it.
 0325                utf8Bytes.AsSpan(0, expectedByteCount).Clear();
 0326                ArrayPool<byte>.Shared.Return(utf8Bytes);
 0327            }
 328
 0329            return ParseUnrented(
 0330                owned.AsMemory(),
 0331                options.GetReaderOptions(),
 0332                allowDuplicateProperties: options.AllowDuplicateProperties);
 0333        }
 334
 335        /// <summary>
 336        ///   Parses text representing a single JSON value into a JsonDocument.
 337        /// </summary>
 338        /// <param name="json">JSON text to parse.</param>
 339        /// <param name="options">Options to control the reader behavior during parsing.</param>
 340        /// <returns>
 341        ///   A JsonDocument representation of the JSON value.
 342        /// </returns>
 343        /// <exception cref="JsonException">
 344        ///   <paramref name="json"/> does not represent a valid single JSON value.
 345        /// </exception>
 346        /// <exception cref="ArgumentException">
 347        ///   <paramref name="options"/> contains unsupported options.
 348        /// </exception>
 349        public static JsonDocument Parse([StringSyntax(StringSyntaxAttribute.Json)] string json, JsonDocumentOptions opt
 0350        {
 0351            ArgumentNullException.ThrowIfNull(json);
 352
 0353            return Parse(json.AsMemory(), options);
 0354        }
 355
 356        /// <summary>
 357        ///   Attempts to parse one JSON value (including objects or arrays) from the provided reader.
 358        /// </summary>
 359        /// <param name="reader">The reader to read.</param>
 360        /// <param name="document">Receives the parsed document.</param>
 361        /// <returns>
 362        ///   <see langword="true"/> if a value was read and parsed into a JsonDocument,
 363        ///   <see langword="false"/> if the reader ran out of data while parsing.
 364        ///   All other situations result in an exception being thrown.
 365        /// </returns>
 366        /// <remarks>
 367        ///   <para>
 368        ///     If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/>
 369        ///     is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the
 370        ///     reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine
 371        ///     the start of the value.
 372        ///   </para>
 373        ///
 374        ///   <para>
 375        ///     Upon completion of this method, <paramref name="reader"/> will be positioned at the
 376        ///     final token in the JSON value.  If an exception is thrown, or <see langword="false"/>
 377        ///     is returned, the reader is reset to the state it was in when the method was called.
 378        ///   </para>
 379        ///
 380        ///   <para>
 381        ///     This method makes a copy of the data the reader acted on, so there is no caller
 382        ///     requirement to maintain data integrity beyond the return of this method.
 383        ///   </para>
 384        /// </remarks>
 385        /// <exception cref="ArgumentException">
 386        ///   <paramref name="reader"/> is using unsupported options.
 387        /// </exception>
 388        /// <exception cref="ArgumentException">
 389        ///   The current <paramref name="reader"/> token does not start or represent a value.
 390        /// </exception>
 391        /// <exception cref="JsonException">
 392        ///   A value could not be read from the reader.
 393        /// </exception>
 394        public static bool TryParseValue(ref Utf8JsonReader reader, [NotNullWhen(true)] out JsonDocument? document)
 0395        {
 0396            return TryParseValue(ref reader, out document, shouldThrow: false, useArrayPools: true);
 0397        }
 398
 399        /// <summary>
 400        ///   Parses one JSON value (including objects or arrays) from the provided reader.
 401        /// </summary>
 402        /// <param name="reader">The reader to read.</param>
 403        /// <returns>
 404        ///   A JsonDocument representing the value (and nested values) read from the reader.
 405        /// </returns>
 406        /// <remarks>
 407        ///   <para>
 408        ///     If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/>
 409        ///     is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the
 410        ///     reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine
 411        ///     the start of the value.
 412        ///   </para>
 413        ///
 414        ///   <para>
 415        ///     Upon completion of this method, <paramref name="reader"/> will be positioned at the
 416        ///     final token in the JSON value. If an exception is thrown, the reader is reset to
 417        ///     the state it was in when the method was called.
 418        ///   </para>
 419        ///
 420        ///   <para>
 421        ///     This method makes a copy of the data the reader acted on, so there is no caller
 422        ///     requirement to maintain data integrity beyond the return of this method.
 423        ///   </para>
 424        /// </remarks>
 425        /// <exception cref="ArgumentException">
 426        ///   <paramref name="reader"/> is using unsupported options.
 427        /// </exception>
 428        /// <exception cref="ArgumentException">
 429        ///   The current <paramref name="reader"/> token does not start or represent a value.
 430        /// </exception>
 431        /// <exception cref="JsonException">
 432        ///   A value could not be read from the reader.
 433        /// </exception>
 434        public static JsonDocument ParseValue(ref Utf8JsonReader reader) =>
 28435            ParseValue(ref reader, allowDuplicateProperties: true);
 436
 437        internal static JsonDocument ParseValue(ref Utf8JsonReader reader, bool allowDuplicateProperties)
 690438        {
 690439            bool ret = TryParseValue(ref reader, out JsonDocument? document, shouldThrow: true, useArrayPools: true, all
 440
 478441            Debug.Assert(ret, "TryParseValue returned false with shouldThrow: true.");
 478442            Debug.Assert(document != null, "null document returned with shouldThrow: true.");
 478443            return document;
 478444        }
 445
 446        internal static bool TryParseValue(
 447            ref Utf8JsonReader reader,
 448            [NotNullWhen(true)] out JsonDocument? document,
 449            bool shouldThrow,
 450            bool useArrayPools,
 451            bool allowDuplicateProperties = true)
 2642452        {
 2642453            JsonReaderState state = reader.CurrentState;
 2642454            CheckSupportedOptions(state.Options, nameof(reader));
 455
 456            // Value copy to overwrite the ref on an exception and undo the destructive reads.
 2642457            Utf8JsonReader restore = reader;
 458
 2642459            ReadOnlySpan<byte> valueSpan = default;
 2642460            ReadOnlySequence<byte> valueSequence = default;
 461
 462            try
 2642463            {
 2642464                switch (reader.TokenType)
 465                {
 466                    // A new reader was created and has never been read,
 467                    // so we need to move to the first token.
 468                    // (or a reader has terminated and we're about to throw)
 469                    case JsonTokenType.None:
 470                    // Using a reader loop the caller has identified a property they wish to
 471                    // hydrate into a JsonDocument. Move to the value first.
 472                    case JsonTokenType.PropertyName:
 28473                    {
 28474                        if (!reader.Read())
 0475                        {
 0476                            if (shouldThrow)
 0477                            {
 0478                                ThrowHelper.ThrowJsonReaderException(
 0479                                    ref reader,
 0480                                    ExceptionResource.ExpectedJsonTokens);
 481                            }
 482
 0483                            reader = restore;
 0484                            document = null;
 0485                            return false;
 486                        }
 487
 28488                        break;
 489                    }
 490                }
 491
 2642492                switch (reader.TokenType)
 493                {
 494                    // Any of the "value start" states are acceptable.
 495                    case JsonTokenType.StartObject:
 496                    case JsonTokenType.StartArray:
 1380497                    {
 1380498                        long startingOffset = reader.TokenStartIndex;
 499
 1380500                        if (!reader.TrySkip())
 0501                        {
 0502                            if (shouldThrow)
 0503                            {
 0504                                ThrowHelper.ThrowJsonReaderException(
 0505                                    ref reader,
 0506                                    ExceptionResource.ExpectedJsonTokens);
 507                            }
 508
 0509                            reader = restore;
 0510                            document = null;
 0511                            return false;
 512                        }
 513
 138514                        long totalLength = reader.BytesConsumed - startingOffset;
 138515                        ReadOnlySequence<byte> sequence = reader.OriginalSequence;
 516
 138517                        if (sequence.IsEmpty)
 69518                        {
 69519                            valueSpan = reader.OriginalSpan.Slice(
 69520                                checked((int)startingOffset),
 69521                                checked((int)totalLength));
 69522                        }
 523                        else
 69524                        {
 69525                            valueSequence = sequence.Slice(startingOffset, totalLength);
 69526                        }
 527
 138528                        Debug.Assert(
 138529                            reader.TokenType == JsonTokenType.EndObject ||
 138530                            reader.TokenType == JsonTokenType.EndArray);
 531
 138532                        break;
 533                    }
 534
 535                    case JsonTokenType.False:
 536                    case JsonTokenType.True:
 537                    case JsonTokenType.Null:
 0538                        if (useArrayPools)
 0539                        {
 0540                            if (reader.HasValueSequence)
 0541                            {
 0542                                valueSequence = reader.ValueSequence;
 0543                            }
 544                            else
 0545                            {
 0546                                valueSpan = reader.ValueSpan;
 0547                            }
 548
 0549                            break;
 550                        }
 551
 0552                        document = CreateForLiteral(reader.TokenType);
 0553                        return true;
 554
 555                    case JsonTokenType.Number:
 416556                    {
 416557                        if (reader.HasValueSequence)
 118558                        {
 118559                            valueSequence = reader.ValueSequence;
 118560                        }
 561                        else
 298562                        {
 298563                            valueSpan = reader.ValueSpan;
 298564                        }
 565
 416566                        break;
 567                    }
 568
 569                    // String's ValueSequence/ValueSpan omits the quotes, we need them back.
 570                    case JsonTokenType.String:
 846571                    {
 846572                        ReadOnlySequence<byte> sequence = reader.OriginalSequence;
 573
 846574                        if (sequence.IsEmpty)
 423575                        {
 576                            // Since the quoted string fit in a ReadOnlySpan originally
 577                            // the contents length plus the two quotes can't overflow.
 423578                            int payloadLength = reader.ValueSpan.Length + 2;
 423579                            Debug.Assert(payloadLength > 1);
 580
 423581                            ReadOnlySpan<byte> readerSpan = reader.OriginalSpan;
 582
 423583                            Debug.Assert(
 423584                                readerSpan[(int)reader.TokenStartIndex] == (byte)'"',
 423585                                $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}");
 586
 423587                            Debug.Assert(
 423588                                readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"',
 423589                                $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]
 590
 423591                            valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength);
 423592                        }
 593                        else
 423594                        {
 423595                            long payloadLength = 2;
 596
 423597                            if (reader.HasValueSequence)
 281598                            {
 281599                                payloadLength += reader.ValueSequence.Length;
 281600                            }
 601                            else
 142602                            {
 142603                                payloadLength += reader.ValueSpan.Length;
 142604                            }
 605
 423606                            valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength);
 423607                            Debug.Assert(
 423608                                valueSequence.First.Span[0] == (byte)'"',
 423609                                $"Calculated sequence starts with {valueSequence.First.Span[0]}");
 610
 423611                            Debug.Assert(
 423612                                valueSequence.ToArray()[payloadLength - 1] == (byte)'"',
 423613                                $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}");
 423614                        }
 615
 846616                        break;
 617                    }
 618                    default:
 0619                    {
 0620                        if (shouldThrow)
 0621                        {
 622                            // Default case would only hit if TokenType equals JsonTokenType.EndObject or JsonTokenType.
 0623                            Debug.Assert(!reader.HasValueSequence);
 0624                            byte displayByte = reader.ValueSpan[0];
 625
 0626                            ThrowHelper.ThrowJsonReaderException(
 0627                                ref reader,
 0628                                ExceptionResource.ExpectedStartOfValueNotFound,
 0629                                displayByte);
 630                        }
 631
 0632                        reader = restore;
 0633                        document = null;
 0634                        return false;
 635                    }
 636                }
 1400637            }
 1242638            catch
 1242639            {
 1242640                reader = restore;
 1242641                throw;
 642            }
 643
 1400644            int length = valueSpan.IsEmpty ? checked((int)valueSequence.Length) : valueSpan.Length;
 1400645            if (useArrayPools)
 478646            {
 478647                byte[] rented = ArrayPool<byte>.Shared.Rent(length);
 478648                Span<byte> rentedSpan = rented.AsSpan(0, length);
 649
 650                try
 478651                {
 478652                    if (valueSpan.IsEmpty)
 199653                    {
 199654                        valueSequence.CopyTo(rentedSpan);
 199655                    }
 656                    else
 279657                    {
 279658                        valueSpan.CopyTo(rentedSpan);
 279659                    }
 660
 478661                    document = Parse(rented.AsMemory(0, length), state.Options, rented, allowDuplicateProperties: allowD
 478662                }
 0663                catch
 0664                {
 665                    // This really shouldn't happen since the document was already checked
 666                    // for consistency by Skip.  But if data mutations happened just after
 667                    // the calls to Read then the copy may not be valid.
 0668                    rentedSpan.Clear();
 0669                    ArrayPool<byte>.Shared.Return(rented);
 0670                    throw;
 671                }
 478672            }
 673            else
 922674            {
 675                byte[] owned;
 676
 922677                if (valueSpan.IsEmpty)
 411678                {
 411679                    owned = valueSequence.ToArray();
 411680                }
 681                else
 511682                {
 511683                    owned = valueSpan.ToArray();
 511684                }
 685
 922686                document = ParseUnrented(owned, state.Options, reader.TokenType, allowDuplicateProperties: allowDuplicat
 922687            }
 688
 1400689            return true;
 1400690        }
 691
 692        private static JsonDocument CreateForLiteral(JsonTokenType tokenType)
 0693        {
 0694            switch (tokenType)
 695            {
 696                case JsonTokenType.False:
 0697                    s_falseLiteral ??= Create(JsonConstants.FalseValue.ToArray());
 0698                    return s_falseLiteral;
 699                case JsonTokenType.True:
 0700                    s_trueLiteral ??= Create(JsonConstants.TrueValue.ToArray());
 0701                    return s_trueLiteral;
 702                default:
 0703                    Debug.Assert(tokenType == JsonTokenType.Null);
 0704                    s_nullLiteral ??= Create(JsonConstants.NullValue.ToArray());
 0705                    return s_nullLiteral;
 706            }
 707
 708            JsonDocument Create(byte[] utf8Json)
 0709            {
 0710                MetadataDb database = MetadataDb.CreateLocked(utf8Json.Length);
 0711                database.Append(tokenType, startLocation: 0, utf8Json.Length);
 0712                return new JsonDocument(utf8Json, database, isDisposable: false);
 0713            }
 0714        }
 715
 716        private static JsonDocument Parse(
 717            ReadOnlyMemory<byte> utf8Json,
 718            JsonReaderOptions readerOptions,
 719            byte[]? extraRentedArrayPoolBytes = null,
 720            PooledByteBufferWriter? extraPooledByteBufferWriter = null,
 721            bool allowDuplicateProperties = true)
 478722        {
 478723            ReadOnlySpan<byte> utf8JsonSpan = utf8Json.Span;
 478724            var database = MetadataDb.CreateRented(utf8Json.Length, convertToAlloc: false);
 478725            var stack = new StackRowStack(JsonDocumentOptions.DefaultMaxDepth * StackRow.Size);
 726            JsonDocument document;
 727
 728            try
 478729            {
 478730                Parse(utf8JsonSpan, readerOptions, ref database, ref stack);
 478731                document = new JsonDocument(utf8Json, database, extraRentedArrayPoolBytes, extraPooledByteBufferWriter, 
 732
 478733                if (!allowDuplicateProperties)
 0734                {
 0735                    ValidateNoDuplicateProperties(document);
 0736                }
 478737            }
 0738            catch
 0739            {
 740                // The caller returns any resources they rented, so all we need to do is dispose the database.
 741                // Specifically: don't dispose the document as that will result in double return of the rented array.
 0742                database.Dispose();
 0743                throw;
 744            }
 745            finally
 478746            {
 478747                stack.Dispose();
 478748            }
 749
 478750            return document;
 478751        }
 752
 753        private static JsonDocument ParseUnrented(
 754            ReadOnlyMemory<byte> utf8Json,
 755            JsonReaderOptions readerOptions,
 756            JsonTokenType tokenType = JsonTokenType.None,
 757            bool allowDuplicateProperties = true)
 922758        {
 759            // These tokens should already have been processed.
 922760            Debug.Assert(
 922761                tokenType != JsonTokenType.Null &&
 922762                tokenType != JsonTokenType.False &&
 922763                tokenType != JsonTokenType.True);
 764
 922765            ReadOnlySpan<byte> utf8JsonSpan = utf8Json.Span;
 766            MetadataDb database;
 767
 922768            if (tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number)
 812769            {
 770                // For primitive types, we can avoid renting MetadataDb and creating StackRowStack.
 812771                database = MetadataDb.CreateLocked(utf8Json.Length);
 812772                StackRowStack stack = default;
 812773                Parse(utf8JsonSpan, readerOptions, ref database, ref stack);
 812774            }
 775            else
 110776            {
 110777                database = MetadataDb.CreateRented(utf8Json.Length, convertToAlloc: true);
 110778                var stack = new StackRowStack(JsonDocumentOptions.DefaultMaxDepth * StackRow.Size);
 779                try
 110780                {
 110781                    Parse(utf8JsonSpan, readerOptions, ref database, ref stack);
 110782                }
 783                finally
 110784                {
 110785                    stack.Dispose();
 110786                }
 110787            }
 788
 922789            JsonDocument document = new JsonDocument(utf8Json, database, isDisposable: false);
 790
 922791            if (!allowDuplicateProperties)
 0792            {
 0793                ValidateNoDuplicateProperties(document);
 0794            }
 795
 922796            return document;
 922797        }
 798
 799        private static ArraySegment<byte> ReadToEnd(Stream stream)
 0800        {
 0801            int written = 0;
 0802            byte[]? rented = null;
 803
 0804            ReadOnlySpan<byte> utf8Bom = JsonConstants.Utf8Bom;
 805
 806            try
 0807            {
 0808                if (stream.CanSeek)
 0809                {
 810                    // Ask for 1 more than the length to avoid resizing later,
 811                    // which is unnecessary in the common case where the stream length doesn't change.
 0812                    long expectedLength = Math.Max(utf8Bom.Length, stream.Length - stream.Position) + 1;
 0813                    rented = ArrayPool<byte>.Shared.Rent(checked((int)expectedLength));
 0814                }
 815                else
 0816                {
 0817                    rented = ArrayPool<byte>.Shared.Rent(UnseekableStreamInitialRentSize);
 0818                }
 819
 820                int lastRead;
 821
 822                // Read up to 3 bytes to see if it's the UTF-8 BOM
 823                do
 0824                {
 825                    // No need for checking for growth, the minimal rent sizes both guarantee it'll fit.
 0826                    Debug.Assert(rented.Length >= utf8Bom.Length);
 827
 0828                    lastRead = stream.Read(
 0829                        rented,
 0830                        written,
 0831                        utf8Bom.Length - written);
 832
 0833                    written += lastRead;
 0834                } while (lastRead > 0 && written < utf8Bom.Length);
 835
 836                // If we have 3 bytes, and they're the BOM, reset the write position to 0.
 0837                if (written == utf8Bom.Length &&
 0838                    utf8Bom.SequenceEqual(rented.AsSpan(0, utf8Bom.Length)))
 0839                {
 0840                    written = 0;
 0841                }
 842
 843                do
 0844                {
 0845                    if (rented.Length == written)
 0846                    {
 0847                        byte[] toReturn = rented;
 0848                        rented = ArrayPool<byte>.Shared.Rent(checked(toReturn.Length * 2));
 0849                        Buffer.BlockCopy(toReturn, 0, rented, 0, toReturn.Length);
 850                        // Holds document content, clear it.
 0851                        ArrayPool<byte>.Shared.Return(toReturn, clearArray: true);
 0852                    }
 853
 0854                    lastRead = stream.Read(rented, written, rented.Length - written);
 0855                    written += lastRead;
 0856                } while (lastRead > 0);
 857
 0858                return new ArraySegment<byte>(rented, 0, written);
 859            }
 0860            catch
 0861            {
 0862                if (rented != null)
 0863                {
 864                    // Holds document content, clear it before returning it.
 0865                    rented.AsSpan(0, written).Clear();
 0866                    ArrayPool<byte>.Shared.Return(rented);
 0867                }
 868
 0869                throw;
 870            }
 0871        }
 872
 873        private static async
 874#if NET
 875            ValueTask<ArraySegment<byte>>
 876#else
 877            Task<ArraySegment<byte>>
 878#endif
 879            ReadToEndAsync(
 880            Stream stream,
 881            CancellationToken cancellationToken)
 0882        {
 0883            int written = 0;
 0884            byte[]? rented = null;
 885
 886            try
 0887            {
 888                // Save the length to a local to be reused across awaits.
 0889                int utf8BomLength = JsonConstants.Utf8Bom.Length;
 890
 0891                if (stream.CanSeek)
 0892                {
 893                    // Ask for 1 more than the length to avoid resizing later,
 894                    // which is unnecessary in the common case where the stream length doesn't change.
 0895                    long expectedLength = Math.Max(utf8BomLength, stream.Length - stream.Position) + 1;
 0896                    rented = ArrayPool<byte>.Shared.Rent(checked((int)expectedLength));
 0897                }
 898                else
 0899                {
 0900                    rented = ArrayPool<byte>.Shared.Rent(UnseekableStreamInitialRentSize);
 0901                }
 902
 903                int lastRead;
 904
 905                // Read up to 3 bytes to see if it's the UTF-8 BOM
 906                do
 0907                {
 908                    // No need for checking for growth, the minimal rent sizes both guarantee it'll fit.
 0909                    Debug.Assert(rented.Length >= JsonConstants.Utf8Bom.Length);
 910
 0911                    lastRead = await stream.ReadAsync(rented.AsMemory(written, utf8BomLength - written), cancellationTok
 912
 0913                    written += lastRead;
 0914                } while (lastRead > 0 && written < utf8BomLength);
 915
 916                // If we have 3 bytes, and they're the BOM, reset the write position to 0.
 0917                if (written == utf8BomLength &&
 0918                    JsonConstants.Utf8Bom.SequenceEqual(rented.AsSpan(0, utf8BomLength)))
 0919                {
 0920                    written = 0;
 0921                }
 922
 923                do
 0924                {
 0925                    if (rented.Length == written)
 0926                    {
 0927                        byte[] toReturn = rented;
 0928                        rented = ArrayPool<byte>.Shared.Rent(toReturn.Length * 2);
 0929                        Buffer.BlockCopy(toReturn, 0, rented, 0, toReturn.Length);
 930                        // Holds document content, clear it.
 0931                        ArrayPool<byte>.Shared.Return(toReturn, clearArray: true);
 0932                    }
 933
 0934                    lastRead = await stream.ReadAsync(rented.AsMemory(written), cancellationToken).ConfigureAwait(false)
 935
 0936                    written += lastRead;
 937
 0938                } while (lastRead > 0);
 939
 0940                return new ArraySegment<byte>(rented, 0, written);
 941            }
 0942            catch
 0943            {
 0944                if (rented != null)
 0945                {
 946                    // Holds document content, clear it before returning it.
 0947                    rented.AsSpan(0, written).Clear();
 0948                    ArrayPool<byte>.Shared.Return(rented);
 0949                }
 950
 0951                throw;
 952            }
 0953        }
 954    }
 955}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.PropertyNameSet.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.Collections.Generic;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Runtime.CompilerServices;
 9
 10namespace System.Text.Json
 11{
 12    public sealed partial class JsonDocument
 13    {
 14        // Ensure this stays on the stack by making it a ref struct.
 15        private ref struct PropertyNameSet : IDisposable
 16        {
 17            // Data structure to track property names in an object while deserializing
 18            // into a JsonDocument and validate that there are no duplicates. A small
 19            // array is used when the number of properties is small and no properties
 20            // are escaped. Otherwise a hash set is used.
 21
 22            private HashSet<ReadOnlyMemory<byte>>? _hashSet;
 23
 24            private const int ArraySetThreshold = 16;
 25            private int _arraySetCount;
 026            private bool _useArraySet = true;
 27
 28#if NET
 29            [InlineArray(ArraySetThreshold)]
 30            private struct InlineRangeArray16
 31            {
 32                private (int Start, int Length) _element0;
 33            }
 34
 35            private InlineRangeArray16 _arraySet;
 36#else
 37            private readonly (int Start, int Length)[] _arraySet = new (int Start, int Length)[ArraySetThreshold];
 38#endif
 39
 40            public PropertyNameSet()
 041            {
 042            }
 43
 44            internal void SetCapacity(int capacity)
 045            {
 046                if (capacity <= ArraySetThreshold)
 047                {
 048                    _useArraySet = true;
 049                }
 50                else
 051                {
 052                    _useArraySet = false;
 053                    if (_hashSet is null)
 054                    {
 055                        _hashSet = new HashSet<ReadOnlyMemory<byte>>(
 056#if NET
 057                            capacity,
 058#endif
 059                            PropertyNameComparer.Instance);
 060                    }
 61                    else
 062                    {
 63#if NET
 064                        _hashSet.EnsureCapacity(capacity);
 65#endif
 066                    }
 067                }
 068            }
 69
 70            internal void AddPropertyName(JsonProperty property, JsonDocument document)
 071            {
 072                DbRow dbRow = document._parsedData.Get(property.Value.MetadataDbIndex - DbRow.Size);
 073                Debug.Assert(dbRow.TokenType is JsonTokenType.PropertyName);
 74
 075                ReadOnlyMemory<byte> utf8Json = document._utf8Json;
 076                ReadOnlyMemory<byte> propertyName = utf8Json.Slice(dbRow.Location, dbRow.SizeOrLength);
 77
 078                if (dbRow.HasComplexChildren)
 079                {
 080                    SwitchToHashSet(utf8Json);
 081                    propertyName = JsonReaderHelper.GetUnescaped(propertyName.Span);
 082                }
 83
 084                if (_useArraySet)
 085                {
 086                    for (int i = 0; i < _arraySetCount; i++)
 087                    {
 088                        (int Start, int Length) range = _arraySet[i];
 089                        ReadOnlySpan<byte> previousPropertyName = utf8Json.Span.Slice(range.Start, range.Length);
 90
 091                        if (previousPropertyName.SequenceEqual(propertyName.Span))
 092                        {
 093                            ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyName.Span);
 094                        }
 095                    }
 96
 097                    _arraySet[_arraySetCount] = (dbRow.Location, dbRow.SizeOrLength);
 098                    _arraySetCount++;
 099                }
 100                else
 0101                {
 0102                    Debug.Assert(_hashSet is not null);
 103
 0104                    if (!_hashSet.Add(propertyName))
 0105                    {
 0106                        ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyName.Span);
 0107                    }
 0108                }
 0109            }
 110
 111            private void SwitchToHashSet(ReadOnlyMemory<byte> utf8Json)
 0112            {
 0113                if (_useArraySet)
 0114                {
 0115                    _hashSet ??= new HashSet<ReadOnlyMemory<byte>>(
 0116#if NET
 0117                        ArraySetThreshold,
 0118#endif
 0119                        PropertyNameComparer.Instance);
 120
 0121                    for (int i = 0; i < _arraySetCount; i++)
 0122                    {
 0123                        (int Start, int Length) range = _arraySet[i];
 0124                        ReadOnlyMemory<byte> propertyName = utf8Json.Slice(range.Start, range.Length);
 0125                        bool success = _hashSet.Add(propertyName);
 0126                        Debug.Assert(success, $"Property name {propertyName} should not already exist in the set.");
 0127                    }
 128
 0129                    _useArraySet = false;
 0130                    _arraySetCount = 0;
 0131                }
 0132            }
 133
 134            internal void Reset()
 0135            {
 0136                _hashSet?.Clear();
 0137                _arraySetCount = 0;
 0138            }
 139
 140            public readonly void Dispose()
 0141            {
 0142            }
 143
 144            private sealed class PropertyNameComparer : IEqualityComparer<ReadOnlyMemory<byte>>
 145            {
 0146                internal static readonly PropertyNameComparer Instance = new();
 147
 148                public bool Equals(ReadOnlyMemory<byte> left, ReadOnlyMemory<byte> right) =>
 0149                    left.Length == right.Length && left.Span.SequenceEqual(right.Span);
 150
 151                public int GetHashCode(ReadOnlyMemory<byte> name)
 0152                {
 153                    // Marvin is the currently used hash algorithm for string comparisons.
 154                    // The seed is unique to this process so an item's hash code can't easily be
 155                    // discovered by an adversary trying to perform a denial of service attack.
 0156                    return Marvin.ComputeHash32(name.Span, Marvin.DefaultSeed);
 0157                }
 158            }
 159        }
 160    }
 161}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.StackRow.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.Diagnostics;
 5using System.Runtime.InteropServices;
 6
 7namespace System.Text.Json
 8{
 9    public sealed partial class JsonDocument
 10    {
 11        // SizeOrLength - offset - 0 - size - 4
 12        // NumberOfRows - offset - 4 - size - 4
 13        [StructLayout(LayoutKind.Sequential)]
 14        private readonly struct StackRow
 15        {
 16            internal const int Size = 8;
 17
 18            internal readonly int SizeOrLength;
 19            internal readonly int NumberOfRows;
 20
 21            internal StackRow(int sizeOrLength = 0, int numberOfRows = -1)
 13822            {
 13823                Debug.Assert(sizeOrLength >= 0);
 13824                Debug.Assert(numberOfRows >= -1);
 25
 13826                SizeOrLength = sizeOrLength;
 13827                NumberOfRows = numberOfRows;
 13828            }
 29        }
 30    }
 31}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.StackRowStack.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.InteropServices;
 7
 8// We need to target netstandard2.0, so keep using ref for MemoryMarshal.Write
 9// CS9191: The 'ref' modifier for argument 2 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' 
 10#pragma warning disable CS9191
 11
 12namespace System.Text.Json
 13{
 14    public sealed partial class JsonDocument
 15    {
 16        private struct StackRowStack : IDisposable
 17        {
 18            private byte[] _rentedBuffer;
 19            private int _topOfStack;
 20
 21            public StackRowStack(int initialSize)
 58822            {
 58823                _rentedBuffer = ArrayPool<byte>.Shared.Rent(initialSize);
 58824                _topOfStack = _rentedBuffer.Length;
 58825            }
 26
 27            public void Dispose()
 58828            {
 58829                byte[] toReturn = _rentedBuffer;
 58830                _rentedBuffer = null!;
 58831                _topOfStack = 0;
 32
 58833                if (toReturn != null)
 58834                {
 35                    // The data in this rented buffer only conveys the positions and
 36                    // lengths of tokens in a document, but no content; so it does not
 37                    // need to be cleared.
 58838                    ArrayPool<byte>.Shared.Return(toReturn);
 58839                }
 58840            }
 41
 42            internal void Push(StackRow row)
 13843            {
 13844                if (_topOfStack < StackRow.Size)
 045                {
 046                    Enlarge();
 047                }
 48
 13849                _topOfStack -= StackRow.Size;
 13850                MemoryMarshal.Write(_rentedBuffer.AsSpan(_topOfStack), ref row);
 13851            }
 52
 53            internal StackRow Pop()
 13854            {
 13855                Debug.Assert(_rentedBuffer != null);
 13856                Debug.Assert(_topOfStack <= _rentedBuffer!.Length - StackRow.Size);
 57
 13858                StackRow row = MemoryMarshal.Read<StackRow>(_rentedBuffer.AsSpan(_topOfStack));
 13859                _topOfStack += StackRow.Size;
 13860                return row;
 13861            }
 62
 63            private void Enlarge()
 064            {
 065                byte[] toReturn = _rentedBuffer;
 066                _rentedBuffer = ArrayPool<byte>.Shared.Rent(toReturn.Length * 2);
 67
 068                Buffer.BlockCopy(
 069                    toReturn,
 070                    _topOfStack,
 071                    _rentedBuffer,
 072                    _rentedBuffer.Length - toReturn.Length + _topOfStack,
 073                    toReturn.Length - _topOfStack);
 74
 075                _topOfStack += _rentedBuffer.Length - toReturn.Length;
 76
 77                // The data in this rented buffer only conveys the positions and
 78                // lengths of tokens in a document, but no content; so it does not
 79                // need to be cleared.
 080                ArrayPool<byte>.Shared.Return(toReturn);
 081            }
 82        }
 83    }
 84}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Document\JsonDocument.TryGetProperty.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;
 6
 7namespace System.Text.Json
 8{
 9    public sealed partial class JsonDocument
 10    {
 11        internal bool TryGetNamedPropertyValue(int index, ReadOnlySpan<char> propertyName, out JsonElement value)
 012        {
 013            CheckNotDisposed();
 14
 015            DbRow row = _parsedData.Get(index);
 16
 017            CheckExpectedType(JsonTokenType.StartObject, row.TokenType);
 18
 19            // Only one row means it was EndObject.
 020            if (row.NumberOfRows == 1)
 021            {
 022                value = default;
 023                return false;
 24            }
 25
 026            int maxBytes = JsonReaderHelper.s_utf8Encoding.GetMaxByteCount(propertyName.Length);
 027            int startIndex = index + DbRow.Size;
 028            int endIndex = checked(row.NumberOfRows * DbRow.Size + index);
 29
 030            if (maxBytes < JsonConstants.StackallocByteThreshold)
 031            {
 032                Span<byte> utf8Name = stackalloc byte[JsonConstants.StackallocByteThreshold];
 033                int len = JsonReaderHelper.GetUtf8FromText(propertyName, utf8Name);
 034                utf8Name = utf8Name.Slice(0, len);
 35
 036                return TryGetNamedPropertyValue(
 037                    startIndex,
 038                    endIndex,
 039                    utf8Name,
 040                    out value);
 41            }
 42
 43            // Unescaping the property name will make the string shorter (or the same)
 44            // So the first viable candidate is one whose length in bytes matches, or
 45            // exceeds, our length in chars.
 46            //
 47            // The maximal escaping seems to be 6 -> 1 ("\u0030" => "0"), but just transcode
 48            // and switch once one viable long property is found.
 49
 050            int minBytes = propertyName.Length;
 51            // Move to the row before the EndObject
 052            int candidateIndex = endIndex - DbRow.Size;
 53
 054            while (candidateIndex > index)
 055            {
 056                int passedIndex = candidateIndex;
 57
 058                row = _parsedData.Get(candidateIndex);
 059                Debug.Assert(row.TokenType != JsonTokenType.PropertyName);
 60
 61                // Move before the value
 062                if (row.IsSimpleValue)
 063                {
 064                    candidateIndex -= DbRow.Size;
 065                }
 66                else
 067                {
 068                    Debug.Assert(row.NumberOfRows > 0);
 069                    candidateIndex -= DbRow.Size * (row.NumberOfRows + 1);
 070                }
 71
 072                row = _parsedData.Get(candidateIndex);
 073                Debug.Assert(row.TokenType == JsonTokenType.PropertyName);
 74
 075                if (row.SizeOrLength >= minBytes)
 076                {
 077                    byte[] tmpUtf8 = ArrayPool<byte>.Shared.Rent(maxBytes);
 078                    Span<byte> utf8Name = default;
 79
 80                    try
 081                    {
 082                        int len = JsonReaderHelper.GetUtf8FromText(propertyName, tmpUtf8);
 083                        utf8Name = tmpUtf8.AsSpan(0, len);
 84
 085                        return TryGetNamedPropertyValue(
 086                            startIndex,
 087                            passedIndex + DbRow.Size,
 088                            utf8Name,
 089                            out value);
 90                    }
 91                    finally
 092                    {
 93                        // While property names aren't usually a secret, they also usually
 94                        // aren't long enough to end up in the rented buffer transcode path.
 95                        //
 96                        // On the basis that this is user data, go ahead and clear it.
 097                        utf8Name.Clear();
 098                        ArrayPool<byte>.Shared.Return(tmpUtf8);
 099                    }
 100                }
 101
 102                // Move to the previous value
 0103                candidateIndex -= DbRow.Size;
 0104            }
 105
 106            // None of the property names were within the range that the UTF-8 encoding would have been.
 0107            value = default;
 0108            return false;
 0109        }
 110
 111        internal bool TryGetNamedPropertyValue(int index, ReadOnlySpan<byte> propertyName, out JsonElement value)
 0112        {
 0113            CheckNotDisposed();
 114
 0115            DbRow row = _parsedData.Get(index);
 116
 0117            CheckExpectedType(JsonTokenType.StartObject, row.TokenType);
 118
 119            // Only one row means it was EndObject.
 0120            if (row.NumberOfRows == 1)
 0121            {
 0122                value = default;
 0123                return false;
 124            }
 125
 0126            int endIndex = checked(row.NumberOfRows * DbRow.Size + index);
 127
 0128            return TryGetNamedPropertyValue(
 0129                index + DbRow.Size,
 0130                endIndex,
 0131                propertyName,
 0132                out value);
 0133        }
 134
 135        private bool TryGetNamedPropertyValue(
 136            int startIndex,
 137            int endIndex,
 138            ReadOnlySpan<byte> propertyName,
 139            out JsonElement value)
 0140        {
 0141            ReadOnlySpan<byte> documentSpan = _utf8Json.Span;
 0142            Span<byte> utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocByteThreshold];
 143
 144            // Move to the row before the EndObject
 0145            int index = endIndex - DbRow.Size;
 146
 0147            while (index > startIndex)
 0148            {
 0149                DbRow row = _parsedData.Get(index);
 0150                Debug.Assert(row.TokenType != JsonTokenType.PropertyName);
 151
 152                // Move before the value
 0153                if (row.IsSimpleValue)
 0154                {
 0155                    index -= DbRow.Size;
 0156                }
 157                else
 0158                {
 0159                    Debug.Assert(row.NumberOfRows > 0);
 0160                    index -= DbRow.Size * (row.NumberOfRows + 1);
 0161                }
 162
 0163                row = _parsedData.Get(index);
 0164                Debug.Assert(row.TokenType == JsonTokenType.PropertyName);
 165
 0166                ReadOnlySpan<byte> currentPropertyName = documentSpan.Slice(row.Location, row.SizeOrLength);
 167
 0168                if (row.HasComplexChildren)
 0169                {
 170                    // An escaped property name will be longer than an unescaped candidate, so only unescape
 171                    // when the lengths are compatible.
 0172                    if (currentPropertyName.Length > propertyName.Length)
 0173                    {
 0174                        int idx = currentPropertyName.IndexOf(JsonConstants.BackSlash);
 0175                        Debug.Assert(idx >= 0);
 176
 177                        // If everything up to where the property name has a backslash matches, keep going.
 0178                        if (propertyName.Length > idx &&
 0179                            currentPropertyName.Slice(0, idx).SequenceEqual(propertyName.Slice(0, idx)))
 0180                        {
 0181                            int remaining = currentPropertyName.Length - idx;
 0182                            int written = 0;
 0183                            byte[]? rented = null;
 184
 185                            try
 0186                            {
 0187                                Span<byte> utf8Unescaped = remaining <= utf8UnescapedStack.Length ?
 0188                                    utf8UnescapedStack :
 0189                                    (rented = ArrayPool<byte>.Shared.Rent(remaining));
 190
 191                                // Only unescape the part we haven't processed.
 0192                                JsonReaderHelper.Unescape(currentPropertyName.Slice(idx), utf8Unescaped, 0, out written)
 193
 194                                // If the unescaped remainder matches the input remainder, it's a match.
 0195                                if (utf8Unescaped.Slice(0, written).SequenceEqual(propertyName.Slice(idx)))
 0196                                {
 197                                    // If the property name is a match, the answer is the next element.
 0198                                    value = new JsonElement(this, index + DbRow.Size);
 0199                                    return true;
 200                                }
 0201                            }
 202                            finally
 0203                            {
 0204                                if (rented != null)
 0205                                {
 0206                                    rented.AsSpan(0, written).Clear();
 0207                                    ArrayPool<byte>.Shared.Return(rented);
 0208                                }
 0209                            }
 0210                        }
 0211                    }
 0212                }
 0213                else if (currentPropertyName.SequenceEqual(propertyName))
 0214                {
 215                    // If the property name is a match, the answer is the next element.
 0216                    value = new JsonElement(this, index + DbRow.Size);
 0217                    return true;
 218                }
 219
 220                // Move to the previous value
 0221                index -= DbRow.Size;
 0222            }
 223
 0224            value = default;
 0225            return false;
 0226        }
 227    }
 228}

Methods/Properties

IsDisposable()
RootElement()
.ctor(System.ReadOnlyMemory`1<System.Byte>,System.Text.Json.JsonDocument/MetadataDb,System.Byte[],System.Text.Json.PooledByteBufferWriter,System.Boolean)
Dispose()
WriteTo(System.Text.Json.Utf8JsonWriter)
GetJsonTokenType(System.Int32)
ValueIsEscaped(System.Int32,System.Boolean)
GetArrayLength(System.Int32)
GetPropertyCount(System.Int32)
GetArrayIndexElement(System.Int32,System.Int32)
GetEndIndex(System.Int32,System.Boolean)
GetRootRawValue()
GetRawValue(System.Int32,System.Boolean)
GetPropertyRawValue(System.Int32)
GetString(System.Int32,System.Text.Json.JsonTokenType)
TextEquals(System.Int32,System.ReadOnlySpan`1<System.Char>,System.Boolean)
TextEquals(System.Int32,System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.Boolean)
GetNameOfPropertyValue(System.Int32)
GetPropertyNameRaw(System.Int32)
TryGetValue(System.Int32,System.Byte[]&)
TryGetValue(System.Int32,System.SByte&)
TryGetValue(System.Int32,System.Byte&)
TryGetValue(System.Int32,System.Int16&)
TryGetValue(System.Int32,System.UInt16&)
TryGetValue(System.Int32,System.Int32&)
TryGetValue(System.Int32,System.UInt32&)
TryGetValue(System.Int32,System.Int64&)
TryGetValue(System.Int32,System.UInt64&)
TryGetValue(System.Int32,System.Double&)
TryGetValue(System.Int32,System.Single&)
TryGetValue(System.Int32,System.Decimal&)
TryGetValue(System.Int32,System.DateTime&)
TryGetValue(System.Int32,System.DateTimeOffset&)
TryGetValue(System.Int32,System.Guid&)
GetRawValueAsString(System.Int32)
GetPropertyRawValueAsString(System.Int32)
CloneElement(System.Int32)
WriteElementTo(System.Int32,System.Text.Json.Utf8JsonWriter)
WriteComplexElement(System.Int32,System.Text.Json.Utf8JsonWriter)
UnescapeString(System.Text.Json.JsonDocument/DbRow&,System.ArraySegment`1<System.Byte>&)
ClearAndReturn(System.ArraySegment`1<System.Byte>)
WritePropertyName(System.Int32,System.Text.Json.Utf8JsonWriter)
WritePropertyName(System.Text.Json.JsonDocument/DbRow&,System.Text.Json.Utf8JsonWriter)
WriteString(System.Text.Json.JsonDocument/DbRow&,System.Text.Json.Utf8JsonWriter)
Parse(System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonReaderOptions,System.Text.Json.JsonDocument/MetadataDb&,System.Text.Json.JsonDocument/StackRowStack&)
ValidateNoDuplicateProperties(System.Text.Json.JsonDocument)
ValidateDuplicatePropertiesCore(System.Text.Json.JsonDocument)
CheckNotDisposed()
CheckExpectedType(System.Text.Json.JsonTokenType,System.Text.Json.JsonTokenType)
CheckSupportedOptions(System.Text.Json.JsonReaderOptions,System.String)
Location()
SizeOrLength()
IsUnknownSize()
HasComplexChildren()
NumberOfRows()
TokenType()
.ctor(System.Text.Json.JsonTokenType,System.Int32,System.Int32)
IsSimpleValue()
Length()
.ctor(System.Byte[],System.Boolean,System.Boolean)
.ctor(System.Byte[])
CreateRented(System.Int32,System.Boolean)
CreateLocked(System.Int32)
Dispose()
CompleteAllocations()
Append(System.Text.Json.JsonTokenType,System.Int32,System.Int32)
Enlarge()
AssertValidIndex(System.Int32)
SetLength(System.Int32,System.Int32)
SetNumberOfRows(System.Int32,System.Int32)
SetHasComplexChildren(System.Int32)
FindIndexOfFirstUnsetSizeOrLength(System.Text.Json.JsonTokenType)
FindOpenElement(System.Text.Json.JsonTokenType)
Get(System.Int32)
GetJsonTokenType(System.Int32)
CopySegment(System.Int32,System.Int32)
Parse(System.ReadOnlyMemory`1<System.Byte>,System.Text.Json.JsonDocumentOptions)
Parse(System.Buffers.ReadOnlySequence`1<System.Byte>,System.Text.Json.JsonDocumentOptions)
Parse(System.IO.Stream,System.Text.Json.JsonDocumentOptions)
ParseRented(System.Text.Json.PooledByteBufferWriter,System.Text.Json.JsonDocumentOptions)
ParseValue(System.IO.Stream,System.Text.Json.JsonDocumentOptions)
ParseValue(System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonDocumentOptions)
ParseValue(System.String,System.Text.Json.JsonDocumentOptions)
ParseAsync(System.IO.Stream,System.Text.Json.JsonDocumentOptions,System.Threading.CancellationToken)
ParseAsyncCore()
ParseAsyncCoreUnrented()
Parse(System.ReadOnlyMemory`1<System.Char>,System.Text.Json.JsonDocumentOptions)
ParseValue(System.ReadOnlySpan`1<System.Char>,System.Text.Json.JsonDocumentOptions)
Parse(System.String,System.Text.Json.JsonDocumentOptions)
TryParseValue(System.Text.Json.Utf8JsonReader&,System.Text.Json.JsonDocument&)
ParseValue(System.Text.Json.Utf8JsonReader&)
ParseValue(System.Text.Json.Utf8JsonReader&,System.Boolean)
TryParseValue(System.Text.Json.Utf8JsonReader&,System.Text.Json.JsonDocument&,System.Boolean,System.Boolean,System.Boolean)
CreateForLiteral(System.Text.Json.JsonTokenType)
Parse(System.ReadOnlyMemory`1<System.Byte>,System.Text.Json.JsonReaderOptions,System.Byte[],System.Text.Json.PooledByteBufferWriter,System.Boolean)
ParseUnrented(System.ReadOnlyMemory`1<System.Byte>,System.Text.Json.JsonReaderOptions,System.Text.Json.JsonTokenType,System.Boolean)
ReadToEnd(System.IO.Stream)
ReadToEndAsync()
.ctor()
SetCapacity(System.Int32)
AddPropertyName(System.Text.Json.JsonProperty,System.Text.Json.JsonDocument)
SwitchToHashSet(System.ReadOnlyMemory`1<System.Byte>)
Reset()
Dispose()
.cctor()
Equals(System.ReadOnlyMemory`1<System.Byte>,System.ReadOnlyMemory`1<System.Byte>)
GetHashCode(System.ReadOnlyMemory`1<System.Byte>)
.ctor(System.Int32,System.Int32)
.ctor(System.Int32)
Dispose()
Push(System.Text.Json.JsonDocument/StackRow)
Pop()
Enlarge()
TryGetNamedPropertyValue(System.Int32,System.ReadOnlySpan`1<System.Char>,System.Text.Json.JsonElement&)
TryGetNamedPropertyValue(System.Int32,System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonElement&)
TryGetNamedPropertyValue(System.Int32,System.Int32,System.ReadOnlySpan`1<System.Byte>,System.Text.Json.JsonElement&)