Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 9ff60da

Browse files
authored
Add GetPosition overload to ReadOnlySequence (#27633)
* Add GetPosition overload to ReadOnlySequence * Address feedback (split tests) * Use new GetPosition overload whenever passing start is redundant.
1 parent c9cdfba commit 9ff60da

File tree

8 files changed

+186
-45
lines changed

8 files changed

+186
-45
lines changed

src/System.IO.Pipelines/tests/BackpressureTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void AdvanceThrowsIfFlushActiveAndNotConsumedPastThreshold()
3434
Assert.False(flushAsync.IsCompleted);
3535

3636
ReadResult result = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
37-
SequencePosition consumed = result.Buffer.GetPosition(result.Buffer.Start, 31);
37+
SequencePosition consumed = result.Buffer.GetPosition(31);
3838
Assert.Throws<InvalidOperationException>(() => _pipe.Reader.AdvanceTo(consumed, result.Buffer.End));
3939

4040
_pipe.Reader.AdvanceTo(result.Buffer.End, result.Buffer.End);
@@ -49,7 +49,7 @@ public void FlushAsyncAwaitableCompletesWhenReaderAdvancesUnderLow()
4949
Assert.False(flushAsync.IsCompleted);
5050

5151
ReadResult result = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
52-
SequencePosition consumed = result.Buffer.GetPosition(result.Buffer.Start, 33);
52+
SequencePosition consumed = result.Buffer.GetPosition(33);
5353
_pipe.Reader.AdvanceTo(consumed, consumed);
5454

5555
Assert.True(flushAsync.IsCompleted);
@@ -66,7 +66,7 @@ public void FlushAsyncAwaitableDoesNotCompletesWhenReaderAdvancesUnderHight()
6666
Assert.False(flushAsync.IsCompleted);
6767

6868
ReadResult result = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
69-
SequencePosition consumed = result.Buffer.GetPosition(result.Buffer.Start, 32);
69+
SequencePosition consumed = result.Buffer.GetPosition(32);
7070
_pipe.Reader.AdvanceTo(consumed, consumed);
7171

7272
Assert.False(flushAsync.IsCompleted);
@@ -81,7 +81,7 @@ public void FlushAsyncAwaitableResetsOnCommit()
8181
Assert.False(flushAsync.IsCompleted);
8282

8383
ReadResult result = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
84-
SequencePosition consumed = result.Buffer.GetPosition(result.Buffer.Start, 33);
84+
SequencePosition consumed = result.Buffer.GetPosition(33);
8585
_pipe.Reader.AdvanceTo(consumed, consumed);
8686

8787
Assert.True(flushAsync.IsCompleted);

src/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public async Task AdvanceWithGetPositionCrossingIntoWriteHeadWorks()
140140

141141
// Create position that would cross into write head
142142
ReadOnlySequence<byte> buffer = readResult.Buffer;
143-
SequencePosition position = buffer.GetPosition(buffer.Start, buffer.Length);
143+
SequencePosition position = buffer.GetPosition(buffer.Length);
144144

145145
// Return everything
146146
_pipe.Reader.AdvanceTo(position);
@@ -512,7 +512,7 @@ public async Task SyncReadThenAsyncRead()
512512

513513
Assert.Equal("Hello World", Encoding.ASCII.GetString(result.Buffer.ToArray()));
514514

515-
_pipe.Reader.AdvanceTo(result.Buffer.GetPosition(result.Buffer.Start, 6));
515+
_pipe.Reader.AdvanceTo(result.Buffer.GetPosition(6));
516516

517517
result = await _pipe.Reader.ReadAsync();
518518

src/System.Memory/ref/System.Memory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ public readonly partial struct ReadOnlySequence<T>
318318
public long Length { get { throw null; } }
319319
public System.SequencePosition Start { get { throw null; } }
320320
public System.Buffers.ReadOnlySequence<T>.Enumerator GetEnumerator() { throw null; }
321-
public System.SequencePosition GetPosition(System.SequencePosition origin, long offset) { throw null; }
321+
public System.SequencePosition GetPosition(long offset) { throw null; }
322+
public System.SequencePosition GetPosition(long offset, System.SequencePosition origin) { throw null; }
322323
public System.Buffers.ReadOnlySequence<T> Slice(int start, int length) { throw null; }
323324
public System.Buffers.ReadOnlySequence<T> Slice(int start, System.SequencePosition end) { throw null; }
324325
public System.Buffers.ReadOnlySequence<T> Slice(long start) { throw null; }

src/System.Memory/src/System/Buffers/BuffersExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static class BuffersExtensions
1919
int index = sequence.First.Span.IndexOf(value);
2020
if (index != -1)
2121
{
22-
return sequence.GetPosition(sequence.Start, index);
22+
return sequence.GetPosition(index);
2323
}
2424

2525
return null;
@@ -39,7 +39,7 @@ public static class BuffersExtensions
3939
int index = memory.Span.IndexOf(value);
4040
if (index != -1)
4141
{
42-
return sequence.GetPosition(result, index);
42+
return sequence.GetPosition(index, result);
4343
}
4444
else if (position.GetObject() == null)
4545
{

src/System.Memory/src/System/Buffers/ReadOnlySequence.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,15 @@ public ReadOnlySequence<T> Slice(long start)
310310
/// </summary>
311311
public Enumerator GetEnumerator() => new Enumerator(this);
312312

313+
/// <summary>
314+
/// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the start of the sequence.
315+
/// </summary>
316+
public SequencePosition GetPosition(long offset) => GetPosition(offset, _sequenceStart);
317+
313318
/// <summary>
314319
/// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the <paramref name="origin"/>
315320
/// </summary>
316-
public SequencePosition GetPosition(SequencePosition origin, long offset)
321+
public SequencePosition GetPosition(long offset, SequencePosition origin)
317322
{
318323
if (offset < 0)
319324
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offset);

src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@ public void SegmentStartIsConsideredInBoundsCheck()
2424

2525
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 50);
2626

27-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 25); // segment 1 index 75
28-
SequencePosition c2 = buffer.GetPosition(buffer.Start, 55); // segment 2 index 5
27+
SequencePosition c1 = buffer.GetPosition(25); // segment 1 index 75
28+
SequencePosition c2 = buffer.GetPosition(55); // segment 2 index 5
2929

