}
SequenceType type = GetSequenceType();
- object endObject = _sequenceEnd.GetObject();
+ object endObject = _endObject;
int startIndex = GetIndex(position);
- int endIndex = GetIndex(_sequenceEnd);
+ int endIndex = GetIndex(_endInteger);
if (type == SequenceType.MultiSegment)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlyMemory<T> GetFirstBuffer()
{
- object startObject = _sequenceStart.GetObject();
+ object startObject = _startObject;
if (startObject == null)
return default;
- int startIndex = _sequenceStart.GetInteger();
- int endIndex = _sequenceEnd.GetInteger();
+ int startIndex = _startInteger;
+ int endIndex = _endInteger;
- bool isMultiSegment = startObject != _sequenceEnd.GetObject();
+ bool isMultiSegment = startObject != _endObject;
// The highest bit of startIndex and endIndex are used to infer the sequence type
// The code below is structured this way for performance reasons and is equivalent to the following:
if (isMultiSegment)
ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
- int startIndex = _sequenceStart.GetInteger();
- int endIndex = _sequenceEnd.GetInteger();
+ int startIndex = _startInteger;
+ int endIndex = _endInteger;
Debug.Assert(startIndex < 0 || endIndex < 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private SequencePosition Seek(in SequencePosition start, in SequencePosition end, long offset, ExceptionArgument argument)
+ internal SequencePosition Seek(long offset, ExceptionArgument exceptionArgument = ExceptionArgument.offset)
{
- int startIndex = GetIndex(start);
- int endIndex = GetIndex(end);
+ object startObject = _startObject;
+ object endObject = _endObject;
+ int startIndex = GetIndex(_startInteger);
+ int endIndex = GetIndex(_endInteger);
+
+ if (startObject != endObject)
+ {
+ Debug.Assert(startObject != null);
+ var startSegment = (ReadOnlySequenceSegment<T>)startObject;
+
+ int currentLength = startSegment.Memory.Length - startIndex;
+
+ // Position in start segment, defer to single segment seek
+ if (currentLength > offset)
+ goto IsSingleSegment;
+
+ if (currentLength < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
+
+ // End of segment. Move to start of next.
+ return SeekMultiSegment(startSegment.Next, endObject, endIndex, offset - currentLength, exceptionArgument);
+ }
+
+ Debug.Assert(startObject == endObject);
+
+ if (endIndex - startIndex < offset)
+ ThrowHelper.ThrowArgumentOutOfRangeException(exceptionArgument);
+
+ // Single segment Seek
+ IsSingleSegment:
+ return new SequencePosition(startObject, startIndex + (int)offset);
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private SequencePosition Seek(in SequencePosition start, long offset)
+ {
object startObject = start.GetObject();
- object endObject = end.GetObject();
+ object endObject = _endObject;
+ int startIndex = GetIndex(start);
+ int endIndex = GetIndex(_endInteger);
if (startObject != endObject)
{
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
// End of segment. Move to start of next.
- return SeekMultiSegment(startSegment.Next, endObject, endIndex, offset - currentLength, argument);
+ return SeekMultiSegment(startSegment.Next, endObject, endIndex, offset - currentLength, ExceptionArgument.offset);
}
Debug.Assert(startObject == endObject);
if (endIndex - startIndex < offset)
- ThrowHelper.ThrowArgumentOutOfRangeException(argument);
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offset);
// Single segment Seek
IsSingleSegment:
private void BoundsCheck(in SequencePosition position)
{
uint sliceStartIndex = (uint)GetIndex(position);
- uint startIndex = (uint)GetIndex(_sequenceStart);
- uint endIndex = (uint)GetIndex(_sequenceEnd);
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ object startObject = _startObject;
+ object endObject = _endObject;
+
+ uint startIndex = (uint)GetIndex(_startInteger);
+ uint endIndex = (uint)GetIndex(_endInteger);
// Single-Segment Sequence
if (startObject == endObject)
private void BoundsCheck(uint sliceStartIndex, object sliceStartObject, uint sliceEndIndex, object sliceEndObject)
{
- uint startIndex = (uint)GetIndex(_sequenceStart);
- uint endIndex = (uint)GetIndex(_sequenceEnd);
+ object startObject = _startObject;
+ object endObject = _endObject;
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ uint startIndex = (uint)GetIndex(_startInteger);
+ uint endIndex = (uint)GetIndex(_endInteger);
// Single-Segment Sequence
if (startObject == endObject)
// start >> 31 = 0, end >> 31 = -1
// 2 * 0 + (-1) = -1, result = (SequenceType)1
- return (SequenceType)(-(2 * (_sequenceStart.GetInteger() >> 31) + (_sequenceEnd.GetInteger() >> 31)));
+ return (SequenceType)(-(2 * (_startInteger >> 31) + (_endInteger >> 31)));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetIndex(in SequencePosition position) => position.GetInteger() & ReadOnlySequence.IndexBitMask;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int GetIndex(int Integer) => Integer & ReadOnlySequence.IndexBitMask;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySequence<T> SliceImpl(in SequencePosition start, in SequencePosition end)
{
// In this method we reset high order bits from indices
return new ReadOnlySequence<T>(
start.GetObject(),
- GetIndex(start) | (_sequenceStart.GetInteger() & ReadOnlySequence.FlagBitMask),
+ GetIndex(start) | (_startInteger & ReadOnlySequence.FlagBitMask),
end.GetObject(),
- GetIndex(end) | (_sequenceEnd.GetInteger() & ReadOnlySequence.FlagBitMask)
+ GetIndex(end) | (_endInteger & ReadOnlySequence.FlagBitMask)
+ );
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ReadOnlySequence<T> SliceImpl(in SequencePosition start)
+ {
+ // In this method we reset high order bits from indices
+ // of positions that were passed in
+ // and apply type bits specific for current ReadOnlySequence type
+
+ return new ReadOnlySequence<T>(
+ start.GetObject(),
+ GetIndex(start) | (_startInteger & ReadOnlySequence.FlagBitMask),
+ _endObject,
+ _endInteger
);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private long GetLength()
{
- int startIndex = GetIndex(_sequenceStart);
- int endIndex = GetIndex(_sequenceEnd);
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ object startObject = _startObject;
+ object endObject = _endObject;
+ int startIndex = GetIndex(_startInteger);
+ int endIndex = GetIndex(_endInteger);
if (startObject != endObject)
{
internal bool TryGetReadOnlySequenceSegment(out ReadOnlySequenceSegment<T> startSegment, out int startIndex, out ReadOnlySequenceSegment<T> endSegment, out int endIndex)
{
- object startObject = _sequenceStart.GetObject();
+ object startObject = _startObject;
// Default or not MultiSegment
if (startObject == null || GetSequenceType() != SequenceType.MultiSegment)
return false;
}
- Debug.Assert(_sequenceEnd.GetObject() != null);
+ Debug.Assert(_endObject != null);
startSegment = (ReadOnlySequenceSegment<T>)startObject;
- startIndex = GetIndex(_sequenceStart);
- endSegment = (ReadOnlySequenceSegment<T>)_sequenceEnd.GetObject();
- endIndex = GetIndex(_sequenceEnd);
+ startIndex = GetIndex(_startInteger);
+ endSegment = (ReadOnlySequenceSegment<T>)_endObject;
+ endIndex = GetIndex(_endInteger);
return true;
}
return false;
}
- Debug.Assert(_sequenceStart.GetObject() != null);
+ Debug.Assert(_startObject != null);
- int startIndex = GetIndex(_sequenceStart);
- segment = new ArraySegment<T>((T[])_sequenceStart.GetObject(), startIndex, GetIndex(_sequenceEnd) - startIndex);
+ int startIndex = GetIndex(_startInteger);
+ segment = new ArraySegment<T>((T[])_startObject, startIndex, GetIndex(_endInteger) - startIndex);
return true;
}
return false;
}
- Debug.Assert(_sequenceStart.GetObject() != null);
+ Debug.Assert(_startObject != null);
- start = GetIndex(_sequenceStart);
- length = GetIndex(_sequenceEnd) - start;
- text = (string)_sequenceStart.GetObject();
+ start = GetIndex(_startInteger);
+ length = GetIndex(_endInteger) - start;
+ text = (string)_startObject;
return true;
}
{
first = default;
next = default;
- SequencePosition start = Start;
- int startIndex = start.GetInteger();
- object startObject = start.GetObject();
+ object startObject = _startObject;
+ int startIndex = _startInteger;
if (startObject != null)
{
- SequencePosition end = End;
- int endIndex = end.GetInteger();
- bool hasMultipleSegments = startObject != end.GetObject();
+ bool hasMultipleSegments = startObject != _endObject;
+ int endIndex = _endInteger;
if (startIndex >= 0)
{
[DebuggerDisplay("{ToString(),raw}")]
public readonly partial struct ReadOnlySequence<T>
{
- private readonly SequencePosition _sequenceStart;
- private readonly SequencePosition _sequenceEnd;
+ // The data is essentially two SequencePositions, however the Start and End SequencePositions are deconstructed to improve packing.
+ private readonly object _startObject;
+ private readonly object _endObject;
+ private readonly int _startInteger;
+ private readonly int _endInteger;
/// <summary>
/// Returns empty <see cref="ReadOnlySequence{T}"/>
public bool IsSingleSegment
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _sequenceStart.GetObject() == _sequenceEnd.GetObject();
+ get => _startObject == _endObject;
}
/// <summary>
/// <summary>
/// A position to the start of the <see cref="ReadOnlySequence{T}"/>.
/// </summary>
- public SequencePosition Start => _sequenceStart;
+ public SequencePosition Start
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => new SequencePosition(_startObject, _startInteger);
+ }
/// <summary>
/// A position to the end of the <see cref="ReadOnlySequence{T}"/>
/// </summary>
- public SequencePosition End => _sequenceEnd;
+ public SequencePosition End
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => new SequencePosition(_endObject, _endInteger);
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySequence(object startSegment, int startIndexAndFlags, object endSegment, int endIndexAndFlags)
Debug.Assert((startSegment != null && endSegment != null) ||
(startSegment == null && endSegment == null && startIndexAndFlags == 0 && endIndexAndFlags == 0));
- _sequenceStart = new SequencePosition(startSegment, startIndexAndFlags);
- _sequenceEnd = new SequencePosition(endSegment, endIndexAndFlags);
+ _startObject = startSegment;
+ _endObject = endSegment;
+ _startInteger = startIndexAndFlags;
+ _endInteger = endIndexAndFlags;
}
/// <summary>
(startSegment == endSegment && endIndex < startIndex))
ThrowHelper.ThrowArgumentValidationException(startSegment, startIndex, endSegment);
- _sequenceStart = new SequencePosition(startSegment, ReadOnlySequence.SegmentToSequenceStart(startIndex));
- _sequenceEnd = new SequencePosition(endSegment, ReadOnlySequence.SegmentToSequenceEnd(endIndex));
+ _startObject = startSegment;
+ _endObject = endSegment;
+ _startInteger = ReadOnlySequence.SegmentToSequenceStart(startIndex);
+ _endInteger = ReadOnlySequence.SegmentToSequenceEnd(endIndex);
}
/// <summary>
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(0));
- _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(array.Length));
+ _startObject = array;
+ _endObject = array;
+ _startInteger = ReadOnlySequence.ArrayToSequenceStart(0);
+ _endInteger = ReadOnlySequence.ArrayToSequenceEnd(array.Length);
}
/// <summary>
(uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentValidationException(array, start);
- _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start));
- _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + length));
+ _startObject = array;
+ _endObject = array;
+ _startInteger = ReadOnlySequence.ArrayToSequenceStart(start);
+ _endInteger = ReadOnlySequence.ArrayToSequenceEnd(start + length);
}
/// <summary>
{
if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<T> manager, out int index, out int length))
{
- _sequenceStart = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceStart(index));
- _sequenceEnd = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceEnd(length));
+ _startObject = manager;
+ _endObject = manager;
+ _startInteger = ReadOnlySequence.MemoryManagerToSequenceStart(index);
+ _endInteger = ReadOnlySequence.MemoryManagerToSequenceEnd(length);
}
else if (MemoryMarshal.TryGetArray(memory, out ArraySegment<T> segment))
{
T[] array = segment.Array;
int start = segment.Offset;
- _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start));
- _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + segment.Count));
+ _startObject = array;
+ _endObject = array;
+ _startInteger = ReadOnlySequence.ArrayToSequenceStart(start);
+ _endInteger = ReadOnlySequence.ArrayToSequenceEnd(start + segment.Count);
}
else if (typeof(T) == typeof(char))
{
if (!MemoryMarshal.TryGetString((ReadOnlyMemory<char>)(object)memory, out string text, out int start, out length))
ThrowHelper.ThrowInvalidOperationException();
- _sequenceStart = new SequencePosition(text, ReadOnlySequence.StringToSequenceStart(start));
- _sequenceEnd = new SequencePosition(text, ReadOnlySequence.StringToSequenceEnd(start + length));
+ _startObject = text;
+ _endObject = text;
+ _startInteger = ReadOnlySequence.StringToSequenceStart(start);
+ _endInteger = ReadOnlySequence.StringToSequenceEnd(start + length);
}
else
{
// Should never be reached
ThrowHelper.ThrowInvalidOperationException();
- _sequenceStart = default;
- _sequenceEnd = default;
+ _startObject = null;
+ _endObject = null;
+ _startInteger = 0;
+ _endInteger = 0;
}
}
SequencePosition begin;
SequencePosition end;
- int startIndex = GetIndex(_sequenceStart);
- int endIndex = GetIndex(_sequenceEnd);
+ int startIndex = GetIndex(_startInteger);
+ int endIndex = GetIndex(_endInteger);
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ object startObject = _startObject;
+ object endObject = _endObject;
if (startObject != endObject)
{
uint sliceEndIndex = (uint)GetIndex(end);
object sliceEndObject = end.GetObject();
- uint startIndex = (uint)GetIndex(_sequenceStart);
- object startObject = _sequenceStart.GetObject();
+ uint startIndex = (uint)GetIndex(_startInteger);
+ object startObject = _startObject;
- uint endIndex = (uint)GetIndex(_sequenceEnd);
- object endObject = _sequenceEnd.GetObject();
+ uint endIndex = (uint)GetIndex(_endInteger);
+ object endObject = _endObject;
// Single-Segment Sequence
if (startObject == endObject)
uint sliceStartIndex = (uint)GetIndex(start);
object sliceStartObject = start.GetObject();
- uint startIndex = (uint)GetIndex(_sequenceStart);
- object startObject = _sequenceStart.GetObject();
+ uint startIndex = (uint)GetIndex(_startInteger);
+ object startObject = _startObject;
- uint endIndex = (uint)GetIndex(_sequenceEnd);
- object endObject = _sequenceEnd.GetObject();
+ uint endIndex = (uint)GetIndex(_endInteger);
+ object endObject = _endObject;
// Single-Segment Sequence
if (startObject == endObject)
public ReadOnlySequence<T> Slice(SequencePosition start)
{
BoundsCheck(start);
- return SliceImpl(start, _sequenceEnd);
+ return SliceImpl(start);
}
/// <summary>
if (start == 0)
return this;
- SequencePosition begin = Seek(_sequenceStart, _sequenceEnd, start, ExceptionArgument.start);
- return SliceImpl(begin, _sequenceEnd);
+ SequencePosition begin = Seek(start, ExceptionArgument.start);
+ return SliceImpl(begin);
}
/// <inheritdoc />
/// <summary>
/// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the start of the sequence.
/// </summary>
- public SequencePosition GetPosition(long offset) => GetPosition(offset, _sequenceStart);
+ public SequencePosition GetPosition(long offset)
+ {
+ if (offset < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange();
+
+ return Seek(offset);
+ }
/// <summary>
/// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the <paramref name="origin"/>
if (offset < 0)
ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange();
- return Seek(origin, _sequenceEnd, offset, ExceptionArgument.offset);
+ return Seek(origin, offset);
}
/// <summary>