Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEgor Bogatov <egorbo@gmail.com>2019-09-26 17:59:39 +0300
committerSteve Pfister <steveisok@users.noreply.github.com>2019-09-26 17:59:39 +0300
commit4e02deca04fe66935c76e1475bd9e00f4377821f (patch)
tree0f0b79f03431b867af5ed1b77921668cceb03e06 /src
parent65712075da8062eb2d9304104c110185001f8541 (diff)
Reflect recent NS21 changes (#348)
Diffstat (limited to 'src')
-rw-r--r--src/Common/src/CoreLib/System/Math.cs14
-rw-r--r--src/System.Data.DataSetExtensions/src/System/Data/DataTableExtensions.cs22
-rw-r--r--src/System.Memory/src/System/Buffers/ReadOnlySequence.cs233
-rw-r--r--src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs390
-rw-r--r--src/System.Memory/src/System/Buffers/StandardFormat.cs111
-rw-r--r--src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs17
6 files changed, 574 insertions, 213 deletions
diff --git a/src/Common/src/CoreLib/System/Math.cs b/src/Common/src/CoreLib/System/Math.cs
index 27ef7752de..96795af41e 100644
--- a/src/Common/src/CoreLib/System/Math.cs
+++ b/src/Common/src/CoreLib/System/Math.cs
@@ -132,6 +132,20 @@ namespace System
return div;
}
+ internal static uint DivRem(uint a, uint b, out uint result)
+ {
+ uint div = a / b;
+ result = a - (div * b);
+ return div;
+ }
+
+ internal static ulong DivRem(ulong a, ulong b, out ulong result)
+ {
+ ulong div = a / b;
+ result = a - (div * b);
+ return div;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static decimal Ceiling(decimal d)
{
diff --git a/src/System.Data.DataSetExtensions/src/System/Data/DataTableExtensions.cs b/src/System.Data.DataSetExtensions/src/System/Data/DataTableExtensions.cs
index 593edb080d..a66ae22c0e 100644
--- a/src/System.Data.DataSetExtensions/src/System/Data/DataTableExtensions.cs
+++ b/src/System.Data.DataSetExtensions/src/System/Data/DataTableExtensions.cs
@@ -218,5 +218,27 @@ namespace System.Data
Debug.Assert(null != table, "null DataTable");
return table;
}
+
+
+ /// <summary>
+ /// Creates a LinkDataView of DataRow over the input table.
+ /// </summary>
+ /// <param name="table">DataTable that the view is over.</param>
+ /// <returns>An instance of LinkDataView.</returns>
+ public static DataView AsDataView(this DataTable table)
+ {
+ throw new PlatformNotSupportedException ();
+ }
+
+ /// <summary>
+ /// Creates a LinqDataView from EnumerableDataTable
+ /// </summary>
+ /// <typeparam name="T">Type of the row in the table. Must inherit from DataRow</typeparam>
+ /// <param name="source">The enumerable-datatable over which view must be created.</param>
+ /// <returns>Generated LinkDataView of type T</returns>
+ public static DataView AsDataView<T>(this EnumerableRowCollection<T> source) where T : DataRow
+ {
+ throw new PlatformNotSupportedException ();
+ }
}
}
diff --git a/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs b/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs
index f8d4046557..34a2997cd3 100644
--- a/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs
+++ b/src/System.Memory/src/System/Buffers/ReadOnlySequence.cs
@@ -3,11 +3,10 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-#if !FEATURE_PORTABLE_SPAN
using Internal.Runtime.CompilerServices;
-#endif // FEATURE_PORTABLE_SPAN
namespace System.Buffers
{
@@ -18,17 +17,16 @@ namespace System.Buffers
[DebuggerDisplay("{ToString(),raw}")]
public readonly partial struct ReadOnlySequence<T>
{
- private readonly SequencePosition _sequenceStart;
- private readonly SequencePosition _sequenceEnd;
+ // The data is essentially two SequencePositions, however the Start and End SequencePositions are deconstructed to improve packing.
+ private readonly object _startObject;
+ private readonly object _endObject;
+ private readonly int _startInteger;
+ private readonly int _endInteger;
/// <summary>
/// Returns empty <see cref="ReadOnlySequence{T}"/>
/// </summary>
-#if FEATURE_PORTABLE_SPAN
- public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(SpanHelpers.PerTypeValues<T>.EmptyArray);
-#else
public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(Array.Empty<T>());
-#endif // FEATURE_PORTABLE_SPAN
/// <summary>
/// Length of the <see cref="ReadOnlySequence{T}"/>.
@@ -46,7 +44,7 @@ namespace System.Buffers
public bool IsSingleSegment
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _sequenceStart.GetObject() == _sequenceEnd.GetObject();
+ get => _startObject == _endObject;
}
/// <summary>
@@ -55,14 +53,27 @@ namespace System.Buffers
public ReadOnlyMemory<T> First => GetFirstBuffer();
/// <summary>
+ /// Gets <see cref="ReadOnlySpan{T}"/> from the first segment.
+ /// </summary>
+ public ReadOnlySpan<T> FirstSpan => GetFirstSpan();
+
+ /// <summary>
/// A position to the start of the <see cref="ReadOnlySequence{T}"/>.
/// </summary>
- public SequencePosition Start => _sequenceStart;
+ public SequencePosition Start
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => new SequencePosition(_startObject, _startInteger);
+ }
/// <summary>
/// A position to the end of the <see cref="ReadOnlySequence{T}"/>
/// </summary>
- public SequencePosition End => _sequenceEnd;
+ public SequencePosition End
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => new SequencePosition(_endObject, _endInteger);
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySequence(object startSegment, int startIndexAndFlags, object endSegment, int endIndexAndFlags)
@@ -73,8 +84,10 @@ namespace System.Buffers
Debug.Assert((startSegment != null && endSegment != null) ||
(startSegment == null && endSegment == null && startIndexAndFlags == 0 && endIndexAndFlags == 0));
- _sequenceStart = new SequencePosition(startSegment, startIndexAndFlags);
- _sequenceEnd = new SequencePosition(endSegment, endIndexAndFlags);
+ _startObject = startSegment;
+ _endObject = endSegment;
+ _startInteger = startIndexAndFlags;
+ _endInteger = endIndexAndFlags;
}
/// <summary>
@@ -91,24 +104,28 @@ namespace System.Buffers
(startSegment == endSegment && endIndex < startIndex))
ThrowHelper.ThrowArgumentValidationException(startSegment, startIndex, endSegment);
- _sequenceStart = new SequencePosition(startSegment, ReadOnlySequence.SegmentToSequenceStart(startIndex));
- _sequenceEnd = new SequencePosition(endSegment, ReadOnlySequence.SegmentToSequenceEnd(endIndex));
+ _startObject = startSegment;
+ _endObject = endSegment;
+ _startInteger = ReadOnlySequence.SegmentToSequenceStart(startIndex);
+ _endInteger = ReadOnlySequence.SegmentToSequenceEnd(endIndex);
}
/// <summary>
- /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="T:T[]"/>.
+ /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the array.
/// </summary>
public ReadOnlySequence(T[] array)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(0));
- _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(array.Length));
+ _startObject = array;
+ _endObject = array;
+ _startInteger = ReadOnlySequence.ArrayToSequenceStart(0);
+ _endInteger = ReadOnlySequence.ArrayToSequenceEnd(array!.Length); // TODO-NULLABLE: Remove ! when [DoesNotReturn] respected
}
/// <summary>
- /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="T:T[]"/>, start and index.
+ /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the array, start, and index.
/// </summary>
public ReadOnlySequence(T[] array, int start, int length)
{
@@ -117,8 +134,10 @@ namespace System.Buffers
(uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentValidationException(array, start);
- _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start));
- _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + length));
+ _startObject = array;
+ _endObject = array;
+ _startInteger = ReadOnlySequence.ArrayToSequenceStart(start);
+ _endInteger = ReadOnlySequence.ArrayToSequenceEnd(start + length);
}
/// <summary>
@@ -127,40 +146,51 @@ namespace System.Buffers
/// </summary>
public ReadOnlySequence(ReadOnlyMemory<T> memory)
{
+#pragma warning disable CS8631 // TODO-NULLABLE: ILLink rewriter removing some necessary metadata (https://github.com/dotnet/corefx/pull/38983#issuecomment-506757237)
if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<T> manager, out int index, out int length))
+#pragma warning restore CS8631
{
- _sequenceStart = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceStart(index));
- _sequenceEnd = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceEnd(length));
+ _startObject = manager;
+ _endObject = manager;
+ _startInteger = ReadOnlySequence.MemoryManagerToSequenceStart(index);
+ _endInteger = ReadOnlySequence.MemoryManagerToSequenceEnd(length);
}
else if (MemoryMarshal.TryGetArray(memory, out ArraySegment<T> segment))
{
T[] array = segment.Array;
int start = segment.Offset;
- _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start));
- _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + segment.Count));
+ _startObject = array;
+ _endObject = array;
+ _startInteger = ReadOnlySequence.ArrayToSequenceStart(start);
+ _endInteger = ReadOnlySequence.ArrayToSequenceEnd(start + segment.Count);
}
else if (typeof(T) == typeof(char))
{
- if (!MemoryMarshal.TryGetString(((ReadOnlyMemory<char>)(object)memory), out string text, out int start, out length))
+ if (!MemoryMarshal.TryGetString((ReadOnlyMemory<char>)(object)memory, out string text, out int start, out length))
ThrowHelper.ThrowInvalidOperationException();
- _sequenceStart = new SequencePosition(text, ReadOnlySequence.StringToSequenceStart(start));
- _sequenceEnd = new SequencePosition(text, ReadOnlySequence.StringToSequenceEnd(start + length));
+ _startObject = text;
+ _endObject = text;
+ _startInteger = ReadOnlySequence.StringToSequenceStart(start);
+ _endInteger = ReadOnlySequence.StringToSequenceEnd(start + length);
}
else
{
// Should never be reached
ThrowHelper.ThrowInvalidOperationException();
- _sequenceStart = default;
- _sequenceEnd = default;
+ _startObject = null;
+ _endObject = null;
+ _startInteger = 0;
+ _endInteger = 0;
}
}
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items.
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
- /// <param name="length">The length of the slice</param>
+ /// <param name="length">The length of the slice.</param>
+ /// <returns>A slice that consists of <paramref name="length" /> elements from the current instance starting at index <paramref name="start" />.</returns>
public ReadOnlySequence<T> Slice(long start, long length)
{
if (start < 0 || length < 0)
@@ -169,11 +199,11 @@ namespace System.Buffers
SequencePosition begin;
SequencePosition end;
- int startIndex = GetIndex(_sequenceStart);
- int endIndex = GetIndex(_sequenceEnd);
+ int startIndex = GetIndex(_startInteger);
+ int endIndex = GetIndex(_endInteger);
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ object startObject = _startObject;
+ object endObject = _endObject;
if (startObject != endObject)
{
@@ -188,22 +218,22 @@ namespace System.Buffers
startIndex += (int)start;
begin = new SequencePosition(startObject, startIndex);
- end = GetEndPosition(startSegment, startObject, startIndex, endObject, endIndex, length);
+ end = GetEndPosition(startSegment, startObject, startIndex, endObject!, endIndex, length);
}
else
{
if (currentLength < 0)
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
- begin = SeekMultiSegment(startSegment.Next, endObject, endIndex, start - currentLength, ExceptionArgument.start);
+ begin = SeekMultiSegment(startSegment.Next!, endObject!, endIndex, start - currentLength, ExceptionArgument.start);
int beginIndex = GetIndex(begin);
- object beginObject = begin.GetObject();
+ object beginObject = begin.GetObject()!;
if (beginObject != endObject)
{
Debug.Assert(beginObject != null);
- end = GetEndPosition((ReadOnlySequenceSegment<T>)beginObject, beginObject, beginIndex, endObject, endIndex, length);
+ end = GetEndPosition((ReadOnlySequenceSegment<T>)beginObject, beginObject, beginIndex, endObject!, endIndex, length);
}
else
{
@@ -232,23 +262,30 @@ namespace System.Buffers
}
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, ending at <paramref name="end"/> (inclusive).
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/> and ending at <paramref name="end"/> (exclusive).
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
- /// <param name="end">The end (inclusive) of the slice</param>
+ /// <param name="end">The ending (exclusive) <see cref="SequencePosition"/> of the slice.</param>
+ /// <returns>A slice that consists of items from the <paramref name="start" /> index to, but not including, the <paramref name="end" /> sequence position in the current read-only sequence.</returns>
public ReadOnlySequence<T> Slice(long start, SequencePosition end)
{
if (start < 0)
ThrowHelper.ThrowStartOrEndArgumentValidationException(start);
+ uint startIndex = (uint)GetIndex(_startInteger);
+ object startObject = _startObject;
+
+ uint endIndex = (uint)GetIndex(_endInteger);
+ object endObject = _endObject;
+
uint sliceEndIndex = (uint)GetIndex(end);
object sliceEndObject = end.GetObject();
- uint startIndex = (uint)GetIndex(_sequenceStart);
- object startObject = _sequenceStart.GetObject();
-
- uint endIndex = (uint)GetIndex(_sequenceEnd);
- object endObject = _sequenceEnd.GetObject();
+ if (sliceEndObject == null)
+ {
+ sliceEndObject = _startObject;
+ sliceEndIndex = startIndex;
+ }
// Single-Segment Sequence
if (startObject == endObject)
@@ -265,16 +302,16 @@ namespace System.Buffers
}
// Multi-Segment Sequence
- var startSegment = (ReadOnlySequenceSegment<T>)startObject;
+ var startSegment = (ReadOnlySequenceSegment<T>)startObject!;
ulong startRange = (ulong)(startSegment.RunningIndex + startIndex);
- ulong sliceRange = (ulong)(((ReadOnlySequenceSegment<T>)sliceEndObject).RunningIndex + sliceEndIndex);
+ ulong sliceRange = (ulong)(((ReadOnlySequenceSegment<T>)sliceEndObject!).RunningIndex + sliceEndIndex);
// This optimization works because we know sliceEndIndex, startIndex, and endIndex are all >= 0
Debug.Assert(sliceEndIndex >= 0 && startIndex >= 0 && endIndex >= 0);
if (!InRange(
sliceRange,
startRange,
- (ulong)(((ReadOnlySequenceSegment<T>)endObject).RunningIndex + endIndex)))
+ (ulong)(((ReadOnlySequenceSegment<T>)endObject!).RunningIndex + endIndex)))
{
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
}
@@ -293,32 +330,39 @@ namespace System.Buffers
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
// End of segment. Move to start of next.
- SequencePosition begin = SeekMultiSegment(startSegment.Next, sliceEndObject, (int)sliceEndIndex, start - currentLength, ExceptionArgument.start);
+ SequencePosition begin = SeekMultiSegment(startSegment.Next!, sliceEndObject, (int)sliceEndIndex, start - currentLength, ExceptionArgument.start);
return SliceImpl(begin, end);
}
FoundInFirstSegment:
// startIndex + start <= int.MaxValue
Debug.Assert(start <= int.MaxValue - startIndex);
- return SliceImpl(new SequencePosition(startObject, (int)startIndex + (int)start), end);
+ return SliceImpl(new SequencePosition(startObject, (int)startIndex + (int)start), new SequencePosition(sliceEndObject, (int)sliceEndIndex));
}
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items.
/// </summary>
/// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</param>
- /// <param name="length">The length of the slice</param>
+ /// <param name="length">The length of the slice.</param>
+ /// <returns>A slice that consists of <paramref name="length" /> elements from the current instance starting at sequence position <paramref name="start" />.</returns>
public ReadOnlySequence<T> Slice(SequencePosition start, long length)
{
+ uint startIndex = (uint)GetIndex(_startInteger);
+ object startObject = _startObject;
+
+ uint endIndex = (uint)GetIndex(_endInteger);
+ object endObject = _endObject;
+
// Check start before length
uint sliceStartIndex = (uint)GetIndex(start);
object sliceStartObject = start.GetObject();
- uint startIndex = (uint)GetIndex(_sequenceStart);
- object startObject = _sequenceStart.GetObject();
-
- uint endIndex = (uint)GetIndex(_sequenceEnd);
- object endObject = _sequenceEnd.GetObject();
+ if (sliceStartObject == null)
+ {
+ sliceStartIndex = startIndex;
+ sliceStartObject = _startObject;
+ }
// Single-Segment Sequence
if (startObject == endObject)
@@ -339,10 +383,10 @@ namespace System.Buffers
}
// Multi-Segment Sequence
- var sliceStartSegment = (ReadOnlySequenceSegment<T>)sliceStartObject;
+ var sliceStartSegment = (ReadOnlySequenceSegment<T>)sliceStartObject!;
ulong sliceRange = (ulong)((sliceStartSegment.RunningIndex + sliceStartIndex));
- ulong startRange = (ulong)(((ReadOnlySequenceSegment<T>)startObject).RunningIndex + startIndex);
- ulong endRange = (ulong)(((ReadOnlySequenceSegment<T>)endObject).RunningIndex + endIndex);
+ ulong startRange = (ulong)(((ReadOnlySequenceSegment<T>)startObject!).RunningIndex + startIndex);
+ ulong endRange = (ulong)(((ReadOnlySequenceSegment<T>)endObject!).RunningIndex + endIndex);
// This optimization works because we know sliceStartIndex, startIndex, and endIndex are all >= 0
Debug.Assert(sliceStartIndex >= 0 && startIndex >= 0 && endIndex >= 0);
@@ -369,42 +413,46 @@ namespace System.Buffers
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
// End of segment. Move to start of next.
- SequencePosition end = SeekMultiSegment(sliceStartSegment.Next, endObject, (int)endIndex, length - currentLength, ExceptionArgument.length);
+ SequencePosition end = SeekMultiSegment(sliceStartSegment.Next!, endObject, (int)endIndex, length - currentLength, ExceptionArgument.length);
return SliceImpl(start, end);
}
FoundInFirstSegment:
// sliceStartIndex + length <= int.MaxValue
Debug.Assert(length <= int.MaxValue - sliceStartIndex);
- return SliceImpl(start, new SequencePosition(sliceStartObject, (int)sliceStartIndex + (int)length));
+ return SliceImpl(new SequencePosition(sliceStartObject, (int)sliceStartIndex), new SequencePosition(sliceStartObject, (int)sliceStartIndex + (int)length));
}
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items.
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
- /// <param name="length">The length of the slice</param>
+ /// <param name="length">The length of the slice.</param>
+ /// <returns>A slice that consists of <paramref name="length" /> elements from the current instance starting at index <paramref name="start" />.</returns>
public ReadOnlySequence<T> Slice(int start, int length) => Slice((long)start, length);
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, ending at <paramref name="end"/> (inclusive).
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/> and ending at <paramref name="end"/> (exclusive).
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
- /// <param name="end">The end (inclusive) of the slice</param>
+ /// <param name="end">The ending (exclusive) <see cref="SequencePosition"/> of the slice.</param>
+ /// <returns>A slice that consists of items from the <paramref name="start" /> index to, but not including, the <paramref name="end" /> sequence position in the current read-only sequence.</returns>
public ReadOnlySequence<T> Slice(int start, SequencePosition end) => Slice((long)start, end);
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at '<paramref name="start"/>, with <paramref name="length"/> items
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, with <paramref name="length"/> items.
/// </summary>
/// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</param>
- /// <param name="length">The length of the slice</param>
+ /// <param name="length">The length of the slice.</param>
+ /// <returns>A slice that consists of <paramref name="length" /> elements from the current instance starting at sequence position <paramref name="start" />.</returns>
public ReadOnlySequence<T> Slice(SequencePosition start, int length) => Slice(start, (long)length);
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, ending at <paramref name="end"/> (inclusive).
+ /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, ending at <paramref name="end"/> (exclusive).
/// </summary>
/// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</param>
- /// <param name="end">The ending (inclusive) <see cref="SequencePosition"/> of the slice</param>
+ /// <param name="end">The ending (exclusive) <see cref="SequencePosition"/> of the slice.</param>
+ /// <returns>A slice that consists of items from the <paramref name="start" /> sequence position to, but not including, the <paramref name="end" /> sequence position in the current read-only sequence.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySequence<T> Slice(SequencePosition start, SequencePosition end)
{
@@ -413,20 +461,23 @@ namespace System.Buffers
}
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, ending at the existing <see cref="ReadOnlySequence{T}"/>'s end.
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}" />, beginning at a specified sequence position and continuing to the end of the read-only sequence.
/// </summary>
/// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</param>
+ /// <returns>A slice starting at sequence position <paramref name="start" /> and continuing to the end of the current read-only sequence.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySequence<T> Slice(SequencePosition start)
{
- BoundsCheck(start);
- return SliceImpl(start, _sequenceEnd);
+ bool positionIsNotNull = start.GetObject() != null;
+ BoundsCheck(start, positionIsNotNull);
+ return SliceImpl(positionIsNotNull ? start : Start);
}
/// <summary>
- /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, ending at the existing <see cref="ReadOnlySequence{T}"/>'s end.
+ /// Forms a slice out of the current <see cref="ReadOnlySequence{T}" /> , beginning at a specified index and continuing to the end of the read-only sequence.
/// </summary>
/// <param name="start">The start index at which to begin this slice.</param>
+ /// <returns>A slice starting at index <paramref name="start" /> and continuing to the end of the current read-only sequence.</returns>
public ReadOnlySequence<T> Slice(long start)
{
if (start < 0)
@@ -435,8 +486,8 @@ namespace System.Buffers
if (start == 0)
return this;
- SequencePosition begin = Seek(_sequenceStart, _sequenceEnd, start, ExceptionArgument.start);
- return SliceImpl(begin, _sequenceEnd);
+ SequencePosition begin = Seek(start, ExceptionArgument.start);
+ return SliceImpl(begin);
}
/// <inheritdoc />
@@ -454,19 +505,7 @@ namespace System.Buffers
if (Length < int.MaxValue)
{
-#if !FEATURE_PORTABLE_SPAN
- return string.Create((int)Length, charSequence, (span, sequence) =>
- {
- foreach (ReadOnlyMemory<char> readOnlyMemory in sequence)
- {
- ReadOnlySpan<char> sourceSpan = readOnlyMemory.Span;
- sourceSpan.CopyTo(span);
- span = span.Slice(sourceSpan.Length);
- }
- });
-#else
- return new string(charSequence.ToArray());
-#endif
+ return string.Create((int)Length, charSequence, (span, sequence) => sequence.CopyTo(span));
}
}
@@ -481,7 +520,13 @@ namespace System.Buffers
/// <summary>
/// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the start of the sequence.
/// </summary>
- public SequencePosition GetPosition(long offset) => GetPosition(offset, _sequenceStart);
+ public SequencePosition GetPosition(long offset)
+ {
+ if (offset < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange();
+
+ return Seek(offset);
+ }
/// <summary>
/// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the <paramref name="origin"/>
@@ -491,7 +536,7 @@ namespace System.Buffers
if (offset < 0)
ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange();
- return Seek(origin, _sequenceEnd, offset, ExceptionArgument.offset);
+ return Seek(origin, offset);
}
/// <summary>
@@ -592,4 +637,4 @@ namespace System.Buffers
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int StringToSequenceEnd(int endIndex) => endIndex | StringEndMask;
}
-}
+} \ No newline at end of file
diff --git a/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs b/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs
index a8ea4a232b..21b7826ee2 100644
--- a/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs
+++ b/src/System.Memory/src/System/Buffers/ReadOnlySequence_helpers.cs
@@ -4,10 +4,8 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
-
-#if !netstandard
+using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;
-#endif
namespace System.Buffers
{
@@ -26,9 +24,9 @@ namespace System.Buffers
}
SequenceType type = GetSequenceType();
- object endObject = _sequenceEnd.GetObject();
+ object endObject = _endObject;
int startIndex = GetIndex(position);
- int endIndex = GetIndex(_sequenceEnd);
+ int endIndex = GetIndex(_endInteger);
if (type == SequenceType.MultiSegment)
{
@@ -83,15 +81,15 @@ namespace System.Buffers
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlyMemory<T> GetFirstBuffer()
{
- object startObject = _sequenceStart.GetObject();
+ object startObject = _startObject;
if (startObject == null)
return default;
- int startIndex = _sequenceStart.GetInteger();
- int endIndex = _sequenceEnd.GetInteger();
+ int startIndex = _startInteger;
+ int endIndex = _endInteger;
- bool isMultiSegment = startObject != _sequenceEnd.GetObject();
+ bool isMultiSegment = startObject != _endObject;
// The highest bit of startIndex and endIndex are used to infer the sequence type
// The code below is structured this way for performance reasons and is equivalent to the following:
@@ -104,59 +102,179 @@ namespace System.Buffers
// Highest bit of startIndex: A = startIndex >> 31
// Highest bit of endIndex: B = endIndex >> 31
- if (startIndex >= 0)
+ // A == 0 && B == 0 means SequenceType.MultiSegment
+ // Equivalent to startIndex >= 0 && endIndex >= 0
+ if ((startIndex | endIndex) >= 0)
+ {
+ ReadOnlyMemory<T> memory = ((ReadOnlySequenceSegment<T>)startObject).Memory;
+ if (isMultiSegment)
+ {
+ return memory.Slice(startIndex);
+ }
+ return memory.Slice(startIndex, endIndex - startIndex);
+ }
+ else
{
- // A == 0 && B == 0 means SequenceType.MultiSegment
- // A == 0 && B == 1 means SequenceType.Array
+ return GetFirstBufferSlow(startObject, isMultiSegment);
+ }
+ }
- if (endIndex >= 0) // SequenceType.MultiSegment
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private ReadOnlyMemory<T> GetFirstBufferSlow(object startObject, bool isMultiSegment)
+ {
+ if (isMultiSegment)
+ ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
+
+ int startIndex = _startInteger;
+ int endIndex = _endInteger;
+
+ Debug.Assert(startIndex < 0 || endIndex < 0);
+
+ // A == 0 && B == 1 means SequenceType.Array
+ if (startIndex >= 0)
+ {
+ Debug.Assert(endIndex < 0);
+ return new ReadOnlyMemory<T>((T[])startObject, startIndex, (endIndex & ReadOnlySequence.IndexBitMask) - startIndex);
+ }
+ else
+ {
+ // The type == char check here is redundant. However, we still have it to allow
+ // the JIT to see when that the code is unreachable and eliminate it.
+ // A == 1 && B == 1 means SequenceType.String
+ if (typeof(T) == typeof(char) && endIndex < 0)
{
- ReadOnlyMemory<T> memory = ((ReadOnlySequenceSegment<T>)startObject).Memory;
- if (isMultiSegment)
- {
- return memory.Slice(startIndex);
- }
- return memory.Slice(startIndex, endIndex - startIndex);
+ // No need to remove the FlagBitMask since (endIndex - startIndex) == (endIndex & ReadOnlySequence.IndexBitMask) - (startIndex & ReadOnlySequence.IndexBitMask)
+ return (ReadOnlyMemory<T>)(object)((string)startObject).AsMemory(startIndex & ReadOnlySequence.IndexBitMask, endIndex - startIndex);
}
- else // endIndex < 0, SequenceType.Array
+ else // endIndex >= 0, A == 1 && B == 0 means SequenceType.MemoryManager
{
- if (isMultiSegment)
- ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
-
- return new ReadOnlyMemory<T>((T[])startObject, startIndex, (endIndex & ReadOnlySequence.IndexBitMask) - startIndex);
+ startIndex &= ReadOnlySequence.IndexBitMask;
+ return ((MemoryManager<T>)startObject).Memory.Slice(startIndex, endIndex - startIndex);
}
}
- else // startIndex < 0
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ReadOnlySpan<T> GetFirstSpan()
+ {
+ object startObject = _startObject;
+
+ if (startObject == null)
+ return default;
+
+ int startIndex = _startInteger;
+ int endIndex = _endInteger;
+
+ bool isMultiSegment = startObject != _endObject;
+
+ // The highest bit of startIndex and endIndex are used to infer the sequence type
+ // The code below is structured this way for performance reasons and is equivalent to the following:
+ // SequenceType type = GetSequenceType();
+ // if (type == SequenceType.MultiSegment) { ... }
+ // else if (type == SequenceType.Array) { ... }
+ // else if (type == SequenceType.String){ ... }
+ // else if (type == SequenceType.MemoryManager) { ... }
+
+ // Highest bit of startIndex: A = startIndex >> 31
+ // Highest bit of endIndex: B = endIndex >> 31
+
+ // A == 0 && B == 0 means SequenceType.MultiSegment
+ // Equivalent to startIndex >= 0 && endIndex >= 0
+ if ((startIndex | endIndex) >= 0)
{
+ ReadOnlySpan<T> span = ((ReadOnlySequenceSegment<T>)startObject).Memory.Span;
if (isMultiSegment)
- ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
+ {
+ return span.Slice(startIndex);
+ }
+ return span.Slice(startIndex, endIndex - startIndex);
+ }
+ else
+ {
+ return GetFirstSpanSlow(startObject, isMultiSegment);
+ }
+ }
- // A == 1 && B == 1 means SequenceType.String
- // A == 1 && B == 0 means SequenceType.MemoryManager
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private ReadOnlySpan<T> GetFirstSpanSlow(object startObject, bool isMultiSegment)
+ {
+ if (isMultiSegment)
+ ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
+
+ int startIndex = _startInteger;
+ int endIndex = _endInteger;
+ Debug.Assert(startIndex < 0 || endIndex < 0);
+
+ // A == 0 && B == 1 means SequenceType.Array
+ if (startIndex >= 0)
+ {
+ Debug.Assert(endIndex < 0);
+ ReadOnlySpan<T> span = (T[])startObject;
+ return span.Slice(startIndex, (endIndex & ReadOnlySequence.IndexBitMask) - startIndex);
+ }
+ else
+ {
// The type == char check here is redundant. However, we still have it to allow
// the JIT to see when that the code is unreachable and eliminate it.
- if (typeof(T) == typeof(char) && endIndex < 0) // SequenceType.String
+ // A == 1 && B == 1 means SequenceType.String
+ if (typeof(T) == typeof(char) && endIndex < 0)
{
+ var memory = (ReadOnlyMemory<T>)(object)((string)startObject).AsMemory();
// No need to remove the FlagBitMask since (endIndex - startIndex) == (endIndex & ReadOnlySequence.IndexBitMask) - (startIndex & ReadOnlySequence.IndexBitMask)
- return (ReadOnlyMemory<T>)(object)((string)startObject).AsMemory((startIndex & ReadOnlySequence.IndexBitMask), endIndex - startIndex);
+ return memory.Span.Slice(startIndex & ReadOnlySequence.IndexBitMask, endIndex - startIndex);
}
- else // endIndex >= 0, SequenceType.MemoryManager
+ else // endIndex >= 0, A == 1 && B == 0 means SequenceType.MemoryManager
{
startIndex &= ReadOnlySequence.IndexBitMask;
- return ((MemoryManager<T>)startObject).Memory.Slice(startIndex, endIndex - startIndex);
+ return ((MemoryManager<T>)startObject).Memory.Span.Slice(startIndex, endIndex - startIndex);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private SequencePosition Seek(in SequencePosition start, in SequencePosition end, long offset, ExceptionArgument argument)
+ internal SequencePosition Seek(long offset, ExceptionArgument exceptionArgument = ExceptionArgument.offset)
{
- int startIndex = GetIndex(start);
- int endIndex = GetIndex(end);
+ object startObject = _startObject;
+ object endObject = _endObject;
+ int startIndex = GetIndex(_startInteger);
+ int endIndex = GetIndex(_endInteger);
+
+ if (startObject != endObject)
+ {
+ Debug.Assert(startObject != null);
+ var startSegment = (ReadOnlySequenceSegment<T>)startObject;
+
+ int currentLength = startSegment.Memory.Length - startIndex;
+ // Position in start segment, defer to single segment seek
+ if (currentLength > offset)
+ goto IsSingleSegment;
+
+ if (currentLength < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
+
+ // End of segment. Move to start of next.
+ return SeekMultiSegment(startSegment.Next!, endObject!, endIndex, offset - currentLength, exceptionArgument);
+ }
+
+ Debug.Assert(startObject == endObject);
+
+ if (endIndex - startIndex < offset)
+ ThrowHelper.ThrowArgumentOutOfRangeException(exceptionArgument);
+
+ // Single segment Seek
+ IsSingleSegment:
+ return new SequencePosition(startObject, startIndex + (int)offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private SequencePosition Seek(in SequencePosition start, long offset)
+ {
object startObject = start.GetObject();
- object endObject = end.GetObject();
+ object endObject = _endObject;
+ int startIndex = GetIndex(start);
+ int endIndex = GetIndex(_endInteger);
if (startObject != endObject)
{
@@ -173,13 +291,13 @@ namespace System.Buffers
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
// End of segment. Move to start of next.
- return SeekMultiSegment(startSegment.Next, endObject, endIndex, offset - currentLength, argument);
+ return SeekMultiSegment(startSegment.Next!, endObject!, endIndex, offset - currentLength, ExceptionArgument.offset);
}
Debug.Assert(startObject == endObject);
if (endIndex - startIndex < offset)
- ThrowHelper.ThrowArgumentOutOfRangeException(argument);
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offset);
// Single segment Seek
IsSingleSegment:
@@ -189,7 +307,7 @@ namespace System.Buffers
[MethodImpl(MethodImplOptions.NoInlining)]
private static SequencePosition SeekMultiSegment(ReadOnlySequenceSegment<T> currentSegment, object endObject, int endIndex, long offset, ExceptionArgument argument)
{
- Debug.Assert(currentSegment != null);
+ Debug.Assert(currentSegment != null); // currentSegment parameter is marked as nullable as the parameter is reused/reassigned in the body
Debug.Assert(offset >= 0);
while (currentSegment != null && currentSegment != endObject)
@@ -213,14 +331,15 @@ namespace System.Buffers
return new SequencePosition(currentSegment, (int)offset);
}
- private void BoundsCheck(in SequencePosition position)
+ private void BoundsCheck(in SequencePosition position, bool positionIsNotNull)
{
uint sliceStartIndex = (uint)GetIndex(position);
- uint startIndex = (uint)GetIndex(_sequenceStart);
- uint endIndex = (uint)GetIndex(_sequenceEnd);
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ object startObject = _startObject;
+ object endObject = _endObject;
+
+ uint startIndex = (uint)GetIndex(_startInteger);
+ uint endIndex = (uint)GetIndex(_endInteger);
// Single-Segment Sequence
if (startObject == endObject)
@@ -234,11 +353,18 @@ namespace System.Buffers
{
// Multi-Segment Sequence
// Storing this in a local since it is used twice within InRange()
- ulong startRange = (ulong)(((ReadOnlySequenceSegment<T>)startObject).RunningIndex + startIndex);
+ ulong startRange = (ulong)(((ReadOnlySequenceSegment<T>)startObject!).RunningIndex + startIndex);
+ long runningIndex = 0;
+ if (positionIsNotNull)
+ {
+ Debug.Assert(position.GetObject() != null);
+ runningIndex = ((ReadOnlySequenceSegment<T>)position.GetObject()!).RunningIndex;
+ }
+
if (!InRange(
- (ulong)(((ReadOnlySequenceSegment<T>)position.GetObject()).RunningIndex + sliceStartIndex),
+ (ulong)(runningIndex + sliceStartIndex),
startRange,
- (ulong)(((ReadOnlySequenceSegment<T>)endObject).RunningIndex + endIndex)))
+ (ulong)(((ReadOnlySequenceSegment<T>)endObject!).RunningIndex + endIndex)))
{
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
}
@@ -247,11 +373,11 @@ namespace System.Buffers
private void BoundsCheck(uint sliceStartIndex, object sliceStartObject, uint sliceEndIndex, object sliceEndObject)
{
- uint startIndex = (uint)GetIndex(_sequenceStart);
- uint endIndex = (uint)GetIndex(_sequenceEnd);
+ object startObject = _startObject;
+ object endObject = _endObject;
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ uint startIndex = (uint)GetIndex(_startInteger);
+ uint endIndex = (uint)GetIndex(_endInteger);
// Single-Segment Sequence
if (startObject == endObject)
@@ -271,14 +397,24 @@ namespace System.Buffers
// This optimization works because we know sliceStartIndex, sliceEndIndex, startIndex, and endIndex are all >= 0
Debug.Assert(sliceStartIndex >= 0 && startIndex >= 0 && endIndex >= 0);
- ulong sliceStartRange = (ulong)(((ReadOnlySequenceSegment<T>)sliceStartObject).RunningIndex + sliceStartIndex);
- ulong sliceEndRange = (ulong)(((ReadOnlySequenceSegment<T>)sliceEndObject).RunningIndex + sliceEndIndex);
+ ulong sliceStartRange = sliceStartIndex;
+ ulong sliceEndRange = sliceEndIndex;
+
+ if (sliceStartObject != null)
+ {
+ sliceStartRange += (ulong)((ReadOnlySequenceSegment<T>)sliceStartObject).RunningIndex;
+ }
+
+ if (sliceEndObject != null)
+ {
+ sliceEndRange += (ulong)((ReadOnlySequenceSegment<T>)sliceEndObject).RunningIndex;
+ }
if (sliceStartRange > sliceEndRange)
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
- if (sliceStartRange < (ulong)(((ReadOnlySequenceSegment<T>)startObject).RunningIndex + startIndex)
- || sliceEndRange > (ulong)(((ReadOnlySequenceSegment<T>)endObject).RunningIndex + endIndex))
+ if (sliceStartRange < (ulong)(((ReadOnlySequenceSegment<T>)startObject!).RunningIndex + startIndex)
+ || sliceEndRange > (ulong)(((ReadOnlySequenceSegment<T>)endObject!).RunningIndex + endIndex))
{
ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange();
}
@@ -322,13 +458,16 @@ namespace System.Buffers
// start >> 31 = 0, end >> 31 = -1
// 2 * 0 + (-1) = -1, result = (SequenceType)1
- return (SequenceType)(-(2 * (_sequenceStart.GetInteger() >> 31) + (_sequenceEnd.GetInteger() >> 31)));
+ return (SequenceType)(-(2 * (_startInteger >> 31) + (_endInteger >> 31)));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetIndex(in SequencePosition position) => position.GetInteger() & ReadOnlySequence.IndexBitMask;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int GetIndex(int Integer) => Integer & ReadOnlySequence.IndexBitMask;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlySequence<T> SliceImpl(in SequencePosition start, in SequencePosition end)
{
// In this method we reset high order bits from indices
@@ -337,24 +476,39 @@ namespace System.Buffers
return new ReadOnlySequence<T>(
start.GetObject(),
- GetIndex(start) | (_sequenceStart.GetInteger() & ReadOnlySequence.FlagBitMask),
+ GetIndex(start) | (_startInteger & ReadOnlySequence.FlagBitMask),
end.GetObject(),
- GetIndex(end) | (_sequenceEnd.GetInteger() & ReadOnlySequence.FlagBitMask)
+ GetIndex(end) | (_endInteger & ReadOnlySequence.FlagBitMask)
+ );
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ReadOnlySequence<T> SliceImpl(in SequencePosition start)
+ {
+ // In this method we reset high order bits from indices
+ // of positions that were passed in
+ // and apply type bits specific for current ReadOnlySequence type
+
+ return new ReadOnlySequence<T>(
+ start.GetObject(),
+ GetIndex(start) | (_startInteger & ReadOnlySequence.FlagBitMask),
+ _endObject,
+ _endInteger
);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private long GetLength()
{
- int startIndex = GetIndex(_sequenceStart);
- int endIndex = GetIndex(_sequenceEnd);
- object startObject = _sequenceStart.GetObject();
- object endObject = _sequenceEnd.GetObject();
+ object startObject = _startObject;
+ object endObject = _endObject;
+ int startIndex = GetIndex(_startInteger);
+ int endIndex = GetIndex(_endInteger);
if (startObject != endObject)
{
- var startSegment = (ReadOnlySequenceSegment<T>)startObject;
- var endSegment = (ReadOnlySequenceSegment<T>)endObject;
+ var startSegment = (ReadOnlySequenceSegment<T>)startObject!;
+ var endSegment = (ReadOnlySequenceSegment<T>)endObject!;
// (End offset) - (start offset)
return (endSegment.RunningIndex + endIndex) - (startSegment.RunningIndex + startIndex);
}
@@ -365,7 +519,7 @@ namespace System.Buffers
internal bool TryGetReadOnlySequenceSegment(out ReadOnlySequenceSegment<T> startSegment, out int startIndex, out ReadOnlySequenceSegment<T> endSegment, out int endIndex)
{
- object startObject = _sequenceStart.GetObject();
+ object startObject = _startObject;
// Default or not MultiSegment
if (startObject == null || GetSequenceType() != SequenceType.MultiSegment)
@@ -376,13 +530,13 @@ namespace System.Buffers
endIndex = 0;
return false;
}
-
- Debug.Assert(_sequenceEnd.GetObject() != null);
+
+ Debug.Assert(_endObject != null);
startSegment = (ReadOnlySequenceSegment<T>)startObject;
- startIndex = GetIndex(_sequenceStart);
- endSegment = (ReadOnlySequenceSegment<T>)_sequenceEnd.GetObject();
- endIndex = GetIndex(_sequenceEnd);
+ startIndex = GetIndex(_startInteger);
+ endSegment = (ReadOnlySequenceSegment<T>)_endObject;
+ endIndex = GetIndex(_endInteger);
return true;
}
@@ -394,10 +548,10 @@ namespace System.Buffers
return false;
}
- Debug.Assert(_sequenceStart.GetObject() != null);
+ Debug.Assert(_startObject != null);
- int startIndex = GetIndex(_sequenceStart);
- segment = new ArraySegment<T>((T[])_sequenceStart.GetObject(), startIndex, GetIndex(_sequenceEnd) - startIndex);
+ int startIndex = GetIndex(_startInteger);
+ segment = new ArraySegment<T>((T[])_startObject, startIndex, GetIndex(_endInteger) - startIndex);
return true;
}
@@ -411,11 +565,11 @@ namespace System.Buffers
return false;
}
- Debug.Assert(_sequenceStart.GetObject() != null);
+ Debug.Assert(_startObject != null);
- start = GetIndex(_sequenceStart);
- length = GetIndex(_sequenceEnd) - start;
- text = (string)_sequenceStart.GetObject();
+ start = GetIndex(_startInteger);
+ length = GetIndex(_endInteger) - start;
+ text = (string)_startObject;
return true;
}
@@ -432,7 +586,7 @@ namespace System.Buffers
// The case, value < start, is invalid.
// In that case, (value - start) would underflow becoming larger than int.MaxValue.
- // (end - start) can never underflow and hence must be within 0 and int.MaxValue.
+ // (end - start) can never underflow and hence must be within 0 and int.MaxValue.
// So, we will correctly return false.
// The case, value > end, is invalid.
@@ -441,7 +595,7 @@ namespace System.Buffers
// In all other cases, value is valid, and we return true.
- // Equivalent to: return (start <= value && value <= start)
+ // Equivalent to: return (start <= value && value <= end)
return (value - start) <= (end - start);
}
@@ -458,7 +612,7 @@ namespace System.Buffers
// The case, value < start, is invalid.
// In that case, (value - start) would underflow becoming larger than long.MaxValue.
- // (end - start) can never underflow and hence must be within 0 and long.MaxValue.
+ // (end - start) can never underflow and hence must be within 0 and long.MaxValue.
// So, we will correctly return false.
// The case, value > end, is invalid.
@@ -470,5 +624,79 @@ namespace System.Buffers
// Equivalent to: return (start <= value && value <= start)
return (value - start) <= (end - start);
}
+
+ /// <summary>
+ /// Helper to efficiently prepare the <see cref="SequenceReader{T}"/>
+ /// </summary>
+ /// <param name="first">The first span in the sequence.</param>
+ /// <param name="next">The next position.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void GetFirstSpan(out ReadOnlySpan<T> first, out SequencePosition next)
+ {
+ first = default;
+ next = default;
+ object startObject = _startObject;
+ int startIndex = _startInteger;
+
+ if (startObject != null)
+ {
+ bool hasMultipleSegments = startObject != _endObject;
+ int endIndex = _endInteger;
+
+ if (startIndex >= 0)
+ {
+ if (endIndex >= 0)
+ {
+ // Positive start and end index == ReadOnlySequenceSegment<T>
+ ReadOnlySequenceSegment<T> segment = (ReadOnlySequenceSegment<T>)startObject;
+ next = new SequencePosition(segment.Next, 0);
+ first = segment.Memory.Span;
+ if (hasMultipleSegments)
+ {
+ first = first.Slice(startIndex);
+ }
+ else
+ {
+ first = first.Slice(startIndex, endIndex - startIndex);
+ }
+ }
+ else
+ {
+ // Positive start and negative end index == T[]
+ if (hasMultipleSegments)
+ ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
+
+ first = new ReadOnlySpan<T>((T[])startObject, startIndex, (endIndex & ReadOnlySequence.IndexBitMask) - startIndex);
+ }
+ }
+ else
+ {
+ first = GetFirstSpanSlow(startObject, startIndex, endIndex, hasMultipleSegments);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static ReadOnlySpan<T> GetFirstSpanSlow(object startObject, int startIndex, int endIndex, bool hasMultipleSegments)
+ {
+ Debug.Assert(startIndex < 0);
+ if (hasMultipleSegments)
+ ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
+
+ // The type == char check here is redundant. However, we still have it to allow
+ // the JIT to see when that the code is unreachable and eliminate it.
+ if (typeof(T) == typeof(char) && endIndex < 0)
+ {
+ // Negative start and negative end index == string
+ ReadOnlySpan<char> spanOfChar = ((string)startObject).AsSpan(startIndex & ReadOnlySequence.IndexBitMask, endIndex - startIndex);
+ return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<char, T>(ref MemoryMarshal.GetReference(spanOfChar)), spanOfChar.Length);
+ }
+ else
+ {
+ // Negative start and positive end index == MemoryManager<T>
+ startIndex &= ReadOnlySequence.IndexBitMask;
+ return ((MemoryManager<T>)startObject).Memory.Span.Slice(startIndex, endIndex - startIndex);
+ }
+ }
}
-}
+} \ No newline at end of file
diff --git a/src/System.Memory/src/System/Buffers/StandardFormat.cs b/src/System.Memory/src/System/Buffers/StandardFormat.cs
index 6975c204f1..83014d06b0 100644
--- a/src/System.Memory/src/System/Buffers/StandardFormat.cs
+++ b/src/System.Memory/src/System/Buffers/StandardFormat.cs
@@ -67,12 +67,34 @@ namespace System.Buffers
public static implicit operator StandardFormat(char symbol) => new StandardFormat(symbol);
/// <summary>
- /// Converts a classic .NET format string into a StandardFormat
+ /// Converts a <see cref="ReadOnlySpan{Char}"/> into a StandardFormat
/// </summary>
public static StandardFormat Parse(ReadOnlySpan<char> format)
{
+ ParseHelper(format, out StandardFormat standardFormat, throws: true);
+
+ return standardFormat;
+ }
+
+ /// <summary>
+ /// Converts a classic .NET format string into a StandardFormat
+ /// </summary>
+ public static StandardFormat Parse(string format) => format == null ? default : Parse(format.AsSpan());
+
+ /// <summary>
+ /// Tries to convert a <see cref="ReadOnlySpan{Char}"/> into a StandardFormat. A return value indicates whether the conversion succeeded or failed.
+ /// </summary>
+ public static bool TryParse(ReadOnlySpan<char> format, out StandardFormat result)
+ {
+ return ParseHelper(format, out result);
+ }
+
+ private static bool ParseHelper(ReadOnlySpan<char> format, out StandardFormat standardFormat, bool throws = false)
+ {
+ standardFormat = default;
+
if (format.Length == 0)
- return default;
+ return true;
char symbol = format[0];
byte precision;
@@ -87,25 +109,24 @@ namespace System.Buffers
{
uint digit = format[srcIndex] - 48u; // '0'
if (digit > 9)
- throw new FormatException(SR.Format(SR.Argument_CannotParsePrecision, MaxPrecision));
-
+ {
+ return throws ? throw new FormatException(SR.Format(SR.Argument_CannotParsePrecision, MaxPrecision)) : false;
+ }
parsedPrecision = parsedPrecision * 10 + digit;
if (parsedPrecision > MaxPrecision)
- throw new FormatException(SR.Format(SR.Argument_PrecisionTooLarge, MaxPrecision));
+ {
+ return throws ? throw new FormatException(SR.Format(SR.Argument_PrecisionTooLarge, MaxPrecision)) : false;
+ }
}
precision = (byte)parsedPrecision;
}
- return new StandardFormat(symbol, precision);
+ standardFormat = new StandardFormat(symbol, precision);
+ return true;
}
/// <summary>
- /// Converts a classic .NET format string into a StandardFormat
- /// </summary>
- public static StandardFormat Parse(string format) => format == null ? default : Parse(format.AsSpan());
-
- /// <summary>
/// Returns true if both the Symbol and Precision are equal.
/// </summary>
public override bool Equals(object obj) => obj is StandardFormat other && Equals(other);
@@ -125,40 +146,54 @@ namespace System.Buffers
/// </summary>
public override string ToString()
{
- unsafe
+ Span<char> buffer = stackalloc char[FormatStringLength];
+ int charsWritten = Format(buffer);
+ return new string(buffer.Slice(0, charsWritten));
+ }
+
+ /// <summary>The exact buffer length required by <see cref="Format"/>.</summary>
+ internal const int FormatStringLength = 3;
+
+ /// <summary>
+ /// Formats the format in classic .NET format.
+ /// </summary>
+ internal int Format(Span<char> destination)
+ {
+ Debug.Assert(destination.Length == FormatStringLength);
+
+ int count = 0;
+ char symbol = Symbol;
+
+ if (symbol != default &&
+ (uint)destination.Length == FormatStringLength) // to eliminate bounds checks
{
- const int MaxLength = 4;
- char* pBuffer = stackalloc char[MaxLength];
+ destination[0] = symbol;
+ count = 1;
- int dstIndex = 0;
- char symbol = Symbol;
- if (symbol != default)
+ uint precision = Precision;
+ if (precision != NoPrecision)
{
- pBuffer[dstIndex++] = symbol;
-
- byte precision = Precision;
- if (precision != NoPrecision)
+ // Note that Precision is stored as a byte, so in theory it could contain
+ // values > MaxPrecision (99). But all supported mechanisms for creating a
+ // StandardFormat limit values to being <= MaxPrecision, so the only way a value
+ // could be larger than that is if unsafe code or the equivalent were used
+ // to force a larger invalid value in, in which case we don't need to
+ // guarantee such an invalid value is properly roundtripped through here;
+ // we just need to make sure things aren't corrupted further.
+
+ if (precision >= 10)
{
- if (precision >= 100)
- {
- pBuffer[dstIndex++] = (char)('0' + (precision / 100) % 10);
- precision = (byte)(precision % 100);
- }
-
- if (precision >= 10)
- {
- pBuffer[dstIndex++] = (char)('0' + (precision / 10) % 10);
- precision = (byte)(precision % 10);
- }
-
- pBuffer[dstIndex++] = (char)('0' + precision);
+ uint div = Math.DivRem(precision, 10, out precision);
+ destination[1] = (char)('0' + div % 10);
+ count = 2;
}
- }
-
- Debug.Assert(dstIndex <= MaxLength);
- return new string(pBuffer, startIndex: 0, length: dstIndex);
+ destination[count] = (char)('0' + precision);
+ count++;
+ }
}
+
+ return count;
}
/// <summary>
diff --git a/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs b/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs
index 8335a4144b..ded0b44d9d 100644
--- a/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs
+++ b/src/System.Memory/src/System/Runtime/InteropServices/SequenceMarshal.cs
@@ -57,5 +57,22 @@ namespace System.Runtime.InteropServices
{
return sequence.TryGetString(out text, out start, out length);
}
+
+ /// <summary>
+ /// Try to read the given type out of the buffer if possible. Warning: this is dangerous to use with arbitrary
+ /// structs- see remarks for full details.
+ /// </summary>
+ /// <remarks>
+ /// IMPORTANT: The read is a straight copy of bits. If a struct depends on specific state of it's members to
+ /// behave correctly this can lead to exceptions, etc. If reading endian specific integers, use the explicit
+ /// overloads such as <see cref="SequenceReaderExtensions.TryReadLittleEndian(ref SequenceReader{byte}, out int)"/>
+ /// </remarks>
+ /// <returns>
+ /// True if successful. <paramref name="value"/> will be default if failed (due to lack of space).
+ /// </returns>
+ public static bool TryRead<T>(ref SequenceReader<byte> reader, out T value) where T : unmanaged
+ {
+ return reader.TryRead<T>(out value);
+ }
}
}