3030
ReadOnlySequence<byte> sliced = buffer.Slice(c1, c2);
3131
Assert.Equal(30, sliced.Length);
32+
33+
c1 = buffer.GetPosition(25, buffer.Start); // segment 1 index 75
34+
c2 = buffer.GetPosition(55, buffer.Start); // segment 2 index 5
35+
36+
sliced = buffer.Slice(c1, c2);
37+
Assert.Equal(30, sliced.Length);
3238
}
3339

3440
[Fact]
@@ -39,7 +45,12 @@ public void GetPositionPrefersNextSegment()
3945

4046
ReadOnlySequence<byte> buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 0);
4147

42-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 50);
48+
SequencePosition c1 = buffer.GetPosition(50);
49+
50+
Assert.Equal(0, c1.GetInteger());
51+
Assert.Equal(bufferSegment2, c1.GetObject());
52+
53+
c1 = buffer.GetPosition(50, buffer.Start);
4354

4455
Assert.Equal(0, c1.GetInteger());
4556
Assert.Equal(bufferSegment2, c1.GetObject());
@@ -54,7 +65,12 @@ public void GetPositionDoesNotCrossOutsideBuffer()
5465

5566
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 100);
5667

57-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 200);
68+
SequencePosition c1 = buffer.GetPosition(200);
69+
70+
Assert.Equal(100, c1.GetInteger());
71+
Assert.Equal(bufferSegment2, c1.GetObject());
72+
73+
c1 = buffer.GetPosition(200, buffer.Start);
5874

