private void AdvanceTo(BufferSegment?consumedSegment, int consumedIndex, BufferSegment?examinedSegment, int examinedIndex) { if (consumedSegment == null || examinedSegment == null) { return; } if (_readHead == null) { ThrowHelper.ThrowInvalidOperationException_AdvanceToInvalidCursor(); } BufferSegment returnStart = _readHead; BufferSegment?returnEnd = consumedSegment; long consumedBytes = BufferSegment.GetLength(returnStart, _readIndex, consumedSegment, consumedIndex); _bufferedBytes -= consumedBytes; Debug.Assert(_bufferedBytes >= 0); _examinedEverything = false; if (examinedSegment == _readTail) { // If we examined everything, we force ReadAsync to actually read from the underlying stream // instead of returning a ReadResult from TryRead. _examinedEverything = examinedIndex == _readTail.End; } // Two cases here: // 1. All data is consumed. If so, we empty clear everything so we don't hold onto any // excess memory. // 2. A segment is entirely consumed but there is still more data in nextSegments // We are allowed to remove an extra segment. by setting returnEnd to be the next block. // 3. We are in the middle of a segment. // Move _readHead and _readIndex to consumedSegment and index if (_bufferedBytes == 0) { returnEnd = null; _readHead = null; _readTail = null; _readIndex = 0; } else if (consumedIndex == returnEnd.Length) { BufferSegment?nextBlock = returnEnd.NextSegment; _readHead = nextBlock; _readIndex = 0; returnEnd = nextBlock; } else { _readHead = consumedSegment; _readIndex = consumedIndex; } // Remove all blocks that are freed (except the last one) while (returnStart != returnEnd) { BufferSegment next = returnStart.NextSegment !; returnStart.ResetMemory(); ReturnSegmentUnsynchronized(returnStart); returnStart = next; } }