diff --git a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs index e028d1a5387896..710241be6fcf18 100644 --- a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs +++ b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs @@ -11,70 +11,71 @@ namespace System.Buffers public readonly partial struct ReadOnlySequence { [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool TryGetBuffer(in SequencePosition position, out ReadOnlyMemory memory, out SequencePosition next) + internal MoveResult MoveToNextItem( + ref SequencePosition position, + bool advance) { - object? positionObject = position.GetObject(); - next = default; + object? positionObject; + ReadOnlyMemory item; + SequencePosition positionOfResult; + MoveStatus status; + MoveResult returnValue; + + positionObject = position.GetObject(); if (positionObject == null) { - memory = default; - return false; - } - - SequenceType type = GetSequenceType(); - object? endObject = _endObject; - int startIndex = position.GetInteger(); - int endIndex = GetIndex(_endInteger); - - if (type == SequenceType.MultiSegment) - { - Debug.Assert(positionObject is ReadOnlySequenceSegment); - - ReadOnlySequenceSegment startSegment = (ReadOnlySequenceSegment)positionObject; - - if (startSegment != endObject) - { - ReadOnlySequenceSegment? nextSegment = startSegment.Next; - - if (nextSegment == null) - ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); - - next = new SequencePosition(nextSegment, 0); - memory = startSegment.Memory.Slice(startIndex); - } - else - { - memory = startSegment.Memory.Slice(startIndex, endIndex - startIndex); - } + item = default; + positionOfResult = default; + status = MoveStatus.NotExist; } else { - if (positionObject != endObject) - ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); + SequenceType type = GetSequenceType(); + object? endObject = _endObject; - if (type == SequenceType.Array) + switch (type) { - Debug.Assert(positionObject is T[]); - - memory = new ReadOnlyMemory((T[])positionObject, startIndex, endIndex - startIndex); + case SequenceType.Array: + { + MoveToNextItemForArray(in position, positionObject, out item); + positionOfResult = default; + break; + } + case SequenceType.MemoryManager: + { + MoveToNextItemForMemoryManager(in position, positionObject, out item); + positionOfResult = default; + break; + } + case SequenceType.MultiSegment: + { + MoveToNextItemForMultiSegment(in position, endObject, positionObject, out item, out positionOfResult); + break; + } + case SequenceType.String + when typeof(T) == typeof(char): + { + MoveToNextItemForString(in position, positionObject, out item); + positionOfResult = default; + break; + } + default: + { + throw new NotImplementedException(); + } } - else if (typeof(T) == typeof(char) && type == SequenceType.String) - { - Debug.Assert(positionObject is string); - memory = (ReadOnlyMemory)(object)((string)positionObject).AsMemory(startIndex, endIndex - startIndex); - } - else // type == SequenceType.MemoryManager - { - Debug.Assert(type == SequenceType.MemoryManager); - Debug.Assert(positionObject is MemoryManager); + status = item.IsEmpty ? MoveStatus.Empty : MoveStatus.Success; + } - memory = ((MemoryManager)positionObject).Memory.Slice(startIndex, endIndex - startIndex); - } + if (advance) + { + position = positionOfResult; } - return true; + returnValue = new MoveResult(item, positionOfResult, status); + return returnValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -694,5 +695,141 @@ private static ReadOnlySpan GetFirstSpanSlow(object startObject, int startInd return ((MemoryManager)startObject).Memory.Span.Slice(startIndex, endIndex - startIndex); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToNextItemForArray( + in SequencePosition position, + object? positionObject, + out ReadOnlyMemory nextItem) + { + if (positionObject != _endObject) + { + ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); + } + + int startIndex = position.GetInteger(); + int endIndex = GetIndex(_endInteger); + + Debug.Assert(positionObject is T[]); + nextItem = new ReadOnlyMemory((T[])positionObject, startIndex, endIndex - startIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToNextItemForMemoryManager( + in SequencePosition position, + object? positionObject, + out ReadOnlyMemory nextItem) + { + if (positionObject != _endObject) + { + ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); + } + + int startIndex = position.GetInteger(); + int endIndex = GetIndex(_endInteger); + + Debug.Assert(positionObject is MemoryManager); + nextItem = ((MemoryManager)positionObject).Memory.Slice(startIndex, endIndex - startIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToNextItemForMultiSegment( + in SequencePosition position, + object? endObject, + object? positionObject, + out ReadOnlyMemory nextItem, + out SequencePosition next) + { + Debug.Assert(positionObject is ReadOnlySequenceSegment); + + ReadOnlyMemory processedSlice; + ReadOnlySequenceSegment startSegment = (ReadOnlySequenceSegment)positionObject; + SequencePosition processedPosition; + int startIndex; + + processedSlice = default; + processedPosition = default; + startIndex = position.GetInteger(); + + while ((startSegment != endObject) + && (processedSlice.IsEmpty)) + { + ReadOnlySequenceSegment? nextSegment = startSegment.Next; + + if (nextSegment == null) + { + ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); + } + + processedSlice = startSegment.Memory.Slice(startIndex); + processedPosition = new SequencePosition(nextSegment, 0); + startSegment = nextSegment; + startIndex = processedPosition.GetInteger(); + }; + + if (processedSlice.IsEmpty) + { + int endIndex = GetIndex(_endInteger); + nextItem = startSegment.Memory.Slice(startIndex, endIndex - startIndex); + next = default; + } + else + { + nextItem = processedSlice; + next = processedPosition; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToNextItemForString( + in SequencePosition position, + object? positionObject, + out ReadOnlyMemory nextItem) + { + if (positionObject != _endObject) + { + ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); + } + + int startIndex = position.GetInteger(); + int endIndex = GetIndex(_endInteger); + + Debug.Assert(positionObject is string); + nextItem = (ReadOnlyMemory)(object)((string)positionObject).AsMemory(startIndex, endIndex - startIndex); + } + + internal readonly struct MoveResult + { + public MoveResult( + ReadOnlyMemory item, + SequencePosition position, + MoveStatus status) + { + Item = item; + Position = position; + Status = status; + } + + public bool IsSuccess + { + get + { + return Status == MoveStatus.Success; + } + } + + public ReadOnlyMemory Item { get; } + + public SequencePosition Position { get; } + + public MoveStatus Status { get; } + } + + internal enum MoveStatus + { + Empty, + NotExist, + Success + } } } diff --git a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs index 549f8edd628bb5..9708618a89d7d2 100644 --- a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs +++ b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs @@ -599,13 +599,13 @@ public SequencePosition GetPosition(long offset, SequencePosition origin) /// public bool TryGet(ref SequencePosition position, out ReadOnlyMemory memory, bool advance = true) { - bool result = TryGetBuffer(position, out memory, out SequencePosition next); - if (advance) - { - position = next; - } + MoveResult moveResult; + + moveResult = MoveToNextItem(ref position, advance); + + memory = moveResult.Item; - return result; + return moveResult.IsSuccess; } /// diff --git a/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs b/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs index 27aef7056647dc..cc7d1c94bca507 100644 --- a/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs +++ b/src/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs @@ -209,7 +209,7 @@ private bool TryReadToInternal(out ReadOnlySequence sequence, T delimiter, bo AdvanceCurrentSpan(index); } - sequence = Sequence.Slice(copy.Position, Position); + sequence = Sequence.Slice(copy.Position, copy.Sequence.End); if (advancePastDelimiter) { Advance(1); diff --git a/src/libraries/System.Memory/src/System/Buffers/SequenceReader.cs b/src/libraries/System.Memory/src/System/Buffers/SequenceReader.cs index 78de8a555c89a2..1c0d999deaa4a9 100644 --- a/src/libraries/System.Memory/src/System/Buffers/SequenceReader.cs +++ b/src/libraries/System.Memory/src/System/Buffers/SequenceReader.cs @@ -287,24 +287,38 @@ private void GetNextSpan() if (!Sequence.IsSingleSegment) { SequencePosition previousNextPosition = _nextPosition; - while (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory memory, advance: true)) + ReadOnlySequence.MoveResult moveResult; + + moveResult = Sequence.MoveToNextItem(ref _nextPosition, true); + + switch (moveResult.Status) { - _currentPosition = previousNextPosition; - if (memory.Length > 0) - { - CurrentSpan = memory.Span; - CurrentSpanIndex = 0; - return; - } - else - { - CurrentSpan = default; - CurrentSpanIndex = 0; - previousNextPosition = _nextPosition; - } + case ReadOnlySequence.MoveStatus.Success: + { + CurrentSpan = moveResult.Item.Span; + CurrentSpanIndex = 0; + _currentPosition = previousNextPosition; + break; + } + case ReadOnlySequence.MoveStatus.Empty: + { + CurrentSpan = default; + CurrentSpanIndex = 0; + _currentPosition = previousNextPosition; + _moreData = false; + break; + } + case ReadOnlySequence.MoveStatus.NotExist: + { + _moreData = false; + break; + } } } - _moreData = false; + else + { + _moreData = false; + } } /// diff --git a/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs b/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs index 8f8ad3ceb1ee04..3ac18ce2b21b77 100644 --- a/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs +++ b/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Common.cs @@ -400,11 +400,11 @@ public void TryGetReturnsEmptySegments() var buffer = new ReadOnlySequence(bufferSegment1, 0, bufferSegment3, 0); var start = buffer.Start; - Assert.True(buffer.TryGet(ref start, out var memory)); + Assert.False(buffer.TryGet(ref start, out var memory)); Assert.Equal(0, memory.Length); - Assert.True(buffer.TryGet(ref start, out memory)); + Assert.False(buffer.TryGet(ref start, out memory)); Assert.Equal(0, memory.Length); - Assert.True(buffer.TryGet(ref start, out memory)); + Assert.False(buffer.TryGet(ref start, out memory)); Assert.Equal(0, memory.Length); Assert.False(buffer.TryGet(ref start, out memory)); } @@ -511,7 +511,7 @@ public void TryGetStopsAtEndWhenEndIsFirstItemOfFull() SequencePosition start = buffer.Start; Assert.True(buffer.TryGet(ref start, out ReadOnlyMemory memory)); Assert.Equal(100, memory.Length); - Assert.True(buffer.TryGet(ref start, out memory)); + Assert.False(buffer.TryGet(ref start, out memory)); Assert.Equal(0, memory.Length); Assert.False(buffer.TryGet(ref start, out memory)); } @@ -527,7 +527,7 @@ public void TryGetStopsAtEndWhenEndIsFirstItemOfEmpty() SequencePosition start = buffer.Start; Assert.True(buffer.TryGet(ref start, out ReadOnlyMemory memory)); Assert.Equal(100, memory.Length); - Assert.True(buffer.TryGet(ref start, out memory)); + Assert.False(buffer.TryGet(ref start, out memory)); Assert.Equal(0, memory.Length); Assert.False(buffer.TryGet(ref start, out memory)); } @@ -568,8 +568,8 @@ public void EnumerableStopsAtEndWhenEndIsFirstItemOfFull() sizes.Add(memory.Length); } - Assert.Equal(2, sizes.Count); - Assert.Equal(new[] { 100, 0 }, sizes); + Assert.Equal(1, sizes.Count); + Assert.Equal(new[] { 100 }, sizes); } [Fact] @@ -586,8 +586,8 @@ public void EnumerableStopsAtEndWhenEndIsFirstItemOfEmpty() sizes.Add(memory.Length); } - Assert.Equal(2, sizes.Count); - Assert.Equal(new[] { 100, 0 }, sizes); + Assert.Equal(1, sizes.Count); + Assert.Equal(new[] { 100 }, sizes); } #endregion diff --git a/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Empty.cs b/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Empty.cs index 1545def54ed7a0..f9699406bcb8f3 100644 --- a/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Empty.cs +++ b/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.Empty.cs @@ -133,7 +133,7 @@ public void Empty_Enumerator() ReadOnlySequence.Enumerator enumerator = buffer.GetEnumerator(); { Assert.Equal(default, enumerator.Current); - Assert.True(enumerator.MoveNext()); + Assert.False(enumerator.MoveNext()); ReadOnlyMemory memory = enumerator.Current; Assert.True(memory.IsEmpty); @@ -142,7 +142,7 @@ public void Empty_Enumerator() enumerator = new ReadOnlySequence.Enumerator(buffer); { Assert.Equal(default, enumerator.Current); - Assert.True(enumerator.MoveNext()); + Assert.False(enumerator.MoveNext()); ReadOnlyMemory memory = enumerator.Current; Assert.True(memory.IsEmpty); @@ -161,16 +161,8 @@ public void Empty_TryGet() ReadOnlyMemory memory; SequencePosition c1 = buffer.Start; - Assert.True(buffer.TryGet(ref c1, out memory, false)); - Assert.Equal(buffer.Start, c1); - Assert.True(memory.IsEmpty); - - Assert.True(buffer.TryGet(ref c1, out memory, true)); - Assert.Null(c1.GetObject()); - Assert.True(memory.IsEmpty); - Assert.False(buffer.TryGet(ref c1, out memory, false)); - Assert.Null(c1.GetObject()); + Assert.Equal(buffer.Start, c1); Assert.True(memory.IsEmpty); Assert.False(buffer.TryGet(ref c1, out memory, true)); @@ -181,21 +173,9 @@ public void Empty_TryGet() Assert.Null(c1.GetObject()); Assert.True(memory.IsEmpty); - Assert.False(buffer.TryGet(ref c1, out memory, true)); - Assert.Null(c1.GetObject()); - Assert.True(memory.IsEmpty); - c1 = buffer.End; - Assert.True(buffer.TryGet(ref c1, out memory, false)); - Assert.Equal(buffer.End, c1); - Assert.True(memory.IsEmpty); - - Assert.True(buffer.TryGet(ref c1, out memory, true)); - Assert.Null(c1.GetObject()); - Assert.True(memory.IsEmpty); - Assert.False(buffer.TryGet(ref c1, out memory, false)); - Assert.Null(c1.GetObject()); + Assert.Equal(buffer.Start, c1); Assert.True(memory.IsEmpty); Assert.False(buffer.TryGet(ref c1, out memory, true)); @@ -205,10 +185,6 @@ public void Empty_TryGet() Assert.False(buffer.TryGet(ref c1, out memory, false)); Assert.Null(c1.GetObject()); Assert.True(memory.IsEmpty); - - Assert.False(buffer.TryGet(ref c1, out memory, true)); - Assert.Null(c1.GetObject()); - Assert.True(memory.IsEmpty); } #endregion diff --git a/src/libraries/System.Memory/tests/SequenceReader/Advance.cs b/src/libraries/System.Memory/tests/SequenceReader/Advance.cs index c2cbf0fc72771b..b738e4f3e3953a 100644 --- a/src/libraries/System.Memory/tests/SequenceReader/Advance.cs +++ b/src/libraries/System.Memory/tests/SequenceReader/Advance.cs @@ -67,7 +67,7 @@ public void PastEmptySegments() reader.Advance(1); Assert.Equal(0, reader.CurrentSpanIndex); Assert.Equal(0, reader.CurrentSpan.Length); - Assert.False(reader.TryPeek(out byte value)); + Assert.False(reader.TryPeek(out byte _)); ReadOnlySequence sequence = reader.Sequence.Slice(reader.Position); Assert.Equal(0, sequence.Length); } diff --git a/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs b/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs index ab7399d30bce7e..cd6255df3648a4 100644 --- a/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs +++ b/src/libraries/System.Memory/tests/SequenceReader/ReadTo.cs @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Linq; using System.Buffers; +using System.Linq; using Xunit; namespace System.Memory.Tests.SequenceReader diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs index 8635031292e32c..e7f4caa790e1f9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs @@ -68,28 +68,17 @@ public Utf8JsonReader(ReadOnlySequence jsonData, bool isFinalBlock, JsonRe bool firstSegmentIsEmpty = _buffer.Length == 0; if (firstSegmentIsEmpty) { - // Once we find a non-empty segment, we need to set current position to it. - // Therefore, track the next position in a copy before it gets advanced to the next segment. - SequencePosition previousNextPosition = _nextPosition; - while (jsonData.TryGet(ref _nextPosition, out ReadOnlyMemory memory, advance: true)) + if (jsonData.TryGet(ref _currentPosition, out ReadOnlyMemory memory, advance: true)) { - // _currentPosition should point to the segment right befor the segment that _nextPosition points to. - _currentPosition = previousNextPosition; if (memory.Length != 0) { _buffer = memory.Span; - break; } - previousNextPosition = _nextPosition; } + _nextPosition = _currentPosition; } - // If firstSegmentIsEmpty is true, - // only check if we have reached the last segment but do not advance _nextPosition. The while loop above already advanced it. - // Otherwise, we would end up skipping a segment (i.e. advance = false). - // If firstSegmentIsEmpty is false, - // make sure to advance _nextPosition so that it is no longer the same as _currentPosition (i.e. advance = true). - _isLastSegment = !jsonData.TryGet(ref _nextPosition, out _, advance: !firstSegmentIsEmpty) && isFinalBlock; // Don't re-order to avoid short-circuiting + _isLastSegment = !jsonData.TryGet(ref _nextPosition, out _, advance: true) && isFinalBlock; // Don't re-order to avoid short-circuiting Debug.Assert(!_nextPosition.Equals(_currentPosition));