Skip to content

ReadOnlySequence<T>.GetPosition(<size>) behaviour #27751

@thargy

Description

@thargy

Current Behaviour

The current behaviour of ReadOnlySequence<T>.GetPosition() is demonstrated in the following code:

ReadOnlySequence<byte> sequence = new ReadOnlySequence<byte>(new byte[size]);
object firstObject = sequence.Start.GetObject();

// End returns a sequence with index int.MinValue, and object is set to last segment
Console.WriteLine(ReferenceEquals(sequence.End.GetObject(), firstObject).ToString()); // True
Console.WriteLine(sequence.End.GetInteger().ToString()); // -2147483647

// GetPosition(0) returns a sequence with index 0, and object is set to first segment
SequencePosition zeroPosition = sequence.GetPosition(0); // True
Console.WriteLine(ReferenceEquals(zeroPosition.GetObject(), firstObject).ToString()); // 0
Console.WriteLine(zeroPosition.GetInteger().ToString()); // True

// GetPosition(0) returns a sequence that equals sequence.Start
Console.WriteLine(zeroPosition.Equals(sequence.Start).ToString()); // True

// GetPosition(size) returns a sequence with last object and index = size ?
SequencePosition sizePosition = sequence.GetPosition(size);
Console.WriteLine(ReferenceEquals(sizePosition.GetObject(), firstObject).ToString()); // True
Console.WriteLine(sizePosition.GetInteger().ToString()); // size

// GetPosition(size) does not match sequence.End
Console.WriteLine(sizePosition.Equals(sequence.End).ToString()); // False

// Asking for size + 1 correctly detects OOB, throwing ArgumentOutOfRangeException
Sequence sizePlusOnePosition = sequence.GetPosition(size + 1);

Summary

The following behaviour is intuitive:

  • ReadOnlySequence<T>.End appears to point after the end of the sequence (index = index.MaxValue, object is last segment).
  • ReadOnlySequence<T>.Start points to first segment, index 0
  • ReadOnlySequence<T>.GetPosition(0) is identical to Sequence.Start
  • ReadOnlySequence<T>.GetPosition(-1) throws an ArgumentOutOfRangeException
  • ReadOnlySequence<T>.GetPosition(<size> + 1) throws an ArgumentOutOfRangeException

However, ReadOnlySequence<T>.GetPosition(<size>) does not appear intuitive to me, unless I'm missing something? Although SequencePosition.GetObject() returns the last segment, SequencePosition.GetIndex() seems to indicate an index beyond the sequence and does not match ReadOnlySequence<T>.End. In the absence of any other indicator there is no obvious way to see this SequencePosition is outside the bounds of the sequence. In constrast, asking for -1 and size+1 throw exceptions indicating that the parameter is bounded.

Expected Behaviour

I would expect one of the following to occur (in order of preference):

  1. ReadOnlySequence<T>.GetPosition(<size>) throws an ArgumentOutOfRangeException as it is beyond the bounds of the sequence - this makes it act more like an array accessor and is more like the rest of the framework. [Preferred]
  2. ReadOnlySequence<T>.GetPosition(<size>) returns a SequencePosition equal to ReadOnlySequence<T>.End, indicating it's immediately beyond the end of the sequence.
  3. ReadOnlySequence<T>.GetPosition(<size>) returns default(SequencePosition), to indicate out of bounds of the array - this is non-intuitive as it implies -1 and size + 1 should also return this value.

On a related note, it would be nice if ReadOnlySequence<T>.GetPosition(offset, origin) supported negative offsets. I understand this may have an impact on pipes, but on many sequences it would be an entirely valid concept.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions