| | | 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 | | |
| | | 4 | | using System.Buffers; |
| | | 5 | | using System.Diagnostics; |
| | | 6 | | using System.IO.Pipelines; |
| | | 7 | | using System.Runtime.InteropServices; |
| | | 8 | | using System.Threading; |
| | | 9 | | using System.Threading.Tasks; |
| | | 10 | | |
| | | 11 | | namespace System.Text.Json.Serialization |
| | | 12 | | { |
| | | 13 | | [StructLayout(LayoutKind.Auto)] |
| | | 14 | | internal struct PipeReadBufferState : IReadBufferState<PipeReadBufferState, PipeReader> |
| | | 15 | | { |
| | | 16 | | private readonly PipeReader _utf8Json; |
| | | 17 | | |
| | 27962 | 18 | | private ReadOnlySequence<byte> _sequence = ReadOnlySequence<byte>.Empty; |
| | | 19 | | private bool _isFinalBlock; |
| | 27962 | 20 | | private bool _isFirstBlock = true; |
| | | 21 | | private int _unsuccessfulReadBytes; |
| | | 22 | | |
| | | 23 | | public PipeReadBufferState(PipeReader utf8Json) |
| | 27962 | 24 | | { |
| | 27962 | 25 | | _utf8Json = utf8Json; |
| | 27962 | 26 | | } |
| | | 27 | | |
| | 28049 | 28 | | public readonly bool IsFinalBlock => _isFinalBlock; |
| | | 29 | | |
| | | 30 | | #if DEBUG |
| | 174 | 31 | | public readonly ReadOnlySequence<byte> Bytes => _sequence; |
| | | 32 | | #endif |
| | | 33 | | |
| | | 34 | | public void Advance(long bytesConsumed) |
| | 27962 | 35 | | { |
| | 27962 | 36 | | _unsuccessfulReadBytes = 0; |
| | 27962 | 37 | | if (bytesConsumed == 0) |
| | 14022 | 38 | | { |
| | 14022 | 39 | | long leftOver = _sequence.Length; |
| | | 40 | | // Cap at int.MaxValue as PipeReader.ReadAtLeastAsync uses an int as the minimum size argument. |
| | 14022 | 41 | | _unsuccessfulReadBytes = (int)Math.Min(int.MaxValue, leftOver * 2); |
| | 14022 | 42 | | } |
| | | 43 | | |
| | 27962 | 44 | | _utf8Json.AdvanceTo(_sequence.Slice(bytesConsumed).Start, _sequence.End); |
| | 27962 | 45 | | _sequence = ReadOnlySequence<byte>.Empty; |
| | 27962 | 46 | | } |
| | | 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, |
| | 27962 | 54 | | { |
| | 27962 | 55 | | 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. |
| | 27962 | 59 | | PipeReadBufferState bufferState = this; |
| | | 60 | | |
| | 27962 | 61 | | int minBufferSize = _unsuccessfulReadBytes > 0 ? _unsuccessfulReadBytes : 0; |
| | 27962 | 62 | | ReadResult readResult = await _utf8Json.ReadAtLeastAsync(minBufferSize, cancellationToken).ConfigureAwait(fa |
| | | 63 | | |
| | 27962 | 64 | | bufferState._sequence = readResult.Buffer; |
| | 27962 | 65 | | bufferState._isFinalBlock = readResult.IsCompleted; |
| | 27962 | 66 | | bufferState.ProcessReadBytes(); |
| | | 67 | | |
| | 27962 | 68 | | if (readResult.IsCanceled) |
| | 0 | 69 | | { |
| | 0 | 70 | | ThrowHelper.ThrowOperationCanceledException_PipeReadCanceled(); |
| | 0 | 71 | | } |
| | | 72 | | |
| | 27962 | 73 | | return bufferState; |
| | 27962 | 74 | | } |
| | | 75 | | |
| | 0 | 76 | | public void Read(PipeReader utf8Json) => throw new NotImplementedException(); |
| | | 77 | | |
| | | 78 | | public void GetReader(JsonReaderState jsonReaderState, out Utf8JsonReader reader) |
| | 27962 | 79 | | { |
| | 27962 | 80 | | if (_sequence.IsSingleSegment) |
| | 0 | 81 | | { |
| | 0 | 82 | | reader = new Utf8JsonReader( |
| | 0 | 83 | | #if NET |
| | 0 | 84 | | _sequence.FirstSpan, |
| | 0 | 85 | | #else |
| | 0 | 86 | | _sequence.First.Span, |
| | 0 | 87 | | #endif |
| | 0 | 88 | | IsFinalBlock, jsonReaderState); |
| | 0 | 89 | | } |
| | | 90 | | else |
| | 27962 | 91 | | { |
| | 27962 | 92 | | reader = new Utf8JsonReader(_sequence, IsFinalBlock, jsonReaderState); |
| | 27962 | 93 | | } |
| | 27962 | 94 | | } |
| | | 95 | | |
| | | 96 | | private void ProcessReadBytes() |
| | 27962 | 97 | | { |
| | 27962 | 98 | | if (_isFirstBlock) |
| | 27962 | 99 | | { |
| | 27962 | 100 | | _isFirstBlock = false; |
| | | 101 | | |
| | | 102 | | // Handle the UTF-8 BOM if present |
| | 27962 | 103 | | if (_sequence.Length > 0) |
| | 27962 | 104 | | { |
| | 27962 | 105 | | if (_sequence.First.Length >= JsonConstants.Utf8Bom.Length) |
| | 14678 | 106 | | { |
| | 14678 | 107 | | if (_sequence.First.Span.StartsWith(JsonConstants.Utf8Bom)) |
| | 0 | 108 | | { |
| | 0 | 109 | | _sequence = _sequence.Slice((byte)JsonConstants.Utf8Bom.Length); |
| | 0 | 110 | | } |
| | 14678 | 111 | | } |
| | | 112 | | else |
| | 13284 | 113 | | { |
| | | 114 | | // BOM spans multiple segments |
| | 13284 | 115 | | SequencePosition pos = _sequence.Start; |
| | 13284 | 116 | | int matched = 0; |
| | 54858 | 117 | | while (matched < JsonConstants.Utf8Bom.Length && _sequence.TryGet(ref pos, out ReadOnlyMemory<by |
| | 41574 | 118 | | { |
| | 41574 | 119 | | ReadOnlySpan<byte> span = mem.Span; |
| | 83271 | 120 | | for (int i = 0; i < span.Length && matched < JsonConstants.Utf8Bom.Length; i++, matched++) |
| | 41574 | 121 | | { |
| | 41574 | 122 | | if (span[i] != JsonConstants.Utf8Bom[matched]) |
| | 41533 | 123 | | { |
| | 41533 | 124 | | matched = 0; |
| | 41533 | 125 | | break; |
| | | 126 | | } |
| | 41 | 127 | | } |
| | 41574 | 128 | | } |
| | | 129 | | |
| | 13284 | 130 | | if (matched == JsonConstants.Utf8Bom.Length) |
| | 0 | 131 | | { |
| | 0 | 132 | | _sequence = _sequence.Slice(JsonConstants.Utf8Bom.Length); |
| | 0 | 133 | | } |
| | 13284 | 134 | | } |
| | 27962 | 135 | | } |
| | 27962 | 136 | | } |
| | 27962 | 137 | | } |
| | | 138 | | |
| | | 139 | | public void Dispose() |
| | 27962 | 140 | | { |
| | 27962 | 141 | | if (_sequence.Equals(ReadOnlySequence<byte>.Empty)) |
| | 27962 | 142 | | { |
| | 27962 | 143 | | 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. |
| | 0 | 149 | | _utf8Json.AdvanceTo(_sequence.Start); |
| | 0 | 150 | | _sequence = ReadOnlySequence<byte>.Empty; |
| | 27962 | 151 | | } |
| | | 152 | | } |
| | | 153 | | } |