private static SequencePosition SeekMultiSegment(IMemoryList <byte> start, int startIndex, IMemoryList <byte> end, int endPosition, long count, bool checkEndReachable) { SequencePosition result = default; bool foundResult = false; IMemoryList <byte> current = start; int currentIndex = startIndex; while (current != null) { // We need to loop up until the end to make sure start and end are connected // if end is not trusted if (!foundResult) { var isEnd = current == end; int currentEnd = isEnd ? endPosition : current.Memory.Length; int currentLength = currentEnd - currentIndex; // We would prefer to put position in the beginning of next segment // then past the end of previous one, but only if we are not leaving current buffer if (currentLength > count || (currentLength == count && isEnd)) { result = new SequencePosition(current, currentIndex + (int)count); foundResult = true; if (!checkEndReachable) { break; } } count -= currentLength; } if (current.Next == null && current != end) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } current = current.Next; currentIndex = 0; } if (!foundResult) { ThrowHelper.ThrowArgumentOutOfRangeException_CountOutOfRange(); } return(result); }
static internal void GetFirstSpan <T>(this ReadOnlySequence <T> sequence, out ReadOnlySpan <T> first, out SequencePosition next) { first = default; next = default; SequencePosition start = sequence.Start; int startIndex = start.GetInteger(); object startObject = start.GetObject(); if (startObject != null) { SequencePosition end = sequence.End; int endIndex = end.GetInteger(); bool hasMultipleSegments = startObject != end.GetObject(); if (startIndex >= 0) { if (endIndex >= 0) { // Positive start and end index == ReadOnlySequenceSegment<T> ReadOnlySequenceSegment <T> segment = (ReadOnlySequenceSegment <T>)startObject; next = new SequencePosition(segment.Next, 0); first = segment.Memory.Span; if (hasMultipleSegments) { first = first.Slice(startIndex); } else { first = first.Slice(startIndex, endIndex - startIndex); } } else { // Positive start and negative end index == T[] if (hasMultipleSegments) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } first = new ReadOnlySpan <T>((T[])startObject, startIndex, (endIndex & ReadOnlySequence.IndexBitMask) - startIndex); } } else { first = GetFirstSpanSlow <T>(startObject, startIndex, endIndex, hasMultipleSegments); } } }
internal SequencePosition Seek(SequencePosition start, SequencePosition end, int count, bool checkEndReachable = true) { int startIndex = start.Index; int endIndex = end.Index; SequenceType type = GetSequenceType(); startIndex = GetIndex(startIndex); endIndex = GetIndex(endIndex); switch (type) { case SequenceType.MemoryList: if (start.Segment == end.Segment && endIndex - startIndex >= count) { return(new SequencePosition(start.Segment, startIndex + count)); } return(SeekMultiSegment((IMemoryList <byte>)start.Segment, startIndex, (IMemoryList <byte>)end.Segment, endIndex, count, checkEndReachable)); case SequenceType.OwnedMemory: case SequenceType.Array: if (start.Segment != end.Segment) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } if (endIndex - startIndex >= count) { return(new SequencePosition(start.Segment, startIndex + count)); } ThrowHelper.ThrowArgumentOutOfRangeException_CountOutOfRange(); return(default); default: ThrowHelper.ThrowInvalidOperationException_UnexpectedSegmentType(); return(default); } }
private static ReadOnlySpan <T> GetFirstSpanSlow <T>(object startObject, int startIndex, int endIndex, bool isMultiSegment) { Debug.Assert(startIndex < 0 || endIndex < 0); if (isMultiSegment) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } // The type == char check here is redundant. However, we still have it to allow // the JIT to see when that the code is unreachable and eliminate it. // A == 1 && B == 1 means SequenceType.String if (typeof(T) == typeof(char) && endIndex < 0) { var memory = (ReadOnlyMemory <T>)(object)((string)startObject).AsMemory(); // No need to remove the FlagBitMask since (endIndex - startIndex) == (endIndex & ReadOnlySequence.IndexBitMask) - (startIndex & ReadOnlySequence.IndexBitMask) return(memory.Span.Slice(startIndex & IndexBitMask, endIndex - startIndex)); } else // endIndex >= 0, A == 1 && B == 0 means SequenceType.MemoryManager { startIndex &= IndexBitMask; return(((MemoryManager <T>)startObject).Memory.Span.Slice(startIndex, endIndex - startIndex)); } }
internal bool TryGetBuffer(SequencePosition start, SequencePosition end, out ReadOnlyMemory <T> data, out SequencePosition next) { if (start.Segment == null) { data = default; next = default; return(false); } int startIndex = start.Index; int endIndex = end.Index; SequenceType type = GetSequenceType(); startIndex = GetIndex(startIndex); endIndex = GetIndex(endIndex); switch (type) { case SequenceType.MemoryList: var segment = (IMemoryList <T>)start.Segment; Memory <T> bufferSegmentMemory = segment.Memory; int currentEndIndex = bufferSegmentMemory.Length; if (segment == end.Segment) { currentEndIndex = endIndex; next = default; } else { IMemoryList <T> nextSegment = segment.Next; if (nextSegment == null) { if (end.Segment != null) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } next = default; } else { next = new SequencePosition(nextSegment, 0); } } data = bufferSegmentMemory.Slice(startIndex, currentEndIndex - startIndex); return(true); case SequenceType.OwnedMemory: var ownedMemory = (OwnedMemory <T>)start.Segment; if (ownedMemory != end.Segment) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } data = ownedMemory.Memory.Slice(startIndex, endIndex - startIndex); next = default; return(true); case SequenceType.Array: var array = (T[])start.Segment; if (array != end.Segment) { ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } data = new Memory <T>(array, startIndex, endIndex - startIndex); next = default; return(true); default: ThrowHelper.ThrowInvalidOperationException_UnexpectedSegmentType(); next = default; data = default; return(false); } }