Implement AdvanceToEnd,UnreadSequence (#388)
authorMarco Rossignoli <marco.rossignoli@gmail.com>
Sat, 7 Dec 2019 01:41:08 +0000 (02:41 +0100)
committerAhson Khan <ahson_ahmedk@yahoo.com>
Sat, 7 Dec 2019 01:41:08 +0000 (17:41 -0800)
* UnreadSequence, AdvanceToEnd

* default _nextPosition

* add some tests

* remove AggressiveInlining

* address PR feedback

* Update src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs

Co-Authored-By: Ahson Khan <ahkha@microsoft.com>
* Update src/libraries/System.Memory/src/System/Buffers/SequenceReader.cs

Co-Authored-By: Ahson Khan <ahkha@microsoft.com>
* fix docs, add some tests

src/libraries/System.Memory/ref/System.Memory.cs
src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs
src/libraries/System.Memory/src/System/Buffers/SequenceReader.cs
src/libraries/System.Memory/tests/SequenceReader/BasicTests.cs

index 8ce1a84..2457f00 100644 (file)
@@ -422,6 +422,7 @@ namespace System.Buffers
         public readonly System.SequencePosition Position { get { throw null; } }
         public readonly long Remaining { get { throw null; } }
         public readonly System.Buffers.ReadOnlySequence<T> Sequence { get { throw null; } }
+        public readonly System.Buffers.ReadOnlySequence<T> UnreadSequence { get { throw null; } }
         public readonly System.ReadOnlySpan<T> UnreadSpan { get { throw null; } }
         public void Advance(long count) { }
         public long AdvancePast(T value) { throw null; }
@@ -429,6 +430,7 @@ namespace System.Buffers
         public long AdvancePastAny(T value0, T value1) { throw null; }
         public long AdvancePastAny(T value0, T value1, T value2) { throw null; }
         public long AdvancePastAny(T value0, T value1, T value2, T value3) { throw null; }
+        public void AdvanceToEnd() { throw null; }
         public bool IsNext(System.ReadOnlySpan<T> next, bool advancePast = false) { throw null; }
         public bool IsNext(T next, bool advancePast = false) { throw null; }
         public void Rewind(long count) { }
index 082d42c..04def8f 100644 (file)
@@ -681,6 +681,22 @@ namespace System.Buffers
         }
 
         /// <summary>
+        /// Moves the reader to the end of the sequence.
+        /// </summary>
+        public void AdvanceToEnd()
+        {
+            if (_moreData)
+            {
+                Consumed = Length;
+                CurrentSpan = default;
+                CurrentSpanIndex = 0;
+                _currentPosition = Sequence.End;
+                _nextPosition = default;
+                _moreData = false;
+            }
+        }
+
+        /// <summary>
         /// Check to see if the given <paramref name="next"/> value is next.
         /// </summary>
         /// <param name="next">The value to compare the next items to.</param>
index b3ca47a..b94b49e 100644 (file)
@@ -50,6 +50,14 @@ namespace System.Buffers
         public readonly ReadOnlySequence<T> Sequence { get; }
 
         /// <summary>
+        /// Gets the unread portion of the <see cref="Sequence"/>.
+        /// </summary>
+        /// <value>
+        /// The unread portion of the <see cref="Sequence"/>.
+        /// </value>
+        public readonly ReadOnlySequence<T> UnreadSequence => Sequence.Slice(Position);
+
+        /// <summary>
         /// The current position in the <see cref="Sequence"/>.
         /// </summary>
         public readonly SequencePosition Position
index 75cfde3..500cedf 100644 (file)
@@ -112,6 +112,7 @@ namespace System.Memory.Tests.SequenceReader
             SequenceReader<T> reader = default;
             Assert.Equal(0, reader.CurrentSpan.Length);
             Assert.Equal(0, reader.UnreadSpan.Length);
+            Assert.Equal(0, reader.UnreadSequence.Length);
             Assert.Equal(0, reader.Consumed);
             Assert.Equal(0, reader.CurrentSpanIndex);
             Assert.Equal(0, reader.Length);
@@ -138,6 +139,7 @@ namespace System.Memory.Tests.SequenceReader
             Assert.False(reader.TryAdvanceToAny(array));
             Assert.Equal(0, reader.CurrentSpan.Length);
             Assert.Equal(0, reader.UnreadSpan.Length);
+            Assert.Equal(0, reader.UnreadSequence.Length);
             Assert.Equal(0, reader.Consumed);
             Assert.Equal(0, reader.CurrentSpanIndex);
             Assert.Equal(0, reader.Length);
@@ -646,6 +648,209 @@ namespace System.Memory.Tests.SequenceReader
         }
 
         [Fact]
