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

Commit 42d5370

Browse files
authored
Add ROSpan StartsWith and EndsWith string-like APIs with StringComparison (#26880)
* Add ROSpan StartsWith and EndsWith string-like APIs with StringComparison * We have separate implementations of slow and fast span. * Remove unused using directive. * Address PR feedback and add tests. * Add test files * Get the string from the underlying span to avoid allocation when possible. * Update ToString and add tests * Update Span ToString and add tests * Address PR feedback and disable ToString test for fast span * Borrow additional tests from the existing StringTests test bed. * Fix comment grammar * No StringComparison results in generic StartsWith being called which does ordinal comparison * Address feedback related to ToString and tests * Fix tests for culture specific cases, for Unix. * Fix typo in variable names * Respond to recent change AsReadOnlySpan -> AsSpan * Fix typo in test.
1 parent 30a00d0 commit 42d5370

File tree

10 files changed

+877
-16
lines changed

10 files changed

+877
-16
lines changed

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static partial class MemoryExtensions
3131
public static int BinarySearch<T, TComparable>(this System.Span<T> span, TComparable comparable) where TComparable : System.IComparable<T> { throw null; }
3232
public static void CopyTo<T>(this T[] array, System.Memory<T> destination) { }
3333
public static void CopyTo<T>(this T[] array, System.Span<T> destination) { }
34+
public static bool EndsWith(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
3435
public static bool EndsWith<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
3536
public static bool EndsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
3637
public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
@@ -63,6 +64,7 @@ public static void Reverse<T>(this System.Span<T> span) { }
6364
public static int SequenceCompareTo<T>(this System.Span<T> first, System.ReadOnlySpan<T> second) where T : System.IComparable<T> { throw null; }
6465
public static bool SequenceEqual<T>(this System.ReadOnlySpan<T> first, System.ReadOnlySpan<T> second) where T : System.IEquatable<T> { throw null; }
6566
public static bool SequenceEqual<T>(this System.Span<T> first, System.ReadOnlySpan<T> second) where T : System.IEquatable<T> { throw null; }
67+
public static bool StartsWith(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
6668
public static bool StartsWith<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
6769
public static bool StartsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
6870
public static System.ReadOnlySpan<char> Trim(this System.ReadOnlySpan<char> span) { throw null; }
@@ -91,7 +93,6 @@ public void CopyTo(System.Memory<T> destination) { }
9193
public override bool Equals(object obj) { throw null; }
9294
[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))]
9395
public override int GetHashCode() { throw null; }
94-
public override string ToString() { throw null; }
9596
public static implicit operator System.Memory<T> (System.ArraySegment<T> arraySegment) { throw null; }
9697
public static implicit operator System.ReadOnlyMemory<T> (System.Memory<T> memory) { throw null; }
9798
public static implicit operator System.Memory<T> (T[] array) { throw null; }
@@ -117,7 +118,6 @@ public void CopyTo(System.Memory<T> destination) { }
117118
public bool Equals(System.ReadOnlyMemory<T> other) { throw null; }
118119
[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))]
119120
public override int GetHashCode() { throw null; }
120-
public override string ToString() { throw null; }
121121
public static implicit operator System.ReadOnlyMemory<T> (System.ArraySegment<T> arraySegment) { throw null; }
122122
public static implicit operator System.ReadOnlyMemory<T> (T[] array) { throw null; }
123123
public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; }
@@ -145,14 +145,14 @@ public void CopyTo(System.Span<T> destination) { }
145145
[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))]
146146
[System.ObsoleteAttribute("GetHashCode() on ReadOnlySpan will always throw an exception.")]
147147
public override int GetHashCode() { throw null; }
148-
public override string ToString() { throw null; }
149148
public static bool operator ==(System.ReadOnlySpan<T> left, System.ReadOnlySpan<T> right) { throw null; }
150149
public static implicit operator System.ReadOnlySpan<T> (System.ArraySegment<T> arraySegment) { throw null; }
151150
public static implicit operator System.ReadOnlySpan<T> (T[] array) { throw null; }
152151
public static bool operator !=(System.ReadOnlySpan<T> left, System.ReadOnlySpan<T> right) { throw null; }
153152
public System.ReadOnlySpan<T> Slice(int start) { throw null; }
154153
public System.ReadOnlySpan<T> Slice(int start, int length) { throw null; }
155154
public T[] ToArray() { throw null; }
155+
public override string ToString() { throw null; }
156156
public bool TryCopyTo(System.Span<T> destination) { throw null; }
157157
public ref partial struct Enumerator
158158
{
@@ -196,7 +196,6 @@ public void Fill(T value) { }
196196
[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))]
197197
[System.ObsoleteAttribute("GetHashCode() on Span will always throw an exception.")]
198198
public override int GetHashCode() { throw null; }
199-
public override string ToString() { throw null; }
200199
public static bool operator ==(System.Span<T> left, System.Span<T> right) { throw null; }
201200
public static implicit operator System.Span<T> (System.ArraySegment<T> arraySegment) { throw null; }
202201
public static implicit operator System.ReadOnlySpan<T> (System.Span<T> span) { throw null; }
@@ -205,6 +204,7 @@ public void Fill(T value) { }
205204
public System.Span<T> Slice(int start) { throw null; }
206205
public System.Span<T> Slice(int start, int length) { throw null; }
207206
public T[] ToArray() { throw null; }
207+
public override string ToString() { throw null; }
208208
public bool TryCopyTo(System.Span<T> destination) { throw null; }
209209
public ref partial struct Enumerator
210210
{
@@ -244,14 +244,14 @@ public partial struct MemoryHandle : System.IDisposable
244244
public unsafe void* Pointer { get { throw null; } }
245245
public void Dispose() { }
246246
}
247-
public abstract class MemoryPool<T> : IDisposable
247+
public abstract partial class MemoryPool<T> : System.IDisposable
248248
{
249-
public static System.Buffers.MemoryPool<T> Shared { get; }
250-
public abstract System.Buffers.OwnedMemory<T> Rent(int minBufferSize=-1);
249+
protected MemoryPool() { }
251250
public abstract int MaxBufferSize { get; }
252-
protected MemoryPool() { throw null; }
253-
public void Dispose() { throw null; }
251+
public static System.Buffers.MemoryPool<T> Shared { get { throw null; } }
252+
public void Dispose() { }
254253
protected abstract void Dispose(bool disposing);
254+
public abstract System.Buffers.OwnedMemory<T> Rent(int minBufferSize = -1);
255255
}
256256
public enum OperationStatus
257257
{
@@ -497,15 +497,15 @@ namespace System.Runtime.InteropServices
497497
public static partial class MemoryMarshal
498498
{
499499
public static System.Memory<T> AsMemory<T>(System.ReadOnlyMemory<T> readOnlyMemory) { throw null; }
500-
public static ref T GetReference<T>(System.ReadOnlySpan<T> span) { throw null; }
501-
public static ref T GetReference<T>(System.Span<T> span) { throw null; }
502-
public static bool TryGetArray<T>(System.ReadOnlyMemory<T> readOnlyMemory, out System.ArraySegment<T> arraySegment) { throw null; }
503-
public static System.Collections.Generic.IEnumerable<T> ToEnumerable<T>(ReadOnlyMemory<T> memory) { throw null; }
504500
public static System.ReadOnlySpan<TTo> Cast<TFrom, TTo>(System.ReadOnlySpan<TFrom> source) where TFrom : struct where TTo : struct { throw null; }
505501
public static System.Span<TTo> Cast<TFrom, TTo>(System.Span<TFrom> source) where TFrom : struct where TTo : struct { throw null; }
506502
#if !FEATURE_PORTABLE_SPAN
507503
public static System.ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) { throw null; }
508504
public static System.Span<T> CreateSpan<T>(ref T reference, int length) { throw null; }
509505
#endif
506+
public static ref T GetReference<T>(System.ReadOnlySpan<T> span) { throw null; }
507+
public static ref T GetReference<T>(System.Span<T> span) { throw null; }
508+
public static System.Collections.Generic.IEnumerable<T> ToEnumerable<T>(System.ReadOnlyMemory<T> memory) { throw null; }
509+
public static bool TryGetArray<T>(System.ReadOnlyMemory<T> readOnlyMemory, out System.ArraySegment<T> arraySegment) { throw null; }
510510
}
511511
}