5975
Assert.Equal(100, c1.GetInteger());
6076
Assert.Equal(bufferSegment2, c1.GetObject());
@@ -70,13 +86,21 @@ public void CheckEndReachableDoesNotCrossPastEnd()
7086

7187
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment4, 100);
7288

73-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 200);
89+
SequencePosition c1 = buffer.GetPosition(200);
7490

7591
Assert.Equal(0, c1.GetInteger());
7692
Assert.Equal(bufferSegment3, c1.GetObject());
7793

7894
ReadOnlySequence<byte> seq = buffer.Slice(0, c1);
7995
Assert.Equal(200, seq.Length);
96+
97+
c1 = buffer.GetPosition(200, buffer.Start);
98+
99+
Assert.Equal(0, c1.GetInteger());
100+
Assert.Equal(bufferSegment3, c1.GetObject());
101+
102+
seq = buffer.Slice(0, c1);
103+
Assert.Equal(200, seq.Length);
80104
}
81105

82106
[Fact]
@@ -89,7 +113,12 @@ public void SeekSkipsEmptySegments()
89113

90114
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment4, 100);
91115

92-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 100);
116+
SequencePosition c1 = buffer.GetPosition(100);
117+
118+
Assert.Equal(0, c1.GetInteger());
119+
Assert.Equal(bufferSegment4, c1.GetObject());
120+
121+
c1 = buffer.GetPosition(100, buffer.Start);
93122

94123
Assert.Equal(0, c1.GetInteger());
95124
Assert.Equal(bufferSegment4, c1.GetObject());
@@ -105,22 +134,54 @@ public void SeekEmptySkipDoesNotCrossPastEnd()
105134

106135
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 0);
107136

108-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 100);
137+
SequencePosition c1 = buffer.GetPosition(100);
109138

110139
Assert.Equal(0, c1.GetInteger());
111140
Assert.Equal(bufferSegment2, c1.GetObject());
141+
142+
c1 = buffer.GetPosition(100, buffer.Start);
143+
144+
Assert.Equal(0, c1.GetInteger());
145+
Assert.Equal(bufferSegment2, c1.GetObject());
146+
}
147+
148+
[Fact]
149+
public void TryGetSkipsEmptyForNextUsingGetPositionWithOffset()
150+
{
151+
var bufferSegment1 = new BufferSegment<byte>(new byte[100]);
152+
BufferSegment<byte> bufferSegment2 = bufferSegment1.Append(new byte[0]);
153+
BufferSegment<byte> bufferSegment3 = bufferSegment2.Append(new byte[0]);
154+
BufferSegment<byte> bufferSegment4 = bufferSegment3.Append(new byte[100]);
155+
156+
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment4, 100);
157+
SequencePosition c1 = buffer.GetPosition(0);
158+
Assert.Equal(0, c1.GetInteger());
159+
Assert.Equal(bufferSegment1, c1.GetObject());
160+
161+
ReadOnlyMemory<byte> data;
162+
Assert.True(buffer.TryGet(ref c1, out data, advance: true));
163+
164+
Assert.Equal(0, c1.GetInteger());
165+
Assert.Equal(bufferSegment4, c1.GetObject());
166+
167+
Assert.True(buffer.TryGet(ref c1, out data, advance: true));
168+
169+
Assert.Equal(0, c1.GetInteger());
170+
Assert.Equal(null, c1.GetObject());
171+
172+
Assert.False(buffer.TryGet(ref c1, out data, advance: true));
112173
}
113174

114175
[Fact]
115-
public void TryGetSkipsEmptyForNext()
176+
public void TryGetSkipsEmptyForNextUsingGetPositionWithOffsetAndOrigin()
116177
{
117178
var bufferSegment1 = new BufferSegment<byte>(new byte[100]);
118179
BufferSegment<byte> bufferSegment2 = bufferSegment1.Append(new byte[0]);
119180
BufferSegment<byte> bufferSegment3 = bufferSegment2.Append(new byte[0]);
120181
BufferSegment<byte> bufferSegment4 = bufferSegment3.Append(new byte[100]);
121182

122183
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment4, 100);
123-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 0);
184+
SequencePosition c1 = buffer.GetPosition(0, buffer.Start);
124185
Assert.Equal(0, c1.GetInteger());
125186
Assert.Equal(bufferSegment1, c1.GetObject());
126187

