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

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs')
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs275
1 files changed, 176 insertions, 99 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs
index c369c816b..92742c7b9 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.cs
@@ -16,6 +16,8 @@ using System.Reflection;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
+using System.Buffers;
+using Internal.Runtime.CompilerServices;
namespace System.Globalization
{
@@ -105,7 +107,7 @@ namespace System.Globalization
{
throw new ArgumentNullException(nameof(assembly));
}
- if (assembly != typeof(Object).Module.Assembly)
+ if (assembly != typeof(object).Module.Assembly)
{
throw new ArgumentException(SR.Argument_OnlyMscorlib);
}
@@ -132,7 +134,7 @@ namespace System.Globalization
throw new ArgumentNullException(name == null ? nameof(name) : nameof(assembly));
}
- if (assembly != typeof(Object).Module.Assembly)
+ if (assembly != typeof(object).Module.Assembly)
{
throw new ArgumentException(SR.Argument_OnlyMscorlib);
}
@@ -302,7 +304,7 @@ namespace System.Globalization
{
if (options == CompareOptions.OrdinalIgnoreCase)
{
- return String.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
+ return string.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
}
// Verify the options before we do any real comparison.
@@ -313,7 +315,7 @@ namespace System.Globalization
throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options));
}
- return String.CompareOrdinal(string1, string2);
+ return string.CompareOrdinal(string1, string2);
}
if ((options & ValidCompareMaskOffFlags) != 0)
@@ -339,9 +341,9 @@ namespace System.Globalization
if (_invariantMode)
{
if ((options & CompareOptions.IgnoreCase) != 0)
- return CompareOrdinalIgnoreCase(string1, 0, string1.Length, string2, 0, string2.Length);
+ return CompareOrdinalIgnoreCase(string1, string2);
- return String.CompareOrdinal(string1, string2);
+ return string.CompareOrdinal(string1, string2);
}
return CompareString(string1.AsSpan(), string2.AsSpan(), options);
@@ -389,38 +391,26 @@ 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 int CompareOptionNone(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2)
{
- if (options == CompareOptions.OrdinalIgnoreCase)
- {
- return CompareOrdinalIgnoreCase(string1, string2);
- }
+ // Check for empty span or span from a null string
+ if (string1.Length == 0 || string2.Length == 0)
+ return string1.Length - string2.Length;
- // 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));
- }
+ return _invariantMode ?
+ string.CompareOrdinal(string1, string2) :
+ CompareString(string1, string2, CompareOptions.None);
+ }
- if (_invariantMode)
- {
- return (options & CompareOptions.IgnoreCase) != 0 ?
- CompareOrdinalIgnoreCase(string1, string2) :
- string.CompareOrdinal(string1, string2);
- }
+ internal int CompareOptionIgnoreCase(ReadOnlySpan<char> string1, ReadOnlySpan<char> string2)
+ {
+ // Check for empty span or span from a null string
+ if (string1.Length == 0 || string2.Length == 0)
+ return string1.Length - string2.Length;
- return CompareString(string1, string2, options);
+ return _invariantMode ?
+ CompareOrdinalIgnoreCase(string1, string2) :
+ CompareString(string1, string2, CompareOptions.IgnoreCase);
}
////////////////////////////////////////////////////////////////////////
@@ -459,7 +449,7 @@ namespace System.Globalization
{
if (options == CompareOptions.OrdinalIgnoreCase)
{
- int result = String.Compare(string1, offset1, string2, offset2, length1 < length2 ? length1 : length2, StringComparison.OrdinalIgnoreCase);
+ int result = string.Compare(string1, offset1, string2, offset2, length1 < length2 ? length1 : length2, StringComparison.OrdinalIgnoreCase);
if ((length1 != length2) && result == 0)
return (length1 > length2 ? 1 : -1);
return (result);
@@ -511,35 +501,23 @@ namespace System.Globalization
return (1);
}
+ ReadOnlySpan<char> span1 = string1.AsSpan(offset1, length1);
+ ReadOnlySpan<char> span2 = string2.AsSpan(offset2, length2);
+
if (options == CompareOptions.Ordinal)
{
- return CompareOrdinal(string1, offset1, length1,
- string2, offset2, length2);
+ return string.CompareOrdinal(span1, span2);
}
if (_invariantMode)
{
if ((options & CompareOptions.IgnoreCase) != 0)
- return CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2);
+ return CompareOrdinalIgnoreCase(span1, span2);
- return CompareOrdinal(string1, offset1, length1, string2, offset2, length2);
+ return string.CompareOrdinal(span1, span2);
}
- return CompareString(
- string1.AsSpan().Slice(offset1, length1),
- string2.AsSpan().Slice(offset2, length2),
- options);
- }
-
- private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2)
- {
- int result = String.CompareOrdinal(string1, offset1, string2, offset2,
- (length1 < length2 ? length1 : length2));
- if ((length1 != length2) && result == 0)
- {
- return (length1 > length2 ? 1 : -1);
- }
- return (result);
+ return CompareString(span1, span2, options);
}
//
@@ -551,57 +529,103 @@ namespace System.Globalization
{
Debug.Assert(indexA + lengthA <= strA.Length);
Debug.Assert(indexB + lengthB <= strB.Length);
- return CompareOrdinalIgnoreCase(strA.AsSpan().Slice(indexA, lengthA), strB.AsSpan().Slice(indexB, lengthB));
+ return CompareOrdinalIgnoreCase(
+ ref Unsafe.Add(ref strA.GetRawStringData(), indexA),
+ lengthA,
+ ref Unsafe.Add(ref strB.GetRawStringData(), indexB),
+ lengthB);
}
- internal static unsafe int CompareOrdinalIgnoreCase(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB)
+ internal static int CompareOrdinalIgnoreCase(ReadOnlySpan<char> strA, ReadOnlySpan<char> strB)
{
- int length = Math.Min(strA.Length, strB.Length);
+ return CompareOrdinalIgnoreCase(ref MemoryMarshal.GetReference(strA), strA.Length, ref MemoryMarshal.GetReference(strB), strB.Length);
+ }
+
+ internal static int CompareOrdinalIgnoreCase(string strA, string strB)
+ {
+ return CompareOrdinalIgnoreCase(ref strA.GetRawStringData(), strA.Length, ref strB.GetRawStringData(), strB.Length);
+ }
+
+ internal static int CompareOrdinalIgnoreCase(ref char strA, int lengthA, ref char strB, int lengthB)
+ {
+ int length = Math.Min(lengthA, lengthB);
int range = length;
- fixed (char* ap = &MemoryMarshal.GetReference(strA))
- fixed (char* bp = &MemoryMarshal.GetReference(strB))
- {
- char* a = ap;
- char* b = bp;
+ ref char charA = ref strA;
+ ref char charB = ref strB;
- // in InvariantMode we support all range and not only the ascii characters.
- char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x7F);
+ // in InvariantMode we support all range and not only the ascii characters.
+ char maxChar = (GlobalizationMode.Invariant ? (char)0xFFFF : (char)0x7F);
- while (length != 0 && (*a <= maxChar) && (*b <= maxChar))
+ while (length != 0 && charA <= maxChar && charB <= maxChar)
+ {
+ // Ordinal equals or lowercase equals if the result ends up in the a-z range
+ if (charA == charB ||
+ ((charA | 0x20) == (charB | 0x20) &&
+ (uint)((charA | 0x20) - 'a') <= (uint)('z' - 'a')))
{
- int charA = *a;
- int charB = *b;
-
- if (charA == charB)
- {
- a++; b++;
- length--;
- continue;
- }
+ length--;
+ charA = ref Unsafe.Add(ref charA, 1);
+ charB = ref Unsafe.Add(ref charB, 1);
+ }
+ else
+ {
+ int currentA = charA;
+ int currentB = charB;
- // uppercase both chars - notice that we need just one compare per char
- if ((uint)(charA - 'a') <= 'z' - 'a') charA -= 0x20;
- if ((uint)(charB - 'a') <= 'z' - 'a') charB -= 0x20;
+ // Uppercase both chars if needed
+ if ((uint)(charA - 'a') <= 'z' - 'a')
+ currentA -= 0x20;
+ if ((uint)(charB - 'a') <= 'z' - 'a')
+ currentB -= 0x20;
// Return the (case-insensitive) difference between them.
- if (charA != charB)
- return charA - charB;
-
- // Next char
- a++; b++;
- length--;
+ return currentA - currentB;
}
+ }
+
+ if (length == 0)
+ return lengthA - lengthB;
+
+ Debug.Assert(!GlobalizationMode.Invariant);
- if (length == 0)
- return strA.Length - strB.Length;
+ range -= length;
- Debug.Assert(!GlobalizationMode.Invariant);
+ return CompareStringOrdinalIgnoreCase(ref charA, lengthA - range, ref charB, lengthB - range);
+ }
+
+
+ internal static bool EqualsOrdinalIgnoreCase(ref char strA, ref char strB, int length)
+ {
+ ref char charA = ref strA;
+ ref char charB = ref strB;
- range -= length;
+ // in InvariantMode we support all range and not only the ascii characters.
+ char maxChar = (GlobalizationMode.Invariant ? (char)0xFFFF : (char)0x7F);
- return CompareStringOrdinalIgnoreCase(a, strA.Length - range, b, strB.Length - range);
+ while (length != 0 && charA <= maxChar && charB <= maxChar)
+ {
+ // Ordinal equals or lowercase equals if the result ends up in the a-z range
+ if (charA == charB ||
+ ((charA | 0x20) == (charB | 0x20) &&
+ (uint)((charA | 0x20) - 'a') <= (uint)('z' - 'a')))
+ {
+ length--;
+ charA = ref Unsafe.Add(ref charA, 1);
+ charB = ref Unsafe.Add(ref charB, 1);
+ }
+ else
+ {
+ return false;
+ }
}
+
+ if (length == 0)
+ return true;
+
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ return CompareStringOrdinalIgnoreCase(ref charA, length, ref charB, length) == 0;
}
////////////////////////////////////////////////////////////////////////
@@ -609,7 +633,7 @@ namespace System.Globalization
// IsPrefix
//
// Determines whether prefix is a prefix of string. If prefix equals
- // String.Empty, true is returned.
+ // string.Empty, true is returned.
//
////////////////////////////////////////////////////////////////////////
public virtual bool IsPrefix(string source, string prefix, CompareOptions options)
@@ -674,7 +698,7 @@ namespace System.Globalization
// IsSuffix
//
// Determines whether suffix is a suffix of string. If suffix equals
- // String.Empty, true is returned.
+ // string.Empty, true is returned.
//
////////////////////////////////////////////////////////////////////////
public virtual bool IsSuffix(string source, string suffix, CompareOptions options)
@@ -741,7 +765,7 @@ namespace System.Globalization
//
// Returns the first index where value is found in string. The
// search starts from startIndex and ends at endIndex. Returns -1 if
- // the specified value is not found. If value equals String.Empty,
+ // the specified value is not found. If value equals string.Empty,
// startIndex is returned. Throws IndexOutOfRange if startIndex or
// endIndex is less than zero or greater than the length of string.
// Throws ArgumentException if value is null.
@@ -910,16 +934,36 @@ namespace System.Globalization
return IndexOfCore(source, value, startIndex, count, options, null);
}
- internal virtual int IndexOfOrdinal(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ internal int IndexOfOrdinal(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!value.IsEmpty);
+ return IndexOfOrdinalCore(source, value, ignoreCase, fromBeginning: true);
+ }
+
+ internal int LastIndexOfOrdinal(ReadOnlySpan<char> source, ReadOnlySpan<char> value, bool ignoreCase)
+ {
+ Debug.Assert(!_invariantMode);
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!value.IsEmpty);
+ return IndexOfOrdinalCore(source, value, ignoreCase, fromBeginning: false);
+ }
+
+ internal unsafe int IndexOf(ReadOnlySpan<char> source, ReadOnlySpan<char> value, CompareOptions options)
{
Debug.Assert(!_invariantMode);
- return IndexOfOrdinalCore(source, value, ignoreCase);
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!value.IsEmpty);
+ return IndexOfCore(source, value, options, null, fromBeginning: true);
}
- internal unsafe virtual int IndexOf(ReadOnlySpan<char> source, ReadOnlySpan<char> value, CompareOptions options)
+ internal unsafe int LastIndexOf(ReadOnlySpan<char> source, ReadOnlySpan<char> value, CompareOptions options)
{
Debug.Assert(!_invariantMode);
- return IndexOfCore(source, value, options, null);
+ Debug.Assert(!source.IsEmpty);
+ Debug.Assert(!value.IsEmpty);
+ return IndexOfCore(source, value, options, null, fromBeginning: false);
}
// The following IndexOf overload is mainly used by String.Replace. This overload assumes the parameters are already validated
@@ -985,7 +1029,7 @@ namespace System.Globalization
//
// Returns the last index where value is found in string. The
// search starts from startIndex and ends at endIndex. Returns -1 if
- // the specified value is not found. If value equals String.Empty,
+ // the specified value is not found. If value equals string.Empty,
// endIndex is returned. Throws IndexOutOfRange if startIndex or
// endIndex is less than zero or greater than the length of string.
// Throws ArgumentException if value is null.
@@ -993,7 +1037,7 @@ namespace System.Globalization
////////////////////////////////////////////////////////////////////////
- public virtual int LastIndexOf(String source, char value)
+ public virtual int LastIndexOf(string source, char value)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
@@ -1208,7 +1252,7 @@ namespace System.Globalization
////////////////////////////////////////////////////////////////////////
- public override bool Equals(Object value)
+ public override bool Equals(object value)
{
CompareInfo that = value as CompareInfo;
@@ -1237,6 +1281,34 @@ namespace System.Globalization
return (this.Name.GetHashCode());
}
+ internal static unsafe int GetIgnoreCaseHash(string source)
+ {
+ Debug.Assert(source != null, "source must not be null");
+
+ // Do not allocate on the stack if string is empty
+ if (source.Length == 0)
+ {
+ return source.GetHashCode();
+ }
+
+ char[] borrowedArr = null;
+ Span<char> span = source.Length <= 255 ?
+ stackalloc char[255] :
+ (borrowedArr = ArrayPool<char>.Shared.Rent(source.Length));
+
+ int charsWritten = source.AsSpan().ToUpperInvariant(span);
+
+ // Slice the array to the size returned by ToUpperInvariant.
+ int hash = Marvin.ComputeHash32(MemoryMarshal.AsBytes(span.Slice(0, charsWritten)), Marvin.DefaultSeed);
+
+ // Return the borrowed array if necessary.
+ if (borrowedArr != null)
+ {
+ ArrayPool<char>.Shared.Return(borrowedArr);
+ }
+
+ return hash;
+ }
////////////////////////////////////////////////////////////////////////
//
@@ -1277,6 +1349,11 @@ namespace System.Globalization
throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
}
+ if (_invariantMode)
+ {
+ return ((options & CompareOptions.IgnoreCase) != 0) ? GetIgnoreCaseHash(source) : source.GetHashCode();
+ }
+
return GetHashCodeOfStringCore(source, options);
}
@@ -1294,7 +1371,7 @@ namespace System.Globalization
if (options == CompareOptions.OrdinalIgnoreCase)
{
- return TextInfo.GetHashCodeOrdinalIgnoreCase(source);
+ return GetIgnoreCaseHash(source);
}
//