1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Globalization;
9 /// <summary>Helpers for string-like operations on spans of chars.</summary>
10 internal static class StringSpanHelpers
12 // TODO https://github.com/dotnet/corefx/issues/21395: Provide public, efficient implementations
14 public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right, StringComparison comparisonType) =>
15 comparisonType == StringComparison.Ordinal ? Equals(left, right) :
16 comparisonType == StringComparison.OrdinalIgnoreCase ? EqualsOrdinalIgnoreCase(left, right) :
17 throw new ArgumentOutOfRangeException(nameof(comparisonType));
19 public static bool Equals(this ReadOnlySpan<char> left, string right) =>
20 Equals(left, right.AsSpan());
22 public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right)
24 if (left.Length != right.Length)
29 for (int i = 0; i < left.Length; i++)
31 if (left[i] != right[i])
40 private static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> left, ReadOnlySpan<char> right)
42 if (left.Length != right.Length)
47 for (int i = 0; i < left.Length; i++)
49 char x = left[i], y = right[i];
51 TextInfo.ToUpperAsciiInvariant(x) != TextInfo.ToUpperAsciiInvariant(y))
60 public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> source)
62 int startIndex = 0, endIndex = source.Length - 1;
64 while (startIndex <= endIndex && char.IsWhiteSpace(source[startIndex]))
69 while (endIndex >= startIndex && char.IsWhiteSpace(source[endIndex]))
74 return source.Slice(startIndex, endIndex - startIndex + 1);
77 public static int IndexOf(this ReadOnlySpan<char> source, char value) =>
78 IndexOf(source, value, 0);
80 public static int IndexOf(this ReadOnlySpan<char> source, char value, int startIndex)
82 for (int i = startIndex; i < source.Length; i++)
84 if (source[i] == value)
93 public static bool Contains(this ReadOnlySpan<char> source, char value)
95 for (int i = 0; i < source.Length; i++)
97 if (source[i] == value)
106 public static ReadOnlySpan<char> Remove(this ReadOnlySpan<char> source, int startIndex, int count)
109 throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
111 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
112 if (count > source.Length - startIndex)
113 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount);
120 int newLength = source.Length - count;
123 return ReadOnlySpan<char>.Empty;
126 Span<char> result = new char[newLength];
127 source.Slice(0, startIndex).CopyTo(result);
128 source.Slice(startIndex + count).CopyTo(result.Slice(startIndex));
132 // Returns the index of the last occurrence of a specified character in the current instance.
133 public static int LastIndexOf(this ReadOnlySpan<char> source, char value)
135 if (source.Length == 0)
138 for (int i = source.Length - 1; i >= 0; i--)
140 if (source[i] == value)
147 public static void CheckStringComparison(StringComparison comparisonType)
149 // Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase]
150 if ((uint)(comparisonType - StringComparison.CurrentCulture) > (StringComparison.OrdinalIgnoreCase - StringComparison.CurrentCulture))
152 throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));