diff options
author | Ahson Khan <ahkha@microsoft.com> | 2018-03-03 17:48:07 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-03 17:48:07 -0800 |
commit | 7ac6d5cd8db4033a4d7a4bfec34e72701b13433b (patch) | |
tree | 017e96a4244e7ff5908d93d9db18a29f36a00238 /src | |
parent | 37d1426400794bad99142d741dc38efec1175e0d (diff) | |
download | coreclr-7ac6d5cd8db4033a4d7a4bfec34e72701b13433b.tar.gz coreclr-7ac6d5cd8db4033a4d7a4bfec34e72701b13433b.tar.bz2 coreclr-7ac6d5cd8db4033a4d7a4bfec34e72701b13433b.zip |
Remove StringSpanHelpers and start using MemoryExtensions (#16718)
* Remove StringSpanHelpers and start using MemoryExtensions
* Address PR feedback.
Diffstat (limited to 'src')
18 files changed, 175 insertions, 292 deletions
diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index 0b3f971b17..46a161e006 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -494,7 +494,6 @@ <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\String.Searching.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\StringSpanHelpers.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\StringComparer.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\StringComparison.cs" /> diff --git a/src/mscorlib/shared/System/Boolean.cs b/src/mscorlib/shared/System/Boolean.cs index fd56082f9b..e476ef7ce6 100644 --- a/src/mscorlib/shared/System/Boolean.cs +++ b/src/mscorlib/shared/System/Boolean.cs @@ -12,7 +12,6 @@ ** ===========================================================*/ -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -203,14 +202,14 @@ namespace System public static bool TryParse(ReadOnlySpan<char> value, out bool result) { ReadOnlySpan<char> trueSpan = TrueLiteral.AsSpan(); - if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) + if (trueSpan.EqualsOrdinalIgnoreCase(value)) { result = true; return true; } ReadOnlySpan<char> falseSpan = FalseLiteral.AsSpan(); - if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) + if (falseSpan.EqualsOrdinalIgnoreCase(value)) { result = false; return true; @@ -219,13 +218,13 @@ namespace System // Special case: Trim whitespace as well as null characters. value = TrimWhiteSpaceAndNull(value); - if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) + if (trueSpan.EqualsOrdinalIgnoreCase(value)) { result = true; return true; } - if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) + if (falseSpan.EqualsOrdinalIgnoreCase(value)) { result = false; return true; diff --git a/src/mscorlib/shared/System/Double.cs b/src/mscorlib/shared/System/Double.cs index 146ee46005..1351ae91d3 100644 --- a/src/mscorlib/shared/System/Double.cs +++ b/src/mscorlib/shared/System/Double.cs @@ -346,15 +346,15 @@ namespace System if (!success) { ReadOnlySpan<char> sTrim = s.Trim(); - if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol)) { result = PositiveInfinity; } - else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + else if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol)) { result = NegativeInfinity; } - else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + else if (sTrim.EqualsOrdinal(info.NaNSymbol)) { result = NaN; } diff --git a/src/mscorlib/shared/System/Globalization/CompareInfo.cs b/src/mscorlib/shared/System/Globalization/CompareInfo.cs index c369c816b0..db0a660ddf 100644 --- a/src/mscorlib/shared/System/Globalization/CompareInfo.cs +++ b/src/mscorlib/shared/System/Globalization/CompareInfo.cs @@ -389,38 +389,18 @@ namespace System.Globalization return CompareString(string1, string2, options); } - // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly? - internal virtual int Compare(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2, CompareOptions options) + internal virtual int CompareOptionNone(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2) { - if (options == CompareOptions.OrdinalIgnoreCase) - { - return CompareOrdinalIgnoreCase(string1, string2); - } - - // Verify the options before we do any real comparison. - if ((options & CompareOptions.Ordinal) != 0) - { - if (options != CompareOptions.Ordinal) - { - throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); - } - - return string.CompareOrdinal(string1, string2); - } - - if ((options & ValidCompareMaskOffFlags) != 0) - { - throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); - } - - if (_invariantMode) - { - return (options & CompareOptions.IgnoreCase) != 0 ? - CompareOrdinalIgnoreCase(string1, string2) : - string.CompareOrdinal(string1, string2); - } + return _invariantMode ? + string.CompareOrdinal(string1, string2) : + CompareString(string1, string2, CompareOptions.None); + } - return CompareString(string1, string2, options); + internal virtual int CompareOptionIgnoreCase(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2) + { + return _invariantMode ? + CompareOrdinalIgnoreCase(string1, string2) : + CompareString(string1, string2, CompareOptions.IgnoreCase); } //////////////////////////////////////////////////////////////////////// diff --git a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs index 5b285eb5df..fd64c6d111 100644 --- a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs +++ b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs @@ -5045,7 +5045,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { return false; } - if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsSpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0) + if (m_info.CompareOptionIgnoreCase(Value.Slice(thisPosition, segmentLength), target.AsSpan().Slice(targetPosition, segmentLength)) != 0) { return false; } @@ -5071,7 +5071,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { return false; } - if (m_info.Compare(Value.Slice(thisPosition, segmentLength), target.AsSpan().Slice(targetPosition, segmentLength), CompareOptions.IgnoreCase) != 0) + if (m_info.CompareOptionIgnoreCase(Value.Slice(thisPosition, segmentLength), target.AsSpan().Slice(targetPosition, segmentLength)) != 0) { return false; } @@ -5286,14 +5286,17 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Check if the last character is a quote. if (ch == '\'' || ch == '\"') { - if (Char.IsWhiteSpace(Value[i - 1])) + if (char.IsWhiteSpace(Value[i - 1])) { i--; - while (i >= 1 && Char.IsWhiteSpace(Value[i - 1])) + while (i >= 1 && char.IsWhiteSpace(Value[i - 1])) { i--; } - Value = Value.Remove(i, Value.Length - 1 - i); + Span<char> result = new char[i + 1]; + result[i] = ch; + Value.Slice(0, i).CopyTo(result); + Value = result.AsReadOnlySpan(); } } } @@ -5311,13 +5314,16 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Check if the last character is a quote. if (ch == '\'' || ch == '\"') { - while ((i + 1) < Length && Char.IsWhiteSpace(Value[i + 1])) + while ((i + 1) < Length && char.IsWhiteSpace(Value[i + 1])) { i++; } if (i != 0) { - Value = Value.Remove(1, i); + Span<char> result = new char[Value.Length - i]; + result[0] = ch; + Value.Slice(i + 1).CopyTo(result.Slice(1)); + Value = result.AsReadOnlySpan(); } } } diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs b/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs index ae77957cec..51fac39d0a 100644 --- a/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs +++ b/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs @@ -274,83 +274,83 @@ namespace System.Globalization internal bool FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 5 && _numCount == 4 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) - && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals3, pattern.AppCompatLiteral) - && StringSpanHelpers.Equals(_literals4, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.DayHourSep) + && _literals2.EqualsOrdinal(pattern.HourMinuteSep) + && _literals3.EqualsOrdinal(pattern.AppCompatLiteral) + && _literals4.EqualsOrdinal(pattern.End); internal bool PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 4 && _numCount == 3 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals2, pattern.AppCompatLiteral) - && StringSpanHelpers.Equals(_literals3, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.HourMinuteSep) + && _literals2.EqualsOrdinal(pattern.AppCompatLiteral) + && _literals3.EqualsOrdinal(pattern.End); /// <summary>DHMSF (all values matched)</summary> internal bool FullMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == MaxLiteralTokens && _numCount == MaxNumericTokens - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) - && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) - && StringSpanHelpers.Equals(_literals4, pattern.SecondFractionSep) - && StringSpanHelpers.Equals(_literals5, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.DayHourSep) + && _literals2.EqualsOrdinal(pattern.HourMinuteSep) + && _literals3.EqualsOrdinal(pattern.MinuteSecondSep) + && _literals4.EqualsOrdinal(pattern.SecondFractionSep) + && _literals5.EqualsOrdinal(pattern.End); /// <summary>D (no hours, minutes, seconds, or fractions)</summary> internal bool FullDMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 2 && _numCount == 1 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.End); /// <summary>HM (no days, seconds, or fractions)</summary> internal bool FullHMMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 3 && _numCount == 2 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals2, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.HourMinuteSep) + && _literals2.EqualsOrdinal(pattern.End); /// <summary>DHM (no seconds or fraction)</summary> internal bool FullDHMMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 4 && _numCount == 3 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) - && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals3, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.DayHourSep) + && _literals2.EqualsOrdinal(pattern.HourMinuteSep) + && _literals3.EqualsOrdinal(pattern.End); /// <summary>HMS (no days or fraction)</summary> internal bool FullHMSMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 4 && _numCount == 3 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) - && StringSpanHelpers.Equals(_literals3, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.HourMinuteSep) + && _literals2.EqualsOrdinal(pattern.MinuteSecondSep) + && _literals3.EqualsOrdinal(pattern.End); /// <summary>DHMS (no fraction)</summary> internal bool FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 5 && _numCount == 4 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) - && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) - && StringSpanHelpers.Equals(_literals4, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.DayHourSep) + && _literals2.EqualsOrdinal(pattern.HourMinuteSep) + && _literals3.EqualsOrdinal(pattern.MinuteSecondSep) + && _literals4.EqualsOrdinal(pattern.End); /// <summary>HMSF (no days)</summary> internal bool FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern) => _sepCount == 5 && _numCount == 4 - && StringSpanHelpers.Equals(_literals0, pattern.Start) - && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) - && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) - && StringSpanHelpers.Equals(_literals3, pattern.SecondFractionSep) - && StringSpanHelpers.Equals(_literals4, pattern.End); + && _literals0.EqualsOrdinal(pattern.Start) + && _literals1.EqualsOrdinal(pattern.HourMinuteSep) + && _literals2.EqualsOrdinal(pattern.MinuteSecondSep) + && _literals3.EqualsOrdinal(pattern.SecondFractionSep) + && _literals4.EqualsOrdinal(pattern.End); internal TTT _lastSeenTTT; internal int _tokenCount; diff --git a/src/mscorlib/shared/System/Guid.cs b/src/mscorlib/shared/System/Guid.cs index 423d5bc78c..1d0942f2cb 100644 --- a/src/mscorlib/shared/System/Guid.cs +++ b/src/mscorlib/shared/System/Guid.cs @@ -449,7 +449,7 @@ namespace System } // Check for braces - bool bracesExistInString = (guidString.IndexOf('{', 0) >= 0); + bool bracesExistInString = (guidString.IndexOf('{') >= 0); if (bracesExistInString) { @@ -471,7 +471,7 @@ namespace System } // Check for parenthesis - bool parenthesisExistInString = (guidString.IndexOf('(', 0) >= 0); + bool parenthesisExistInString = (guidString.IndexOf('(') >= 0); if (parenthesisExistInString) { @@ -548,7 +548,7 @@ namespace System // Find the end of this hex number (since it is not fixed length) numStart = 3; - numLen = guidString.IndexOf(',', numStart) - numStart; + numLen = guidString.Slice(numStart).IndexOf(','); if (numLen <= 0) { result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); @@ -566,7 +566,7 @@ namespace System } // +3 to get by ',0x' numStart = numStart + numLen + 3; - numLen = guidString.IndexOf(',', numStart) - numStart; + numLen = guidString.Slice(numStart).IndexOf(','); if (numLen <= 0) { result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); @@ -584,7 +584,7 @@ namespace System } // +3 to get by ',0x' numStart = numStart + numLen + 3; - numLen = guidString.IndexOf(',', numStart) - numStart; + numLen = guidString.Slice(numStart).IndexOf(','); if (numLen <= 0) { result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); @@ -621,7 +621,7 @@ namespace System // Calculate number length if (i < 7) // first 7 cases { - numLen = guidString.IndexOf(',', numStart) - numStart; + numLen = guidString.Slice(numStart).IndexOf(','); if (numLen <= 0) { result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); @@ -630,7 +630,7 @@ namespace System } else // last case ends with '}', not ',' { - numLen = guidString.IndexOf('}', numStart) - numStart; + numLen = guidString.Slice(numStart).IndexOf('}'); if (numLen <= 0) { result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBraceAfterLastNumber)); diff --git a/src/mscorlib/shared/System/IO/Path.Windows.cs b/src/mscorlib/shared/System/IO/Path.Windows.cs index 0448337731..2f1a527d8b 100644 --- a/src/mscorlib/shared/System/IO/Path.Windows.cs +++ b/src/mscorlib/shared/System/IO/Path.Windows.cs @@ -90,7 +90,7 @@ namespace System.IO // Drive relative paths Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2])); - if (StringSpanHelpers.Equals(GetVolumeName(path), GetVolumeName(basePath))) + if (GetVolumeName(path).EqualsOrdinal(GetVolumeName(basePath))) { // Matching root // "C:Foo" and "C:\Bar" => "C:\Bar\Foo" @@ -241,11 +241,11 @@ namespace System.IO { bool isDevice = PathInternal.IsDevice(path); - if (!isDevice && StringSpanHelpers.Equals(path.Slice(0, 2), @"\\") ) + if (!isDevice && path.Slice(0, 2).EqualsOrdinal(@"\\") ) return 2; else if (isDevice && path.Length >= 8 - && (StringSpanHelpers.Equals(path.Slice(0, 8), PathInternal.UncExtendedPathPrefix) - || StringSpanHelpers.Equals(path.Slice(5, 4), @"UNC\"))) + && (path.Slice(0, 8).EqualsOrdinal(PathInternal.UncExtendedPathPrefix) + || path.Slice(5, 4).EqualsOrdinal(@"UNC\"))) return 8; return -1; diff --git a/src/mscorlib/shared/System/IO/PathHelper.Windows.cs b/src/mscorlib/shared/System/IO/PathHelper.Windows.cs index 74ceed10aa..6faebe8ffa 100644 --- a/src/mscorlib/shared/System/IO/PathHelper.Windows.cs +++ b/src/mscorlib/shared/System/IO/PathHelper.Windows.cs @@ -35,7 +35,7 @@ namespace System.IO // TryExpandShortName does this input identity check. string result = builder.AsSpan().Contains('~') ? TryExpandShortFileName(ref builder, originalPath: path) - : builder.AsSpan().Equals(path.AsSpan()) ? path : builder.ToString(); + : builder.AsSpan().EqualsOrdinal(path.AsSpan()) ? path : builder.ToString(); // Clear the buffer builder.Dispose(); @@ -220,7 +220,7 @@ namespace System.IO // Strip out any added characters at the front of the string ReadOnlySpan<char> output = builderToUse.AsSpan().Slice(rootDifference); - string returnValue = output.Equals(originalPath.AsSpan()) + string returnValue = output.EqualsOrdinal(originalPath.AsSpan()) ? originalPath : new string(output); inputBuilder.Dispose(); diff --git a/src/mscorlib/shared/System/MemoryExtensions.Fast.cs b/src/mscorlib/shared/System/MemoryExtensions.Fast.cs index 56dd203e1f..d28c3d6883 100644 --- a/src/mscorlib/shared/System/MemoryExtensions.Fast.cs +++ b/src/mscorlib/shared/System/MemoryExtensions.Fast.cs @@ -36,41 +36,67 @@ namespace System /// </summary> public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) { - StringSpanHelpers.CheckStringComparison(comparisonType); + string.CheckStringComparison(comparisonType); switch (comparisonType) { case StringComparison.CurrentCulture: - return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None) == 0); + return (CultureInfo.CurrentCulture.CompareInfo.CompareOptionNone(span, value) == 0); case StringComparison.CurrentCultureIgnoreCase: - return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase) == 0); + return (CultureInfo.CurrentCulture.CompareInfo.CompareOptionIgnoreCase(span, value) == 0); case StringComparison.InvariantCulture: - return (CompareInfo.Invariant.Compare(span, value, CompareOptions.None) == 0); + return (CompareInfo.Invariant.CompareOptionNone(span, value) == 0); case StringComparison.InvariantCultureIgnoreCase: - return (CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase) == 0); + return (CompareInfo.Invariant.CompareOptionIgnoreCase(span, value) == 0); case StringComparison.Ordinal: - if (span.Length != value.Length) - return false; - if (value.Length == 0) // span.Length == value.Length == 0 - return true; - return span.SequenceEqual(value); //TODO: Optimize - https://github.com/dotnet/corefx/issues/27487 + return EqualsOrdinal(span, value); case StringComparison.OrdinalIgnoreCase: - if (span.Length != value.Length) - return false; - if (value.Length == 0) // span.Length == value.Length == 0 - return true; - return (CompareInfo.CompareOrdinalIgnoreCase(span, value) == 0); + return EqualsOrdinalIgnoreCase(span, value); } Debug.Fail("StringComparison outside range"); return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool EqualsOrdinal(this ReadOnlySpan<char> span, ReadOnlySpan<char> value) + { + if (span.Length != value.Length) + return false; + if (value.Length == 0) // span.Length == value.Length == 0 + return true; + return span.SequenceEqual(value); //TODO: Optimize - https://github.com/dotnet/corefx/issues/27487 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> span, ReadOnlySpan<char> value) + { + if (span.Length != value.Length) + return false; + if (value.Length == 0) // span.Length == value.Length == 0 + return true; + return (CompareInfo.CompareOrdinalIgnoreCase(span, value) == 0); + } + + // TODO https://github.com/dotnet/corefx/issues/27526 + internal static bool Contains(this ReadOnlySpan<char> source, char value) + { + for (int i = 0; i < source.Length; i++) + { + if (source[i] == value) + { + return true; + } + } + + return false; + } + /// <summary> /// Compares the specified <paramref name="span"/> and <paramref name="value"/> using the specified <paramref name="comparisonType"/>, /// and returns an integer that indicates their relative position in the sort order. @@ -80,21 +106,21 @@ namespace System /// </summary> public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) { - StringSpanHelpers.CheckStringComparison(comparisonType); + string.CheckStringComparison(comparisonType); switch (comparisonType) { case StringComparison.CurrentCulture: - return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None); + return CultureInfo.CurrentCulture.CompareInfo.CompareOptionNone(span, value); case StringComparison.CurrentCultureIgnoreCase: - return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase); + return CultureInfo.CurrentCulture.CompareInfo.CompareOptionIgnoreCase(span, value); case StringComparison.InvariantCulture: - return CompareInfo.Invariant.Compare(span, value, CompareOptions.None); + return CompareInfo.Invariant.CompareOptionNone(span, value); case StringComparison.InvariantCultureIgnoreCase: - return CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase); + return CompareInfo.Invariant.CompareOptionIgnoreCase(span, value); case StringComparison.Ordinal: if (span.Length == 0 || value.Length == 0) @@ -117,7 +143,7 @@ namespace System /// </summary> public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType) { - StringSpanHelpers.CheckStringComparison(comparisonType); + string.CheckStringComparison(comparisonType); if (value.Length == 0) { @@ -262,7 +288,7 @@ namespace System { if (value.Length == 0) { - StringSpanHelpers.CheckStringComparison(comparisonType); + string.CheckStringComparison(comparisonType); return true; } @@ -301,7 +327,7 @@ namespace System { if (value.Length == 0) { - StringSpanHelpers.CheckStringComparison(comparisonType); + string.CheckStringComparison(comparisonType); return true; } diff --git a/src/mscorlib/shared/System/MemoryExtensions.cs b/src/mscorlib/shared/System/MemoryExtensions.cs index b625ece7e3..d5a6b72eb4 100644 --- a/src/mscorlib/shared/System/MemoryExtensions.cs +++ b/src/mscorlib/shared/System/MemoryExtensions.cs @@ -181,7 +181,7 @@ namespace System ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), Unsafe.As<T, byte>(ref value), span.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// <summary> @@ -199,7 +199,7 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), value.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// <summary> @@ -301,7 +301,7 @@ namespace System ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), Unsafe.As<T, byte>(ref value), span.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// <summary> @@ -319,7 +319,7 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), value.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// <summary> diff --git a/src/mscorlib/shared/System/Number.Parsing.cs b/src/mscorlib/shared/System/Number.Parsing.cs index 46951094eb..c6ae34d050 100644 --- a/src/mscorlib/shared/System/Number.Parsing.cs +++ b/src/mscorlib/shared/System/Number.Parsing.cs @@ -734,15 +734,15 @@ namespace System //Check the three with which we're concerned and rethrow if it's not one of //those strings. ReadOnlySpan<char> sTrim = value.Trim(); - if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) + if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol)) { return double.PositiveInfinity; } - if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) + if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol)) { return double.NegativeInfinity; } - if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) + if (sTrim.EqualsOrdinal(numfmt.NaNSymbol)) { return double.NaN; } @@ -768,15 +768,15 @@ namespace System //Check the three with which we're concerned and rethrow if it's not one of //those strings. ReadOnlySpan<char> sTrim = value.Trim(); - if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) + if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol)) { return float.PositiveInfinity; } - if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) + if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol)) { return float.NegativeInfinity; } - if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) + if (sTrim.EqualsOrdinal(numfmt.NaNSymbol)) { return float.NaN; } diff --git a/src/mscorlib/shared/System/Single.cs b/src/mscorlib/shared/System/Single.cs index 7bffa1ac77..013dd9b40f 100644 --- a/src/mscorlib/shared/System/Single.cs +++ b/src/mscorlib/shared/System/Single.cs @@ -335,15 +335,15 @@ namespace System if (!success) { ReadOnlySpan<char> sTrim = s.Trim(); - if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol)) { result = PositiveInfinity; } - else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + else if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol)) { result = NegativeInfinity; } - else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + else if (sTrim.EqualsOrdinal(info.NaNSymbol)) { result = NaN; } diff --git a/src/mscorlib/shared/System/StringSpanHelpers.cs b/src/mscorlib/shared/System/StringSpanHelpers.cs deleted file mode 100644 index 2d6152de56..0000000000 --- a/src/mscorlib/shared/System/StringSpanHelpers.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace System -{ - /// <summary>Helpers for string-like operations on spans of chars.</summary> - internal static class StringSpanHelpers - { - // TODO https://github.com/dotnet/corefx/issues/21395: Provide public, efficient implementations - - public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right, StringComparison comparisonType) => - comparisonType == StringComparison.Ordinal ? Equals(left, right) : - comparisonType == StringComparison.OrdinalIgnoreCase ? EqualsOrdinalIgnoreCase(left, right) : - throw new ArgumentOutOfRangeException(nameof(comparisonType)); - - public static bool Equals(this ReadOnlySpan<char> left, string right) => - Equals(left, right.AsSpan()); - - public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right) - { - if (left.Length != right.Length) - { - return false; - } - - for (int i = 0; i < left.Length; i++) - { - if (left[i] != right[i]) - { - return false; - } - } - - return true; - } - - private static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> left, ReadOnlySpan<char> right) - { - if (left.Length != right.Length) - { - return false; - } - - for (int i = 0; i < left.Length; i++) - { - char x = left[i], y = right[i]; - if (x != y && - TextInfo.ToUpperAsciiInvariant(x) != TextInfo.ToUpperAsciiInvariant(y)) - { - return false; - } - } - - return true; - } - - public static int IndexOf(this ReadOnlySpan<char> source, char value) => - IndexOf(source, value, 0); - - public static int IndexOf(this ReadOnlySpan<char> source, char value, int startIndex) - { - for (int i = startIndex; i < source.Length; i++) - { - if (source[i] == value) - { - return i; - } - } - - return -1; - } - - public static bool Contains(this ReadOnlySpan<char> source, char value) - { - for (int i = 0; i < source.Length; i++) - { - if (source[i] == value) - { - return true; - } - } - - return false; - } - - public static ReadOnlySpan<char> Remove(this ReadOnlySpan<char> source, int startIndex, int count) - { - if (startIndex < 0) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount); - if (count > source.Length - startIndex) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount); - - if (count == 0) - { - return source; - } - - int newLength = source.Length - count; - if (newLength == 0) - { - return ReadOnlySpan<char>.Empty; - } - - Span<char> result = new char[newLength]; - source.Slice(0, startIndex).CopyTo(result); - source.Slice(startIndex + count).CopyTo(result.Slice(startIndex)); - return result; - } - - // Returns the index of the last occurrence of a specified character in the current instance. - public static int LastIndexOf(this ReadOnlySpan<char> source, char value) - { - if (source.Length == 0) - return -1; - - for (int i = source.Length - 1; i >= 0; i--) - { - if (source[i] == value) - return i; - } - - return -1; - } - - public static void CheckStringComparison(StringComparison comparisonType) - { - // Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase] - if ((uint)(comparisonType - StringComparison.CurrentCulture) > (StringComparison.OrdinalIgnoreCase - StringComparison.CurrentCulture)) - { - throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); - } - } - } -} diff --git a/src/mscorlib/shared/System/Text/StringBuilder.cs b/src/mscorlib/shared/System/Text/StringBuilder.cs index a7804c3991..65a4a240ef 100644 --- a/src/mscorlib/shared/System/Text/StringBuilder.cs +++ b/src/mscorlib/shared/System/Text/StringBuilder.cs @@ -1696,7 +1696,7 @@ namespace System.Text ReadOnlySpan<char> chunk = new ReadOnlySpan<char>(sbChunk.m_ChunkChars, 0, chunk_length); - if (!chunk.Equals(value.Slice(value.Length - offset, chunk_length))) + if (!chunk.EqualsOrdinal(value.Slice(value.Length - offset, chunk_length))) return false; sbChunk = sbChunk.m_ChunkPrevious; diff --git a/src/mscorlib/shared/System/Version.cs b/src/mscorlib/shared/System/Version.cs index 9e4cefcd6f..fe086be512 100644 --- a/src/mscorlib/shared/System/Version.cs +++ b/src/mscorlib/shared/System/Version.cs @@ -333,13 +333,15 @@ namespace System // Find the ends of the optional minor and build portions. // We musn't have any separators after build. int buildEnd = -1; - int minorEnd = input.IndexOf('.', majorEnd + 1); + int minorEnd = input.Slice(majorEnd + 1).IndexOf('.'); if (minorEnd != -1) { - buildEnd = input.IndexOf('.', minorEnd + 1); + minorEnd += (majorEnd + 1); + buildEnd = input.Slice(minorEnd + 1).IndexOf('.'); if (buildEnd != -1) { - if (input.IndexOf('.', buildEnd + 1) != -1) + buildEnd += (minorEnd + 1); + if (input.Slice(buildEnd + 1).IndexOf('.') != -1) { if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); return null; @@ -347,10 +349,10 @@ namespace System } } - int major, minor, build, revision; + int minor, build, revision; // Parse the major version - if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out major)) + if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out int major)) { return null; } diff --git a/src/mscorlib/src/System/String.Comparison.cs b/src/mscorlib/src/System/String.Comparison.cs index 44b3ed94b8..23e9efe73e 100644 --- a/src/mscorlib/src/System/String.Comparison.cs +++ b/src/mscorlib/src/System/String.Comparison.cs @@ -387,19 +387,19 @@ namespace System { if (object.ReferenceEquals(strA, strB)) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return 0; } // They can't both be null at this point. if (strA == null) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return -1; } if (strB == null) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return 1; } @@ -559,7 +559,7 @@ namespace System public static int Compare(String strA, int indexA, String strB, int indexB, int length, StringComparison comparisonType) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); if (strA == null || strB == null) { @@ -652,7 +652,6 @@ namespace System return CompareOrdinalHelper(strA, strB); } - // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly? internal static int CompareOrdinal(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB) { // TODO: Add a vectorized code path, similar to SequenceEqual @@ -781,13 +780,13 @@ namespace System if ((Object)this == (Object)value) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return true; } if (value.Length == 0) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return true; } @@ -877,13 +876,13 @@ namespace System { if ((Object)this == (Object)value) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return true; } if ((Object)value == null) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return false; } @@ -944,13 +943,13 @@ namespace System { if ((Object)a == (Object)b) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return true; } if ((Object)a == null || (Object)b == null) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return false; } @@ -1096,13 +1095,13 @@ namespace System if ((Object)this == (Object)value) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return true; } if (value.Length == 0) { - StringSpanHelpers.CheckStringComparison(comparisonType); + CheckStringComparison(comparisonType); return true; } @@ -1158,5 +1157,14 @@ namespace System } public bool StartsWith(char value) => Length != 0 && _firstChar == value; + + internal static void CheckStringComparison(StringComparison comparisonType) + { + // Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase] + if ((uint)(comparisonType - StringComparison.CurrentCulture) > (StringComparison.OrdinalIgnoreCase - StringComparison.CurrentCulture)) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.NotSupported_StringComparison, ExceptionArgument.comparisonType); + } + } } } diff --git a/src/mscorlib/src/System/ThrowHelper.cs b/src/mscorlib/src/System/ThrowHelper.cs index 6b755a13fc..4eb92be31d 100644 --- a/src/mscorlib/src/System/ThrowHelper.cs +++ b/src/mscorlib/src/System/ThrowHelper.cs @@ -479,7 +479,8 @@ namespace System culture, comparable, source, - state + state, + comparisonType } // @@ -589,6 +590,7 @@ namespace System InvalidOperation_HandleIsNotInitialized, AsyncMethodBuilder_InstanceNotInitialized, ArgumentNull_SafeHandle, + NotSupported_StringComparison, } } |