src/System.Memory/src/System/MemoryExtensions.Fast.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@ namespace System
1111
/// </summary>
1212
public static partial class MemoryExtensions
1313
{
14+
/// <summary>
15+
/// Determines whether the end of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
16+
/// </summary>
17+
/// <param name="span">The source span.</param>
18+
/// <param name="value">The sequence to compare to the end of the source span.</param>
19+
/// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
20+
public static bool EndsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
21+
=> Span.EndsWith(span, value, comparisonType);
22+
23+
/// <summary>
24+
/// Determines whether the beginning of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
25+
/// </summary>
26+
/// <param name="span">The source span.</param>
27+
/// <param name="value">The sequence to compare to the beginning of the source span.</param>
28+
/// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
29+
public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
30+
=> Span.StartsWith(span, value, comparisonType);
31+
1432
/// <summary>
1533
/// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
1634
/// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.

src/System.Memory/src/System/MemoryExtensions.Portable.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,42 @@ namespace System
1212
/// </summary>
1313
public static partial class MemoryExtensions
1414
{
15+
/// <summary>
16+
/// Determines whether the end of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
17+
/// </summary>
18+
/// <param name="span">The source span.</param>
19+
/// <param name="value">The sequence to compare to the end of the source span.</param>
20+
/// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
21+
public static bool EndsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
22+
{
23+
if (comparisonType == StringComparison.Ordinal)
24+
{
25+
return span.EndsWith<char>(value);
26+
}
27+
28+
string sourceString = span.ToString();
29+
string valueString = value.ToString();
30+
return sourceString.EndsWith(valueString, comparisonType);
31+
}
32+
33+
/// <summary>
34+
/// Determines whether the beginning of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
35+
/// </summary>
36+
/// <param name="span">The source span.</param>
37+
/// <param name="value">The sequence to compare to the beginning of the source span.</param>
38+
/// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
39+
public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
40+
{
41+
if (comparisonType == StringComparison.Ordinal)
42+
{
43+
return span.StartsWith<char>(value);
44+
}
45+
46+
string sourceString = span.ToString();
47+
string valueString = value.ToString();
48+
return sourceString.StartsWith(valueString, comparisonType);
49+
}
50+
1551
/// <summary>
1652
/// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
1753
/// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.

src/System.Memory/src/System/ReadOnlySpan.Portable.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,17 @@ public override string ToString()
193193
{
194194
if (typeof(T) == typeof(char))
195195
{
196+
// If this wraps a string and represents the full length of the string, just return the wrapped string.
197+
if (_byteOffset == MemoryExtensions.StringAdjustment)
198+
{
199+
object obj = Unsafe.As<object>(_pinnable); // minimize chances the compilers will optimize away the 'is' check
200+
if (obj is string str && _length == str.Length)
201+
{
202+
return str;
203+
}
204+
}
205+
206+
// Otherwise, copy the data to a new string.
196207
unsafe
197208
{
198209
fixed (char* src = &Unsafe.As<T, char>(ref DangerousGetPinnableReference()))

0 commit comments

Comments
 (0)