@@ -139,15 +200,37 @@ public void TryGetSkipsEmptyForNext()
139200
}
140201

141202
[Fact]
142-
public void TryGetSkipDoesNotCrossPastEnd()
203+
public void TryGetSkipDoesNotCrossPastEndUsingGetPositionWithOffset()
204+
{
205+
var bufferSegment1 = new BufferSegment<byte>(new byte[100]);
206+
BufferSegment<byte> bufferSegment2 = bufferSegment1.Append(new byte[0]);
207+
BufferSegment<byte> bufferSegment3 = bufferSegment2.Append(new byte[0]);
208+
BufferSegment<byte> bufferSegment4 = bufferSegment3.Append(new byte[100]);
209+
210+
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 0);
211+
SequencePosition c1 = buffer.GetPosition(0);
212+
Assert.Equal(0, c1.GetInteger());
213+
Assert.Equal(bufferSegment1, c1.GetObject());
214+
215+
ReadOnlyMemory<byte> data;
216+
Assert.True(buffer.TryGet(ref c1, out data, advance: true));
217+
218+
Assert.Equal(0, c1.GetInteger());
219+
Assert.Equal(null, c1.GetObject());
220+
221+
Assert.False(buffer.TryGet(ref c1, out data, advance: true));
222+
}
223+
224+
[Fact]
225+
public void TryGetSkipDoesNotCrossPastEndUsingGetPositionWithOffsetAndOrigin()
143226
{
144227
var bufferSegment1 = new BufferSegment<byte>(new byte[100]);
145228
BufferSegment<byte> bufferSegment2 = bufferSegment1.Append(new byte[0]);
146229
BufferSegment<byte> bufferSegment3 = bufferSegment2.Append(new byte[0]);
147230
BufferSegment<byte> bufferSegment4 = bufferSegment3.Append(new byte[100]);
148231

149232
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 0);
150-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 0);
233+
SequencePosition c1 = buffer.GetPosition(0, buffer.Start);
151234
Assert.Equal(0, c1.GetInteger());
152235
Assert.Equal(bufferSegment1, c1.GetObject());
153236

src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,28 @@ public void ReadOnlyBufferDoesNotAllowSlicingOutOfRange(Action<ReadOnlySequence<
9999
public void ReadOnlyBufferGetPosition_MovesPosition()
100100
{
101101
ReadOnlySequence<byte> buffer = Factory.CreateOfSize(100);
102-
SequencePosition position = buffer.GetPosition(buffer.Start, 65);
102+
103+
SequencePosition position = buffer.GetPosition(65);
104+
Assert.Equal(buffer.Slice(position).Length, 35);
105+
106+
position = buffer.GetPosition(65, buffer.Start);
103107
Assert.Equal(buffer.Slice(position).Length, 35);
104108
}
105109

106110
[Fact]
107111
public void ReadOnlyBufferGetPosition_ChecksBounds()
108112
{
109113
ReadOnlySequence<byte> buffer = Factory.CreateOfSize(100);
110-
Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(buffer.Start, 101));
114+
Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(101));
115+
Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(101, buffer.Start));
111116
}
112117

113118
[Fact]
114119
public void ReadOnlyBufferGetPosition_DoesNotAlowNegative()
115120
{
116121
ReadOnlySequence<byte> buffer = Factory.CreateOfSize(20);
117-
Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(buffer.Start, -1));
122+
Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(-1));
123+
Assert.Throws<ArgumentOutOfRangeException>(() => buffer.GetPosition(-1, buffer.Start));
118124
}
119125

120126
public void ReadOnlyBufferSlice_ChecksEnd()
@@ -134,11 +140,17 @@ public void SegmentStartIsConsideredInBoundsCheck()
134140

