< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 89
Coverable lines: 89
Total lines: 154
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 30
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
Advance(...)0%220%
ReadAsync()0%440%
Read(...)100%110%
GetReader(...)0%220%
ProcessReadBytes()0%20200%
Dispose()0%220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/PipeReadBufferState.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.IO.Pipelines;
 7using System.Runtime.InteropServices;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Text.Json.Serialization
 12{
 13    [StructLayout(LayoutKind.Auto)]
 14    internal struct PipeReadBufferState : IReadBufferState<PipeReadBufferState, PipeReader>
 15    {
 16        private readonly PipeReader _utf8Json;
 17
 018        private ReadOnlySequence<byte> _sequence = ReadOnlySequence<byte>.Empty;
 19        private bool _isFinalBlock;
 020        private bool _isFirstBlock = true;
 21        private int _unsuccessfulReadBytes;
 22
 23        public PipeReadBufferState(PipeReader utf8Json)
 024        {
 025            _utf8Json = utf8Json;
 026        }
 27
 028        public readonly bool IsFinalBlock => _isFinalBlock;
 29
 30#if DEBUG
 031        public readonly ReadOnlySequence<byte> Bytes => _sequence;
 32#endif
 33
 34        public void Advance(long bytesConsumed)
 035        {
 036            _unsuccessfulReadBytes = 0;
 037            if (bytesConsumed == 0)
 038            {
 039                long leftOver = _sequence.Length;
 40                // Cap at int.MaxValue as PipeReader.ReadAtLeastAsync uses an int as the minimum size argument.
 041                _unsuccessfulReadBytes = (int)Math.Min(int.MaxValue, leftOver * 2);
 042            }
 43
 044            _utf8Json.AdvanceTo(_sequence.Slice(bytesConsumed).Start, _sequence.End);
 045            _sequence = ReadOnlySequence<byte>.Empty;
 046        }
 47
 48        /// <summary>
 49        /// Read from the PipeReader until either our buffer limit is filled or we hit EOF.
 50        /// Calling ReadCore is relatively expensive, so we minimize the number of times
 51        /// we need to call it.
 52        /// </summary>
 53        public async ValueTask<PipeReadBufferState> ReadAsync(PipeReader utf8Json, CancellationToken cancellationToken, 
 054        {
 055            Debug.Assert(_sequence.Equals(ReadOnlySequence<byte>.Empty), "ReadAsync should only be called when the buffe
 56
 57            // Since mutable structs don't work well with async state machines,
 58            // make all updates on a copy which is returned once complete.
 059            PipeReadBufferState bufferState = this;
 60
 061            int minBufferSize = _unsuccessfulReadBytes > 0 ? _unsuccessfulReadBytes : 0;
 062            ReadResult readResult = await _utf8Json.ReadAtLeastAsync(minBufferSize, cancellationToken).ConfigureAwait(fa
 63
 064            bufferState._sequence = readResult.Buffer;
 065            bufferState._isFinalBlock = readResult.IsCompleted;
 066            bufferState.ProcessReadBytes();
 67
 068            if (readResult.IsCanceled)
 069            {
 070                ThrowHelper.ThrowOperationCanceledException_PipeReadCanceled();
 071            }
 72
 073            return bufferState;
 074        }
 75
 076        public void Read(PipeReader utf8Json) => throw new NotImplementedException();
 77
 78        public void GetReader(JsonReaderState jsonReaderState, out Utf8JsonReader reader)
 079        {
 080            if (_sequence.IsSingleSegment)
 081            {
 082                reader = new Utf8JsonReader(
 083#if NET
 084                    _sequence.FirstSpan,
 085#else
 086                    _sequence.First.Span,
 087#endif
 088                    IsFinalBlock, jsonReaderState);
 089            }
 90            else
 091            {
 092                reader = new Utf8JsonReader(_sequence, IsFinalBlock, jsonReaderState);
 093            }
 094        }
 95
 96        private void ProcessReadBytes()
 097        {
 098            if (_isFirstBlock)
 099            {
 0100                _isFirstBlock = false;
 101
 102                // Handle the UTF-8 BOM if present
 0103                if (_sequence.Length > 0)
 0104                {
 0105                    if (_sequence.First.Length >= JsonConstants.Utf8Bom.Length)
 0106                    {
 0107                        if (_sequence.First.Span.StartsWith(JsonConstants.Utf8Bom))
 0108                        {
 0109                            _sequence = _sequence.Slice((byte)JsonConstants.Utf8Bom.Length);
 0110                        }
 0111                    }
 112                    else
 0113                    {
 114                        // BOM spans multiple segments
 0115                        SequencePosition pos = _sequence.Start;
 0116                        int matched = 0;
 0117                        while (matched < JsonConstants.Utf8Bom.Length && _sequence.TryGet(ref pos, out ReadOnlyMemory<by
 0118                        {
 0119                            ReadOnlySpan<byte> span = mem.Span;
 0120                            for (int i = 0; i < span.Length && matched < JsonConstants.Utf8Bom.Length; i++, matched++)
 0121                            {
 0122                                if (span[i] != JsonConstants.Utf8Bom[matched])
 0123                                {
 0124                                    matched = 0;
 0125                                    break;
 126                                }
 0127                            }
 0128                        }
 129
 0130                        if (matched == JsonConstants.Utf8Bom.Length)
 0131                        {
 0132                            _sequence = _sequence.Slice(JsonConstants.Utf8Bom.Length);
 0133                        }
 0134                    }
 0135                }
 0136            }
 0137        }
 138
 139        public void Dispose()
 0140        {
 0141            if (_sequence.Equals(ReadOnlySequence<byte>.Empty))
 0142            {
 0143                return;
 144            }
 145
 146            // If we have a sequence, that likely means an Exception was thrown during deserialization.
 147            // We should make sure to call AdvanceTo so that future reads on the PipeReader can be done without throwing
 148            // We'll advance to the start of the sequence as we don't know how many bytes were consumed.
 0149            _utf8Json.AdvanceTo(_sequence.Start);
 0150            _sequence = ReadOnlySequence<byte>.Empty;
 0151        }
 152    }
 153}
 154