From d0ca6de52a724b4bb688565f3800a482ec7376df Mon Sep 17 00:00:00 2001 From: Krzysztof Cwalina Date: Tue, 2 Jan 2018 14:40:02 -0800 Subject: [PATCH] BufferReader over sequences (#2015) * Simplified Position * Changed BufferReader to work over sequences --- .../System/Buffers/ReadOnlyBufferReader.cs | 33 ++++++++----- src/System.Text.Http.Parser/HttpParser.cs | 8 ++-- tests/Benchmarks/BytesReaderBench.cs | 2 +- .../ReadableBufferReaderFacts.cs | 48 +++++++++---------- .../ReadCursorOperationsThroughput.cs | 4 +- 5 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs b/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs index b39be58dd7d..08f520e0a8c 100644 --- a/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs +++ b/src/System.Buffers.Primitives/System/Buffers/ReadOnlyBufferReader.cs @@ -6,20 +6,32 @@ namespace System.Buffers { - public ref struct BufferReader + public class BufferReader + { + public static BufferReader Create(TSequence buffer) where TSequence : ISequence> + { + return new BufferReader(buffer); + } + } + + public ref struct BufferReader where TSequence : ISequence> { private ReadOnlySpan _currentSpan; private int _index; - private ReadOnlyBuffer.Enumerator _enumerator; + private TSequence _sequence; + private Position _currentPosition; + private Position _nextPosition; private int _consumedBytes; private bool _end; - public BufferReader(ReadOnlyBuffer buffer) + public BufferReader(TSequence buffer) { _end = false; _index = 0; _consumedBytes = 0; - _enumerator = buffer.GetEnumerator(); + _sequence = buffer; + _currentPosition = _sequence.Start; + _nextPosition = _currentPosition; _currentSpan = default; MoveNext(); } @@ -28,7 +40,7 @@ public BufferReader(ReadOnlyBuffer buffer) public int Index => _index; - public Position Cursor => _enumerator.CreateCursor(_index); + public Position Position => _currentPosition + _index; public ReadOnlySpan Span => _currentSpan; @@ -68,18 +80,17 @@ public int Take() [MethodImpl(MethodImplOptions.NoInlining)] private void MoveNext() { - while (_enumerator.MoveNext()) + var previous = _nextPosition; + while(_sequence.TryGet(ref _nextPosition, out var memory, true)) { + _currentPosition = previous; + _currentSpan = memory.Span; _index = 0; - var memory = _enumerator.Current; - var length = memory.Length; - if (length != 0) + if (_currentSpan.Length > 0) { - _currentSpan = memory.Span; return; } } - _end = true; } diff --git a/src/System.Text.Http.Parser/HttpParser.cs b/src/System.Text.Http.Parser/HttpParser.cs index d6ee38fbf55..17ab3e91eaa 100644 --- a/src/System.Text.Http.Parser/HttpParser.cs +++ b/src/System.Text.Http.Parser/HttpParser.cs @@ -237,8 +237,8 @@ public unsafe bool ParseHeaders(T handler, in ReadOnlyBuffer buffer, out Posi var bufferEnd = buffer.End; - var reader = new BufferReader(buffer); - var start = default(BufferReader); + var reader = BufferReader.Create(buffer); + var start = default(BufferReader); var done = false; try @@ -319,7 +319,7 @@ public unsafe bool ParseHeaders(T handler, in ReadOnlyBuffer buffer, out Posi } else { - var current = reader.Cursor; + var current = reader.Position; // Split buffers if (ReadOnlyBuffer.Seek(current, bufferEnd, out var lineEnd, ByteLF) == -1) @@ -355,7 +355,7 @@ public unsafe bool ParseHeaders(T handler, in ReadOnlyBuffer buffer, out Posi } finally { - consumed = reader.Cursor; + consumed = reader.Position; consumedBytes = reader.ConsumedBytes; if (done) diff --git a/tests/Benchmarks/BytesReaderBench.cs b/tests/Benchmarks/BytesReaderBench.cs index 9606cbca6cc..b786ba7f79f 100644 --- a/tests/Benchmarks/BytesReaderBench.cs +++ b/tests/Benchmarks/BytesReaderBench.cs @@ -80,7 +80,7 @@ static void ParseInt32ReadableBufferReader() foreach (var iteration in Benchmark.Iterations) { var buffer = new ReadOnlyBuffer(s_data); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); using (iteration.StartMeasurement()) { diff --git a/tests/System.Buffers.Primitives.Tests/ReadableBufferReaderFacts.cs b/tests/System.Buffers.Primitives.Tests/ReadableBufferReaderFacts.cs index 5fa5ddc38be..a1d55a76e28 100644 --- a/tests/System.Buffers.Primitives.Tests/ReadableBufferReaderFacts.cs +++ b/tests/System.Buffers.Primitives.Tests/ReadableBufferReaderFacts.cs @@ -30,7 +30,7 @@ internal SingleSegment(ReadOnlyBufferFactory factory) : base(factory) { } [Fact] public void SkipSingleBufferSkipsBytes() { - var reader = new BufferReader(BufferUtilities.CreateBuffer(new byte[] { 1, 2, 3, 4, 5 })); + var reader = BufferReader.Create(BufferUtilities.CreateBuffer(new byte[] { 1, 2, 3, 4, 5 })); reader.Skip(2); Assert.Equal(2, reader.Index); Assert.Equal(3, reader.Span[reader.Index]); @@ -44,7 +44,7 @@ public void SkipSingleBufferSkipsBytes() [Fact] public void TakeReturnsByteAndMoves() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { 1, 2 })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2 })); Assert.Equal(0, reader.Index); Assert.Equal(1, reader.Span[reader.Index]); Assert.Equal(1, reader.Take()); @@ -71,7 +71,7 @@ internal ReadableBufferReaderFacts(ReadOnlyBufferFactory factory) [Fact] public void PeekReturnsByteWithoutMoving() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { 1, 2 })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2 })); Assert.Equal(1, reader.Peek()); Assert.Equal(1, reader.Peek()); } @@ -79,7 +79,7 @@ public void PeekReturnsByteWithoutMoving() [Fact] public void CursorIsCorrectAtEnd() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { 1, 2 })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2 })); reader.Take(); reader.Take(); Assert.True(reader.End); @@ -98,19 +98,19 @@ public void CursorIsCorrectWithEmptyLastBlock() var start = new Position(first, first.Start); var end = new Position(last, last.Start); - var reader = new BufferReader(new ReadOnlyBuffer(start, end)); + var reader = BufferReader.Create(new ReadOnlyBuffer(start, end)); reader.Take(); reader.Take(); reader.Take(); - Assert.Same(last, reader.Cursor.Segment); - Assert.Equal(0, reader.Cursor.Index); + Assert.Same(last, reader.Position.Segment); + Assert.Equal(0, reader.Position.Index); Assert.True(reader.End); } [Fact] public void PeekReturnsMinuOneByteInTheEnd() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { 1, 2 })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2 })); Assert.Equal(1, reader.Take()); Assert.Equal(2, reader.Take()); Assert.Equal(-1, reader.Peek()); @@ -119,7 +119,7 @@ public void PeekReturnsMinuOneByteInTheEnd() [Fact] public void SkipToEndThenPeekReturnsMinusOne() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { 1, 2, 3, 4, 5 })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2, 3, 4, 5 })); reader.Skip(5); Assert.True(reader.End); Assert.Equal(-1, reader.Peek()); @@ -128,7 +128,7 @@ public void SkipToEndThenPeekReturnsMinusOne() [Fact] public void SkippingPastLengthThrows() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { 1, 2, 3, 4, 5 })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { 1, 2, 3, 4, 5 })); try { reader.Skip(6); @@ -144,7 +144,7 @@ public void SkippingPastLengthThrows() public void CtorFindsFirstNonEmptySegment() { var buffer = Factory.CreateWithContent(new byte[] { 1 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); Assert.Equal(1, reader.Peek()); } @@ -153,7 +153,7 @@ public void CtorFindsFirstNonEmptySegment() public void EmptySegmentsAreSkippedOnMoveNext() { var buffer = Factory.CreateWithContent(new byte[] { 1, 2 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); Assert.Equal(1, reader.Peek()); reader.Skip(1); @@ -164,7 +164,7 @@ public void EmptySegmentsAreSkippedOnMoveNext() public void PeekGoesToEndIfAllEmptySegments() { var buffer = BufferUtilities.CreateBuffer(new[] { new byte[] { }, new byte[] { }, new byte[] { }, new byte[] { } }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); Assert.Equal(-1, reader.Peek()); Assert.True(reader.End); @@ -174,7 +174,7 @@ public void PeekGoesToEndIfAllEmptySegments() public void SkipTraversesSegments() { var buffer = Factory.CreateWithContent(new byte[] { 1, 2, 3 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); reader.Skip(2); Assert.Equal(3, reader.Span[reader.Index]); @@ -185,7 +185,7 @@ public void SkipTraversesSegments() public void SkipThrowsPastLengthMultipleSegments() { var buffer = Factory.CreateWithContent(new byte[] { 1, 2, 3 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); try { @@ -202,7 +202,7 @@ public void SkipThrowsPastLengthMultipleSegments() public void TakeTraversesSegments() { var buffer = Factory.CreateWithContent(new byte[] { 1, 2, 3 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); Assert.Equal(1, reader.Take()); Assert.Equal(2, reader.Take()); @@ -214,7 +214,7 @@ public void TakeTraversesSegments() public void PeekTraversesSegments() { var buffer = Factory.CreateWithContent(new byte[] { 1, 2 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); Assert.Equal(1, reader.Span[reader.Index]); Assert.Equal(1, reader.Take()); @@ -230,7 +230,7 @@ public void PeekTraversesSegments() public void PeekWorkesWithEmptySegments() { var buffer = Factory.CreateWithContent(new byte[] { 1 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); Assert.Equal(0, reader.Index); Assert.Equal(1, reader.Span.Length); @@ -243,7 +243,7 @@ public void PeekWorkesWithEmptySegments() [Fact] public void WorkesWithEmptyBuffer() { - var reader = new BufferReader(Factory.CreateWithContent(new byte[] { })); + var reader = BufferReader.Create(Factory.CreateWithContent(new byte[] { })); Assert.Equal(0, reader.Index); Assert.Equal(0, reader.Span.Length); @@ -261,14 +261,14 @@ public void WorkesWithEmptyBuffer() public void ReturnsCorrectCursor(int takes, bool end) { var readableBuffer = Factory.CreateWithContent(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); - var reader = new BufferReader(readableBuffer); + var reader = BufferReader.Create(readableBuffer); for (int i = 0; i < takes; i++) { reader.Take(); } var expected = end ? new byte[] {} : readableBuffer.Slice((long)takes).ToArray(); - Assert.Equal(expected, readableBuffer.Slice(reader.Cursor).ToArray()); + Assert.Equal(expected, readableBuffer.Slice(reader.Position).ToArray()); } [Fact] @@ -277,8 +277,8 @@ public void SlicingBufferReturnsCorrectCursor() var buffer = Factory.CreateWithContent(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); var sliced = buffer.Slice(2L); - var reader = new BufferReader(sliced); - Assert.Equal(sliced.ToArray(), buffer.Slice(reader.Cursor).ToArray()); + var reader = BufferReader.Create(sliced); + Assert.Equal(sliced.ToArray(), buffer.Slice(reader.Position).ToArray()); Assert.Equal(2, reader.Peek()); Assert.Equal(0, reader.Index); } @@ -287,7 +287,7 @@ public void SlicingBufferReturnsCorrectCursor() public void ReaderIndexIsCorrect() { var buffer = Factory.CreateWithContent(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); var counter = 1; while (!reader.End) diff --git a/tests/System.IO.Pipelines.Performance.Tests/ReadCursorOperationsThroughput.cs b/tests/System.IO.Pipelines.Performance.Tests/ReadCursorOperationsThroughput.cs index ad623d08984..1552342864b 100644 --- a/tests/System.IO.Pipelines.Performance.Tests/ReadCursorOperationsThroughput.cs +++ b/tests/System.IO.Pipelines.Performance.Tests/ReadCursorOperationsThroughput.cs @@ -108,7 +108,7 @@ public void SeekLiveAspNetMultiBufferReadableBufferReader() private static void FindAllNewLinesReadableBufferReader(ReadOnlyBuffer buffer) { - var reader = new BufferReader(buffer); + var reader = BufferReader.Create(buffer); var end = buffer.End; while (!reader.End) @@ -128,7 +128,7 @@ private static void FindAllNewLinesReadableBufferReader(ReadOnlyBuffer buffer) if (length == -1) { - var current = reader.Cursor; + var current = reader.Position; if (ReadOnlyBuffer.Seek(current, end, out var found, (byte)'\n') == -1) {