135141
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 50);
136142

137-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 25); // segment 1 index 75
138-
SequencePosition c2 = buffer.GetPosition(buffer.Start, 55); // segment 2 index 5
143+
SequencePosition c1 = buffer.GetPosition(25); // segment 1 index 75
144+
SequencePosition c2 = buffer.GetPosition(55); // segment 2 index 5
139145

140146
ReadOnlySequence<byte> sliced = buffer.Slice(c1, c2);
141147
Assert.Equal(30, sliced.Length);
148+
149+
c1 = buffer.GetPosition(25, buffer.Start); // segment 1 index 75
150+
c2 = buffer.GetPosition(55, buffer.Start); // segment 2 index 5
151+
152+
sliced = buffer.Slice(c1, c2);
153+
Assert.Equal(30, sliced.Length);
142154
}
143155

144156
[Fact]
@@ -149,7 +161,12 @@ public void GetPositionPrefersNextSegment()
149161

150162
ReadOnlySequence<byte> buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 0);
151163

152-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 50);
164+
SequencePosition c1 = buffer.GetPosition(50);
165+
166+
Assert.Equal(0, c1.GetInteger());
167+
Assert.Equal(bufferSegment2, c1.GetObject());
168+
169+
c1 = buffer.GetPosition(50, buffer.Start);
153170

154171
Assert.Equal(0, c1.GetInteger());
155172
Assert.Equal(bufferSegment2, c1.GetObject());
@@ -164,7 +181,12 @@ public void GetPositionDoesNotCrossOutsideBuffer()
164181

165182
var buffer = new ReadOnlySequence<byte>(bufferSegment1, 0, bufferSegment2, 100);
166183

167-
SequencePosition c1 = buffer.GetPosition(buffer.Start, 200);
184+
SequencePosition c1 = buffer.GetPosition(200);
185+
186+
Assert.Equal(100, c1.GetInteger());
187+
Assert.Equal(bufferSegment2, c1.GetObject());
188+
189+
c1 = buffer.GetPosition(200, buffer.Start);
168190

169191
Assert.Equal(100, c1.GetInteger());
170192
Assert.Equal(bufferSegment2, c1.GetObject());
@@ -265,14 +287,19 @@ public void CopyTo_ThrowsWhenSourceLargerThenDestination()
265287
b => b.Slice(5),
266288
b => b.Slice(0).Slice(5),
267289
b => b.Slice(5, 5),
268-
b => b.Slice(b.GetPosition(b.Start, 5), 5),
269-
b => b.Slice(5, b.GetPosition(b.Start, 10)),
270-
b => b.Slice(b.GetPosition(b.Start, 5), b.GetPosition(b.Start, 10)),
290+
b => b.Slice(b.GetPosition(5), 5),
291+
b => b.Slice(5, b.GetPosition(10)),
292+
b => b.Slice(b.GetPosition(5), b.GetPosition(10)),
293+
b => b.Slice(b.GetPosition(5, b.Start), 5),
294+
b => b.Slice(5, b.GetPosition(10, b.Start)),
295+
b => b.Slice(b.GetPosition(5, b.Start), b.GetPosition(10, b.Start)),
271296

272297
b => b.Slice((long)5),
273298
b => b.Slice((long)5, 5),
274-
b => b.Slice(b.GetPosition(b.Start, 5), (long)5),
275-
b => b.Slice((long)5, b.GetPosition(b.Start, 10)),
299+
b => b.Slice(b.GetPosition(5), (long)5),
300+
b => b.Slice((long)5, b.GetPosition(10)),
301+
b => b.Slice(b.GetPosition(5, b.Start), (long)5),
302+
b => b.Slice((long)5, b.GetPosition(10, b.Start)),
276303
};
277304

278305
public static TheoryData<Action<ReadOnlySequence<byte>>> OutOfRangeSliceCases => new TheoryData<Action<ReadOnlySequence<byte>>>

0 commit comments

Comments
 (0)