Remove StringSpanHelpers and start using MemoryExtensions (#16718)
authorAhson Khan <ahkha@microsoft.com>
Sun, 4 Mar 2018 01:48:07 +0000 (17:48 -0800)
committerGitHub <noreply@github.com>
Sun, 4 Mar 2018 01:48:07 +0000 (17:48 -0800)
* Remove StringSpanHelpers and start using MemoryExtensions

* Address PR feedback.

18 files changed:
src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
src/mscorlib/shared/System/Boolean.cs
src/mscorlib/shared/System/Double.cs
src/mscorlib/shared/System/Globalization/CompareInfo.cs
src/mscorlib/shared/System/Globalization/DateTimeParse.cs
src/mscorlib/shared/System/Globalization/TimeSpanParse.cs
src/mscorlib/shared/System/Guid.cs
src/mscorlib/shared/System/IO/Path.Windows.cs
src/mscorlib/shared/System/IO/PathHelper.Windows.cs
src/mscorlib/shared/System/MemoryExtensions.Fast.cs
src/mscorlib/shared/System/MemoryExtensions.cs
src/mscorlib/shared/System/Number.Parsing.cs
src/mscorlib/shared/System/Single.cs
src/mscorlib/shared/System/StringSpanHelpers.cs [deleted file]
src/mscorlib/shared/System/Text/StringBuilder.cs
src/mscorlib/shared/System/Version.cs
src/mscorlib/src/System/String.Comparison.cs
src/mscorlib/src/System/ThrowHelper.cs

index 0b3f971..46a161e 100644 (file)
     <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" />
index fd56082..e476ef7 100644 (file)
@@ -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;
index 146ee46..1351ae9 100644 (file)
@@ -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;
                 }
index c369c81..db0a660 100644 (file)
@@ -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);
         }
 
         ////////////////////////////////////////////////////////////////////////
index 5b285eb..fd64c6d 100644 (file)
@@ -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();
                 }
             }
         }
index ae77957..51fac39 100644 (file)
@@ -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;
index 423d5bc..1d0942f 100644 (file)
@@ -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));
index 0448337..2f1a527 100644 (file)
@@ -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;
index 74ceed1..6faebe8 100644 (file)
@@ -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();
index 56dd203..d28c3d6 100644 (file)
@@ -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;
             }
 
index b625ece..d5a6b72 100644 (file)
@@ -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>
index 4695109..c6ae34d 100644 (file)
@@ -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;
                 }
index 7bffa1a..013dd9b 100644 (file)
@@ -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 (file)
index 2d6152d..0000000
+++ /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));
-            }
-        }
-    }
-}
index a7804c3..65a4a24 100644 (file)
@@ -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;
index 9e4cefc..fe086be 100644 (file)
@@ -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;
             }
index 44b3ed9..23e9efe 100644 (file)
@@ -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);
+            }
+        }
     }
 }
index 6b755a1..4eb92be 100644 (file)
@@ -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,
     }
 }