Runtime: ReadOnlySequence.Slice which accepts a SequencePosition throws ArgumentOutOfRangeException unexpectedly.

Created on 7 Nov 2018  Â·  10Comments  Â·  Source: dotnet/runtime

The behavior of Slice(offset, SequencePosition) should match GetPosition(offset) and Slice(offset).
I should be able to slice a ReadOnlySequence past the current sequence if the next one is empty (for example: [97] -> []).

```C#
[Fact]
public static void ReadOnlySequenceTest()
{
string jsonString = "a";

ReadOnlyMemory<byte> dataMemory = Encoding.UTF8.GetBytes(jsonString);

var firstSegment = new BufferSegment<byte>(dataMemory.Slice(0, 1));
ReadOnlyMemory<byte> secondMem = dataMemory.Slice(0, 0);
BufferSegment<byte> secondSegment = firstSegment.Append(secondMem);

// A two segment sequence where the second one is empty: [a]-> []
var sequence = new ReadOnlySequence<byte>(firstSegment, 0, secondSegment, secondMem.Length);

SequencePosition expectedPosition = sequence.GetPosition(1);
Console.WriteLine(expectedPosition.GetInteger());   // 0
Console.WriteLine(expectedPosition.GetObject());    // System.Text.Json.Tests.BufferSegment`1[System.Byte]
Assert.True(((ReadOnlySequenceSegment<byte>)expectedPosition.GetObject()).Memory.IsEmpty);

SequencePosition actualPosition = sequence.Slice(1).Start;
Console.WriteLine(actualPosition.GetInteger()); // 0
Console.WriteLine(actualPosition.GetObject());  // System.Text.Json.Tests.BufferSegment`1[System.Byte]
Assert.True(((ReadOnlySequenceSegment<byte>)actualPosition.GetObject()).Memory.IsEmpty);
Assert.Equal(expectedPosition, actualPosition); // True

SequencePosition actualPosition2 = sequence.GetPosition(1, sequence.Start);
Console.WriteLine(actualPosition2.GetInteger());   // 0
Console.WriteLine(actualPosition2.GetObject());    // System.Text.Json.Tests.BufferSegment`1[System.Byte]
Assert.True(((ReadOnlySequenceSegment<byte>)actualPosition2.GetObject()).Memory.IsEmpty);

// System.ArgumentOutOfRangeException : Specified argument was out of the range of valid values.

/*
    System.ArgumentOutOfRangeException : Specified argument was out of the range of valid values.
    Parameter name: start
    Stack Trace:
    E:\GitHub\Fork\corefx\src\System.Memory\src\System\ThrowHelper.cs(31,0): at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument)
    E:\GitHub\Fork\corefx\src\System.Memory\src\System\Buffers\ReadOnlySequence.cs(281,0): at System.Buffers.ReadOnlySequence`1.Slice(Int64 start, SequencePosition end)
    E:\GitHub\Fork\corefx\src\System.Memory\src\System\Buffers\ReadOnlySequence.cs(388,0): at System.Buffers.ReadOnlySequence`1.Slice(Int32 start, SequencePosition end)
    E:\GitHub\Fork\corefx\src\System.Text.Json\tests\Utf8JsonReaderTests.TryGet.cs(357,0): at System.Text.Json.Tests.Utf8JsonReaderTests.ReadOnlySequenceTest()
*/
sequence = sequence.Slice(1, sequence.Start);

}
```

We need to use the Slice overload that takes a SequencePosition for performance to avoid having to scan the segments from the beginning every time. Using GetPosition with long is too slow:
https://github.com/dotnet/corefxlab/blob/63ef1a5db4fdbfcf9822b8cd2cbc3921b3d3cb9e/src/System.Text.JsonLab/System/Text/Json/JsonUtf8Reader.cs#L112

cc @pakrym, @davidfowl

area-System.Memory bug up-for-grabs

All 10 comments

There's still an issue with the ROSequence .Slice API. GetPosition works as expected which is why Utf8JsonReader can use it.

I'm asking because I think I just fixed dotnet/corefx#35254 (PR coming soon) and thought it might be related.

But I don't understand this issue. I would say the sample throws the exception as expected.

But I don't understand this issue. I would say the sample throws the exception as expected.

C# // A two segment sequence where the second one is empty: [a]-> [] SequencePosition pos = sequence.GetPosition(1, sequence.Start); // success sequence = sequence.Slice(1, sequence.Start); // throws

Why would you consider the exception when calling slice, expected?

As I understand the API this means a slice from position 1 back to position 0. The second parameter is the end of the resulting slice. The second param of .GetPosition() has a different meaning. What slice would you expect it to return?

As I understand the API this means a slice from position 1 back to position 0. The second parameter is the end of the resulting slice.

Actually, you are totally right. I confused the parameters of GetPosition and Slice (end vs origin). Passing in sequence.Start as the end position should continue to throw.
C# public ReadOnlySequence<T> Slice(int start, SequencePosition end) { throw null; } public ReadOnlySequence<T> Slice(long start, SequencePosition end) { throw null; } public SequencePosition GetPosition(long offset, SequencePosition origin)

For now, it looks like this issue is non-actionable. Let me page back the scenario where I was seeing an issue and double check the expected behavior. I'll either update the issue or close it based on my findings. Thanks for bringing up the clarification and correcting me :)

Sounds good. Other PR is ready.

For now, it looks like this issue is non-actionable. Let me page back the scenario where I was seeing an issue and double check the expected behavior. I'll either update the issue or close it based on my findings. Thanks for bringing up the clarification and correcting me :)

@ahsonkhan should we close this or move future?

I haven't gotten a chance to look again. Moving to future for now. Thanks for the ping :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GitAntoinee picture GitAntoinee  Â·  3Comments

EgorBo picture EgorBo  Â·  3Comments

matty-hall picture matty-hall  Â·  3Comments

omariom picture omariom  Â·  3Comments

noahfalk picture noahfalk  Â·  3Comments