+        public void AdvanceTo_End()
+        {
+            ReadOnlySpan<T> data = (T[])_inputData.Clone();
+
+            SequenceSegment<T> last = new SequenceSegment<T>();
+            last.SetMemory(new OwnedArray<T>(data.Slice(5).ToArray()), 0, 5);
+
+            SequenceSegment<T> first = new SequenceSegment<T>();
+            first.SetMemory(new OwnedArray<T>(data.Slice(0, 5).ToArray()), 0, 5);
+            first.SetNext(last);
+
+            ReadOnlySequence<T> sequence = new ReadOnlySequence<T>(first, first.Start, last, last.End);
+            SequenceReader<T> reader = new SequenceReader<T>(sequence);
+
+            reader.AdvanceToEnd();
+
+            Assert.Equal(data.Length, reader.Length);
+            Assert.Equal(data.Length, reader.Consumed);
+            Assert.Equal(reader.Length, reader.Consumed);
+            Assert.True(reader.End);
+            Assert.Equal(0, reader.CurrentSpanIndex);
+            Assert.Equal(sequence.End, reader.Position);
+            Assert.Equal(0, reader.Remaining);
+            Assert.True(default == reader.UnreadSpan);
+            Assert.True(default == reader.CurrentSpan);
+        }
+
+        [Fact]
+        public void AdvanceTo_End_EmptySegment()
+        {
+            ReadOnlySpan<T> data = (T[])_inputData.Clone();
+
+            // Empty segment
+            SequenceSegment<T> third = new SequenceSegment<T>();
+
+            SequenceSegment<T> second = new SequenceSegment<T>();
+            second.SetMemory(new OwnedArray<T>(data.Slice(5).ToArray()), 0, 5);
+            second.SetNext(third);
+
+            SequenceSegment<T> first = new SequenceSegment<T>();
+            first.SetMemory(new OwnedArray<T>(data.Slice(0, 5).ToArray()), 0, 5);
+            first.SetNext(second);
+
+            ReadOnlySequence<T> sequence = new ReadOnlySequence<T>(first, first.Start, third, third.End);
+            SequenceReader<T> reader = new SequenceReader<T>(sequence);
+
+            reader.AdvanceToEnd();
+
+            Assert.Equal(first.Length + second.Length, reader.Length);
+            Assert.Equal(first.Length + second.Length, reader.Consumed);
+            Assert.Equal(reader.Length, reader.Consumed);
+            Assert.True(reader.End);
+            Assert.Equal(0, reader.CurrentSpanIndex);
+            Assert.Equal(sequence.End, reader.Position);
+            Assert.Equal(0, reader.Remaining);
+            Assert.True(default == reader.UnreadSpan);
+            Assert.True(default == reader.CurrentSpan);
+        }
+
+        [Fact]
+        public void AdvanceTo_End_Rewind_Advance()
+        {
+            ReadOnlySpan<T> data = (T[])_inputData.Clone();
+
+            SequenceSegment<T> last = new SequenceSegment<T>();
+            last.SetMemory(new OwnedArray<T>(data.Slice(5).ToArray()), 0, 5);
+
+            SequenceSegment<T> first = new SequenceSegment<T>();
+            first.SetMemory(new OwnedArray<T>(data.Slice(0, 5).ToArray()), 0, 5);
+            first.SetNext(last);
+
+            ReadOnlySequence<T> sequence = new ReadOnlySequence<T>(first, first.Start, last, last.End);
+            SequenceReader<T> reader = new SequenceReader<T>(sequence);
+
+            reader.AdvanceToEnd();
+
+            Assert.Equal(data.Length, reader.Length);
+            Assert.Equal(data.Length, reader.Consumed);
+            Assert.Equal(reader.Length, reader.Consumed);
+            Assert.True(reader.End);
+            Assert.Equal(0, reader.CurrentSpanIndex);
+            Assert.Equal(sequence.End, reader.Position);
+            Assert.Equal(0, reader.Remaining);
+            Assert.True(default == reader.UnreadSpan);
+            Assert.True(default == reader.CurrentSpan);
+
+            // Rewind to second element
+            reader.Rewind(9);
+
+            Assert.Equal(1, reader.Consumed);
+            Assert.False(reader.End);
+            Assert.Equal(1, reader.CurrentSpanIndex);
+            Assert.Equal(9, reader.Remaining);
+            Assert.Equal(sequence.Slice(1), reader.UnreadSequence);
+
+            // Consume next five elements and stop at second element of second segment
+            reader.Advance(5);
+
+            Assert.Equal(6, reader.Consumed);
+            Assert.False(reader.End);
+            Assert.Equal(1, reader.CurrentSpanIndex);
+            Assert.Equal(4, reader.Remaining);
+            Assert.Equal(sequence.Slice(6), reader.UnreadSequence);
+
+            reader.AdvanceToEnd();
+
+            Assert.Equal(data.Length, reader.Length);
+            Assert.Equal(data.Length, reader.Consumed);
+            Assert.Equal(reader.Length, reader.Consumed);
+            Assert.True(reader.End);
+            Assert.Equal(0, reader.CurrentSpanIndex);
+            Assert.Equal(sequence.End, reader.Position);
+            Assert.Equal(0, reader.Remaining);
+            Assert.True(default == reader.UnreadSpan);
+            Assert.True(default == reader.CurrentSpan);
+        }
+
+        [Fact]
+        public void AdvanceTo_End_Multiple()
+        {
+            ReadOnlySpan<T> data = (T[])_inputData.Clone();
+
+            SequenceSegment<T> last = new SequenceSegment<T>();
+            last.SetMemory(new OwnedArray<T>(data.Slice(5).ToArray()), 0, 5);
+
+            SequenceSegment<T> first = new SequenceSegment<T>();
+            first.SetMemory(new OwnedArray<T>(data.Slice(0, 5).ToArray()), 0, 5);
+            first.SetNext(last);
+
+            ReadOnlySequence<T> sequence = new ReadOnlySequence<T>(first, first.Start, last, last.End);
+            SequenceReader<T> reader = new SequenceReader<T>(sequence);
+
+            reader.AdvanceToEnd();
+            reader.AdvanceToEnd();
+            reader.AdvanceToEnd();
+
+            Assert.Equal(data.Length, reader.Length);
+            Assert.Equal(data.Length, reader.Consumed);
+            Assert.Equal(reader.Length, reader.Consumed);
+            Assert.True(reader.End);
+            Assert.Equal(0, reader.CurrentSpanIndex);
+            Assert.Equal(sequence.End, reader.Position);
+            Assert.Equal(0, reader.Remaining);
+            Assert.True(default == reader.UnreadSpan);
+            Assert.True(default == reader.CurrentSpan);
+        }
+
+        [Fact]
+        public void UnreadSequence()
+        {
+            ReadOnlySpan<T> data = (T[])_inputData.Clone();
+
+            SequenceSegment<T> last = new SequenceSegment<T>();
+            last.SetMemory(new OwnedArray<T>(data.Slice(5).ToArray()), 0, 5);
+
+            SequenceSegment<T> first = new SequenceSegment<T>();
+            first.SetMemory(new OwnedArray<T>(data.Slice(0, 5).ToArray()), 0, 5);
+            first.SetNext(last);
+
+            ReadOnlySequence<T> sequence = new ReadOnlySequence<T>(first, first.Start, last, last.End);
+            SequenceReader<T> reader = new SequenceReader<T>(sequence);
+
+            Assert.Equal(sequence, reader.UnreadSequence);
+            Assert.Equal(data.Length, reader.UnreadSequence.Length);
+            Assert.True(reader.TryRead(out T _));
+            Assert.True(reader.TryRead(out T _));
+            Assert.Equal(sequence.Slice(2), reader.UnreadSequence);
+            // Advance to the end
+            reader.Advance(8);
+            Assert.Equal(0, reader.UnreadSequence.Length);
+        }
+
+        [Fact]
+        public void UnreadSequence_EmptySegment()
+        {
+            ReadOnlySpan<T> data = (T[])_inputData.Clone();
+
+            // Empty segment
+            SequenceSegment<T> third = new SequenceSegment<T>();
+
+            SequenceSegment<T> second = new SequenceSegment<T>();
+            second.SetMemory(new OwnedArray<T>(data.Slice(5).ToArray()), 0, 5);
+            second.SetNext(third);
+
+            SequenceSegment<T> first = new SequenceSegment<T>();
+            first.SetMemory(new OwnedArray<T>(data.Slice(0, 5).ToArray()), 0, 5);
+            first.SetNext(second);
+
+            ReadOnlySequence<T> sequence = new ReadOnlySequence<T>(first, first.Start, third, third.End);
+            SequenceReader<T> reader = new SequenceReader<T>(sequence);
+
+            // Drain until the expected end of data with simple read
+            for (int i = 0; i < data.Length; i++)
+            {
+                reader.TryRead(out T _);
+            }
+
+            Assert.Equal(sequence.Slice(data.Length), reader.UnreadSequence);
+            Assert.Equal(0, reader.UnreadSequence.Length);
+            Assert.False(reader.TryRead(out T _));
+        }
+
+        [Fact]
         public void CopyToSmallerBufferWorks()
         {
             T[] content = (T[])_inputData.Clone();