/// <summary> /// Reads the current token, ensuring enough data is pulled from the underlying reader /// </summary> /// <param name="reader">A reference to the UTF-8 JSON Reader used to read JSON</param> public void Read(ref Utf8JsonReader reader) { while (!reader.Read()) { // Insufficient data in the buffer for this token, pull more from the Reader var Consumed = (int)reader.BytesConsumed; if (Consumed > 0) { var RemovedLength = 0; // Trim what leftover data we can if (_LeftoverHead != null) { // Remove segments and decrease Consumed until we run out of segments, or run out of Consumed bytes while (_LeftoverHead.Memory.Length < Consumed) { var OldLength = _LeftoverHead.Memory.Length; Consumed -= OldLength; RemovedLength += OldLength; if (MemoryMarshal.TryGetArray(_LeftoverHead.Memory, out var LeftoverBuffer)) { ArrayPool <byte> .Shared.Return(LeftoverBuffer.Array !); } if (_HasExtraTail && _LeftoverHead == _LeftoverTail) { _LeftoverHead = null; } else { _LeftoverHead = (LeftoverSegment?)_LeftoverHead.Next; } if (_LeftoverHead == null) { break; } } var NextSegment = _LeftoverHead; if (NextSegment != null) { // Correct the remainder on the header NextSegment.CorrectMemory(Consumed); NextSegment.CorrectRunningIndex(RemovedLength); NextSegment = (LeftoverSegment?)NextSegment.Next; // Correct the Running Index on subsequent entries while (NextSegment != null) { NextSegment.CorrectRunningIndex(Consumed + RemovedLength); if (_HasExtraTail && NextSegment == _LeftoverTail) { NextSegment = null; } else { NextSegment = (LeftoverSegment?)NextSegment.Next; } } Consumed = 0; } else { // We removed all saved segments, leaving only the contents of the current buffer _LeftoverTail = null; _HasExtraTail = false; } } } // Have we consumed all of the supplied buffer? if (Consumed == _LastBuffer.Length) { // No need to pass anything on to the next reader _Reader.Advance(_LastBuffer.Length); _LastBuffer = _Reader.GetMemory(0); _HasExtraTail = false; // If this is the final block, this will probably fail on the next read, since the consumer called Read after the final token reader = new Utf8JsonReader(_LastBuffer.Span, _LastBuffer.IsEmpty, reader.CurrentState); } else { // Before we advance, capture anything leftover and record a segment var Remainder = _LastBuffer.Length - Consumed; var Buffer = ArrayPool <byte> .Shared.Rent(Remainder); _LastBuffer.Slice(Consumed).CopyTo(Buffer); if (_LeftoverTail == null) { // No start segment, so create it _LeftoverTail = _LeftoverHead = new LeftoverSegment(Buffer.AsMemory(0, Remainder)); } else { // We have an existing head segment, chain a new segment to it _LeftoverTail = new LeftoverSegment(_LeftoverTail, Buffer.AsMemory(0, Remainder)); } // Now it's safe to advance and grab the next buffer _Reader.Advance(Consumed); _LastBuffer = _Reader.GetMemory(0); if (_LastBuffer.IsEmpty) { // No more data, finalise the reader reader = new Utf8JsonReader(new ReadOnlySequence <byte>(_LeftoverHead !, 0, _LeftoverTail, _LeftoverTail.Memory.Length), true, reader.CurrentState); _HasExtraTail = false; // The next read (when we hit the end of the while and loop back) will throw if the JSON is truncated } else { // More data, continue the reader // Create a temp segment representing any previous data, along with our new buffer var TempSegment = new LeftoverSegment(_LeftoverTail, _LastBuffer); _HasExtraTail = true; reader = new Utf8JsonReader(new ReadOnlySequence <byte>(_LeftoverHead !, 0, TempSegment, TempSegment.Memory.Length), false, reader.CurrentState); } } } }
internal LeftoverSegment(LeftoverSegment previous, ReadOnlyMemory <byte> memory) { Memory = memory; RunningIndex = previous.RunningIndex + previous.Memory.Length; previous.Next = this; }