ReadOnlySequence<T>
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.Buffers
{
[DebuggerTypeProxy(typeof(System.Buffers.ReadOnlySequenceDebugView<>))]
[DebuggerDisplay("{ToString(),raw}")]
public readonly struct ReadOnlySequence<T>
{
public struct Enumerator
{
private readonly ReadOnlySequence<T> _sequence;
private SequencePosition _next;
private ReadOnlyMemory<T> _currentMemory;
public ReadOnlyMemory<T> Current => _currentMemory;
public Enumerator([In] [System.Runtime.CompilerServices.IsReadOnly] ref ReadOnlySequence<T> sequence)
{
_currentMemory = default(ReadOnlyMemory<T>);
_next = sequence.Start;
_sequence = sequence;
}
public bool MoveNext()
{
if (_next.GetObject() == null)
return false;
return _sequence.TryGet(ref _next, out _currentMemory, true);
}
}
private enum SequenceType
{
MultiSegment,
Array,
MemoryManager,
String,
Empty
}
private readonly SequencePosition _sequenceStart;
private readonly SequencePosition _sequenceEnd;
public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(System.SpanHelpers.PerTypeValues<T>.EmptyArray);
public long Length => GetLength();
public bool IsEmpty => Length == 0;
public bool IsSingleSegment {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get {
return _sequenceStart.GetObject() == _sequenceEnd.GetObject();
}
}
public ReadOnlyMemory<T> First => GetFirstBuffer();
public SequencePosition Start => _sequenceStart;
public SequencePosition End => _sequenceEnd;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySequence(object startSegment, int startIndexAndFlags, object endSegment, int endIndexAndFlags)
{
_sequenceStart = new SequencePosition(startSegment, startIndexAndFlags);
_sequenceEnd = new SequencePosition(endSegment, endIndexAndFlags);
}
public ReadOnlySequence(ReadOnlySequenceSegment<T> startSegment, int startIndex, ReadOnlySequenceSegment<T> endSegment, int endIndex)
{
if (startSegment != null && endSegment != null && (startSegment == endSegment || startSegment.RunningIndex <= endSegment.RunningIndex)) {
ReadOnlyMemory<T> memory = startSegment.Memory;
if ((uint)memory.Length >= (uint)startIndex) {
memory = endSegment.Memory;
if ((uint)memory.Length >= (uint)endIndex && (startSegment != endSegment || endIndex >= startIndex))
goto IL_004c;
}
}
System.ThrowHelper.ThrowArgumentValidationException<T>(startSegment, startIndex, endSegment);
goto IL_004c;
IL_004c:
_sequenceStart = new SequencePosition(startSegment, System.Buffers.ReadOnlySequence.SegmentToSequenceStart(startIndex));
_sequenceEnd = new SequencePosition(endSegment, System.Buffers.ReadOnlySequence.SegmentToSequenceEnd(endIndex));
}
public ReadOnlySequence(T[] array)
{
if (array == null)
System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.array);
_sequenceStart = new SequencePosition(array, System.Buffers.ReadOnlySequence.ArrayToSequenceStart(0));
_sequenceEnd = new SequencePosition(array, System.Buffers.ReadOnlySequence.ArrayToSequenceEnd(array.Length));
}
public ReadOnlySequence(T[] array, int start, int length)
{
if (array == null || (uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
System.ThrowHelper.ThrowArgumentValidationException(array, start);
_sequenceStart = new SequencePosition(array, System.Buffers.ReadOnlySequence.ArrayToSequenceStart(start));
_sequenceEnd = new SequencePosition(array, System.Buffers.ReadOnlySequence.ArrayToSequenceEnd(start + length));
}
public ReadOnlySequence(ReadOnlyMemory<T> memory)
{
ArraySegment<T> segment;
if (MemoryMarshal.TryGetMemoryManager<T, MemoryManager<T>>(memory, out MemoryManager<T> manager, out int start, out int length)) {
_sequenceStart = new SequencePosition(manager, System.Buffers.ReadOnlySequence.MemoryManagerToSequenceStart(start));
_sequenceEnd = new SequencePosition(manager, System.Buffers.ReadOnlySequence.MemoryManagerToSequenceEnd(length));
} else if (MemoryMarshal.TryGetArray<T>(memory, out segment)) {
T[] array = segment.Array;
int offset = segment.Offset;
_sequenceStart = new SequencePosition(array, System.Buffers.ReadOnlySequence.ArrayToSequenceStart(offset));
_sequenceEnd = new SequencePosition(array, System.Buffers.ReadOnlySequence.ArrayToSequenceEnd(offset + segment.Count));
} else if (typeof(T) == typeof(char)) {
if (!MemoryMarshal.TryGetString((ReadOnlyMemory<char>)(object)memory, out string text, out int start2, out length))
System.ThrowHelper.ThrowInvalidOperationException();
_sequenceStart = new SequencePosition(text, System.Buffers.ReadOnlySequence.StringToSequenceStart(start2));
_sequenceEnd = new SequencePosition(text, System.Buffers.ReadOnlySequence.StringToSequenceEnd(start2 + length));
} else {
System.ThrowHelper.ThrowInvalidOperationException();
_sequenceStart = default(SequencePosition);
_sequenceEnd = default(SequencePosition);
}
}
public ReadOnlySequence<T> Slice(long start, long length)
{
if (start < 0 || length < 0)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(start);
int index = GetIndex(ref _sequenceStart);
int index2 = GetIndex(ref _sequenceEnd);
object object = _sequenceStart.GetObject();
object object2 = _sequenceEnd.GetObject();
SequencePosition start2;
SequencePosition end;
if (object != object2) {
ReadOnlySequenceSegment<T> readOnlySequenceSegment = (ReadOnlySequenceSegment<T>)object;
int num = readOnlySequenceSegment.Memory.Length - index;
if (num > start) {
index += (int)start;
start2 = new SequencePosition(object, index);
end = GetEndPosition(readOnlySequenceSegment, object, index, object2, index2, length);
} else {
if (num < 0)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
start2 = SeekMultiSegment(readOnlySequenceSegment.Next, object2, index2, start - num, System.ExceptionArgument.start);
int index3 = GetIndex(ref start2);
object object3 = start2.GetObject();
if (object3 != object2)
end = GetEndPosition((ReadOnlySequenceSegment<T>)object3, object3, index3, object2, index2, length);
else {
if (index2 - index3 < length)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0);
end = new SequencePosition(object3, index3 + (int)length);
}
}
} else {
if (index2 - index < start)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(-1);
index += (int)start;
start2 = new SequencePosition(object, index);
if (index2 - index < length)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0);
end = new SequencePosition(object, index + (int)length);
}
return SliceImpl(ref start2, ref end);
}
public ReadOnlySequence<T> Slice(long start, SequencePosition end)
{
if (start < 0)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(start);
uint index = (uint)GetIndex(ref end);
object object = end.GetObject();
uint index2 = (uint)GetIndex(ref _sequenceStart);
object object2 = _sequenceStart.GetObject();
uint index3 = (uint)GetIndex(ref _sequenceEnd);
object object3 = _sequenceEnd.GetObject();
if (object2 == object3) {
if (!InRange(index, index2, index3))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
if (index - index2 < start)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(-1);
} else {
ReadOnlySequenceSegment<T> readOnlySequenceSegment = (ReadOnlySequenceSegment<T>)object2;
ulong num = (ulong)(readOnlySequenceSegment.RunningIndex + index2);
ulong num2 = (ulong)(((ReadOnlySequenceSegment<T>)object).RunningIndex + index);
if (!InRange(num2, num, (ulong)(((ReadOnlySequenceSegment<T>)object3).RunningIndex + index3)))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
if ((ulong)((long)num + start) > num2)
System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start);
int num3 = readOnlySequenceSegment.Memory.Length - (int)index2;
if (num3 <= start) {
if (num3 < 0)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
SequencePosition start2 = SeekMultiSegment(readOnlySequenceSegment.Next, object, (int)index, start - num3, System.ExceptionArgument.start);
return SliceImpl(ref start2, ref end);
}
}
SequencePosition start3 = new SequencePosition(object2, (int)index2 + (int)start);
return SliceImpl(ref start3, ref end);
}
public ReadOnlySequence<T> Slice(SequencePosition start, long length)
{
uint index = (uint)GetIndex(ref start);
object object = start.GetObject();
uint index2 = (uint)GetIndex(ref _sequenceStart);
object object2 = _sequenceStart.GetObject();
uint index3 = (uint)GetIndex(ref _sequenceEnd);
object object3 = _sequenceEnd.GetObject();
if (object2 == object3) {
if (!InRange(index, index2, index3))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
if (length < 0)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0);
if (index3 - index < length)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0);
} else {
ReadOnlySequenceSegment<T> readOnlySequenceSegment = (ReadOnlySequenceSegment<T>)object;
ulong num = (ulong)(readOnlySequenceSegment.RunningIndex + index);
ulong start2 = (ulong)(((ReadOnlySequenceSegment<T>)object2).RunningIndex + index2);
ulong num2 = (ulong)(((ReadOnlySequenceSegment<T>)object3).RunningIndex + index3);
if (!InRange(num, start2, num2))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
if (length < 0)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0);
if ((ulong)((long)num + length) > num2)
System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length);
int num3 = readOnlySequenceSegment.Memory.Length - (int)index;
if (num3 < length) {
if (num3 < 0)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
SequencePosition end = SeekMultiSegment(readOnlySequenceSegment.Next, object3, (int)index3, length - num3, System.ExceptionArgument.length);
return SliceImpl(ref start, ref end);
}
}
SequencePosition end2 = new SequencePosition(object, (int)index + (int)length);
return SliceImpl(ref start, ref end2);
}
public ReadOnlySequence<T> Slice(int start, int length)
{
return Slice((long)start, (long)length);
}
public ReadOnlySequence<T> Slice(int start, SequencePosition end)
{
return Slice((long)start, end);
}
public ReadOnlySequence<T> Slice(SequencePosition start, int length)
{
return Slice(start, (long)length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySequence<T> Slice(SequencePosition start, SequencePosition end)
{
BoundsCheck((uint)GetIndex(ref start), start.GetObject(), (uint)GetIndex(ref end), end.GetObject());
return SliceImpl(ref start, ref end);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySequence<T> Slice(SequencePosition start)
{
BoundsCheck(ref start);
return SliceImpl(ref start, ref _sequenceEnd);
}
public ReadOnlySequence<T> Slice(long start)
{
if (start < 0)
System.ThrowHelper.ThrowStartOrEndArgumentValidationException(start);
if (start == 0)
return this;
SequencePosition start2 = Seek(ref _sequenceStart, ref _sequenceEnd, start, System.ExceptionArgument.start);
return SliceImpl(ref start2, ref _sequenceEnd);
}
public override string ToString()
{
if (typeof(T) == typeof(char)) {
ReadOnlySequence<T> source = this;
ReadOnlySequence<char> sequence = Unsafe.As<ReadOnlySequence<T>, ReadOnlySequence<char>>(ref source);
if (SequenceMarshal.TryGetString(sequence, out string text, out int start, out int length))
return text.Substring(start, length);
if (Length < 2147483647)
return new string(BuffersExtensions.ToArray<char>(ref sequence));
}
return $"""{typeof(T).Name}""{Length}""";
}
public Enumerator GetEnumerator()
{
return new Enumerator(ref this);
}
public SequencePosition GetPosition(long offset)
{
return GetPosition(offset, _sequenceStart);
}
public SequencePosition GetPosition(long offset, SequencePosition origin)
{
if (offset < 0)
System.ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange();
return Seek(ref origin, ref _sequenceEnd, offset, System.ExceptionArgument.offset);
}
public bool TryGet(ref SequencePosition position, out ReadOnlyMemory<T> memory, bool advance = true)
{
SequencePosition next;
bool result = TryGetBuffer(ref position, out memory, out next);
if (advance)
position = next;
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool TryGetBuffer([In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition position, out ReadOnlyMemory<T> memory, out SequencePosition next)
{
object object = position.GetObject();
next = default(SequencePosition);
if (object == null) {
memory = default(ReadOnlyMemory<T>);
return false;
}
SequenceType sequenceType = GetSequenceType();
object object2 = _sequenceEnd.GetObject();
int index = GetIndex(ref position);
int index2 = GetIndex(ref _sequenceEnd);
if (sequenceType == SequenceType.MultiSegment) {
ReadOnlySequenceSegment<T> readOnlySequenceSegment = (ReadOnlySequenceSegment<T>)object;
ReadOnlyMemory<T> memory2;
if (readOnlySequenceSegment != object2) {
ReadOnlySequenceSegment<T> next2 = readOnlySequenceSegment.Next;
if (next2 == null)
System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
next = new SequencePosition(next2, 0);
memory2 = readOnlySequenceSegment.Memory;
memory = memory2.Slice(index);
} else {
memory2 = readOnlySequenceSegment.Memory;
memory = memory2.Slice(index, index2 - index);
}
} else {
if (object != object2)
System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
if (sequenceType == SequenceType.Array)
memory = new ReadOnlyMemory<T>((T[])object, index, index2 - index);
else if (typeof(T) == typeof(char) && sequenceType == SequenceType.String) {
memory = (ReadOnlyMemory<T>)(object)MemoryExtensions.AsMemory((string)object, index, index2 - index);
} else {
memory = ((MemoryManager<T>)object).Memory.Slice(index, index2 - index);
}
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlyMemory<T> GetFirstBuffer()
{
object object = _sequenceStart.GetObject();
if (object == null)
return default(ReadOnlyMemory<T>);
int integer = _sequenceStart.GetInteger();
int integer2 = _sequenceEnd.GetInteger();
bool flag = object != _sequenceEnd.GetObject();
if (integer >= 0) {
if (integer2 >= 0) {
ReadOnlyMemory<T> memory = ((ReadOnlySequenceSegment<T>)object).Memory;
if (flag)
return memory.Slice(integer);
return memory.Slice(integer, integer2 - integer);
}
if (flag)
System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
return new ReadOnlyMemory<T>((T[])object, integer, (integer2 & 2147483647) - integer);
}
if (flag)
System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
if (typeof(T) == typeof(char) && integer2 < 0)
return (ReadOnlyMemory<T>)(object)MemoryExtensions.AsMemory((string)object, integer & 2147483647, integer2 - integer);
integer &= 2147483647;
return ((MemoryManager<T>)object).Memory.Slice(integer, integer2 - integer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private SequencePosition Seek([In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition start, [In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition end, long offset, System.ExceptionArgument argument)
{
int index = GetIndex(ref start);
int index2 = GetIndex(ref end);
object object = start.GetObject();
object object2 = end.GetObject();
if (object != object2) {
ReadOnlySequenceSegment<T> readOnlySequenceSegment = (ReadOnlySequenceSegment<T>)object;
int num = readOnlySequenceSegment.Memory.Length - index;
if (num <= offset) {
if (num < 0)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
return SeekMultiSegment(readOnlySequenceSegment.Next, object2, index2, offset - num, argument);
}
} else if (index2 - index < offset) {
System.ThrowHelper.ThrowArgumentOutOfRangeException(argument);
}
return new SequencePosition(object, index + (int)offset);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static SequencePosition SeekMultiSegment(ReadOnlySequenceSegment<T> currentSegment, object endObject, int endIndex, long offset, System.ExceptionArgument argument)
{
while (true) {
if (currentSegment == null || currentSegment == endObject) {
if (currentSegment == null || endIndex < offset)
System.ThrowHelper.ThrowArgumentOutOfRangeException(argument);
break;
}
int length = currentSegment.Memory.Length;
if (length > offset)
break;
offset -= length;
currentSegment = currentSegment.Next;
}
return new SequencePosition(currentSegment, (int)offset);
}
private void BoundsCheck([In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition position)
{
uint index = (uint)GetIndex(ref position);
uint index2 = (uint)GetIndex(ref _sequenceStart);
uint index3 = (uint)GetIndex(ref _sequenceEnd);
object object = _sequenceStart.GetObject();
object object2 = _sequenceEnd.GetObject();
if (object == object2) {
if (!InRange(index, index2, index3))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
} else {
ulong start = (ulong)(((ReadOnlySequenceSegment<T>)object).RunningIndex + index2);
if (!InRange((ulong)(((ReadOnlySequenceSegment<T>)position.GetObject()).RunningIndex + index), start, (ulong)(((ReadOnlySequenceSegment<T>)object2).RunningIndex + index3)))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
}
}
private void BoundsCheck(uint sliceStartIndex, object sliceStartObject, uint sliceEndIndex, object sliceEndObject)
{
uint index = (uint)GetIndex(ref _sequenceStart);
uint index2 = (uint)GetIndex(ref _sequenceEnd);
object object = _sequenceStart.GetObject();
object object2 = _sequenceEnd.GetObject();
if (object == object2) {
if (sliceStartObject != sliceEndObject || sliceStartObject != object || sliceStartIndex > sliceEndIndex || sliceStartIndex < index || sliceEndIndex > index2)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
} else {
ulong num = (ulong)(((ReadOnlySequenceSegment<T>)sliceStartObject).RunningIndex + sliceStartIndex);
ulong num2 = (ulong)(((ReadOnlySequenceSegment<T>)sliceEndObject).RunningIndex + sliceEndIndex);
if (num > num2)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
if (num < (ulong)(((ReadOnlySequenceSegment<T>)object).RunningIndex + index) || num2 > (ulong)(((ReadOnlySequenceSegment<T>)object2).RunningIndex + index2))
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
}
}
private static SequencePosition GetEndPosition(ReadOnlySequenceSegment<T> startSegment, object startObject, int startIndex, object endObject, int endIndex, long length)
{
int num = startSegment.Memory.Length - startIndex;
if (num > length)
return new SequencePosition(startObject, startIndex + (int)length);
if (num < 0)
System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
return SeekMultiSegment(startSegment.Next, endObject, endIndex, length - num, System.ExceptionArgument.length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private SequenceType GetSequenceType()
{
return (SequenceType)(-(2 * (_sequenceStart.GetInteger() >> 31) + (_sequenceEnd.GetInteger() >> 31)));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetIndex([In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition position)
{
return position.GetInteger() & 2147483647;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySequence<T> SliceImpl([In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition start, [In] [System.Runtime.CompilerServices.IsReadOnly] ref SequencePosition end)
{
return new ReadOnlySequence<T>(start.GetObject(), GetIndex(ref start) | (_sequenceStart.GetInteger() & -2147483648), end.GetObject(), GetIndex(ref end) | (_sequenceEnd.GetInteger() & -2147483648));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private long GetLength()
{
int index = GetIndex(ref _sequenceStart);
int index2 = GetIndex(ref _sequenceEnd);
object object = _sequenceStart.GetObject();
object object2 = _sequenceEnd.GetObject();
if (object != object2) {
ReadOnlySequenceSegment<T> readOnlySequenceSegment = (ReadOnlySequenceSegment<T>)object;
ReadOnlySequenceSegment<T> readOnlySequenceSegment2 = (ReadOnlySequenceSegment<T>)object2;
return readOnlySequenceSegment2.RunningIndex + index2 - (readOnlySequenceSegment.RunningIndex + index);
}
return index2 - index;
}
internal bool TryGetReadOnlySequenceSegment(out ReadOnlySequenceSegment<T> startSegment, out int startIndex, out ReadOnlySequenceSegment<T> endSegment, out int endIndex)
{
object object = _sequenceStart.GetObject();
if (object == null || GetSequenceType() != 0) {
startSegment = null;
startIndex = 0;
endSegment = null;
endIndex = 0;
return false;
}
startSegment = (ReadOnlySequenceSegment<T>)object;
startIndex = GetIndex(ref _sequenceStart);
endSegment = (ReadOnlySequenceSegment<T>)_sequenceEnd.GetObject();
endIndex = GetIndex(ref _sequenceEnd);
return true;
}
internal bool TryGetArray(out ArraySegment<T> segment)
{
if (GetSequenceType() != SequenceType.Array) {
segment = default(ArraySegment<T>);
return false;
}
int index = GetIndex(ref _sequenceStart);
segment = new ArraySegment<T>((T[])_sequenceStart.GetObject(), index, GetIndex(ref _sequenceEnd) - index);
return true;
}
internal bool TryGetString(out string text, out int start, out int length)
{
if (typeof(T) != typeof(char) || GetSequenceType() != SequenceType.String) {
start = 0;
length = 0;
text = null;
return false;
}
start = GetIndex(ref _sequenceStart);
length = GetIndex(ref _sequenceEnd) - start;
text = (string)_sequenceStart.GetObject();
return true;
}
private static bool InRange(uint value, uint start, uint end)
{
return value - start <= end - start;
}
private static bool InRange(ulong value, ulong start, ulong end)
{
return value - start <= end - start;
}
}
}